mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
Update to 4.2.0 - Configuration changes (big but old way still works, naming changes) & Implement https://github.com/kataras/iris/issues/409
This commit is contained in:
parent
1a7c79db66
commit
f561b7a90d
282
HISTORY.md
282
HISTORY.md
|
@ -1,6 +1,286 @@
|
||||||
# History
|
# History
|
||||||
|
|
||||||
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras/iris` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`.
|
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras/iris` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris`.
|
||||||
|
|
||||||
|
## 4.1.7 -> 4.2.0
|
||||||
|
|
||||||
|
- **ADDED**: `iris.TemplateSourceString(src string, binding interface{}) string` this will parse the src raw contents to the template engine and return the string result & `context.RenderTemplateSource(status int, src string, binding interface{}, options ...map[string]interface{}) error` this will parse the src raw contents to the template engine and render the result to the client, as requseted [here](https://github.com/kataras/iris/issues/409).
|
||||||
|
|
||||||
|
|
||||||
|
This version has 'breaking' changes if you were, directly, passing custom configuration to a custom iris instance before.
|
||||||
|
As the TODO2 I had to think and implement a way to make configuration even easier and more simple to use.
|
||||||
|
|
||||||
|
With last changes in place, Iris is using new, cross-framework, and more stable packages made by me(so don't worry things are working and will as you expect) to render [templates](https://github.com/kataras/go-template), manage [sessions](https://github.com/kataras/go-sesions) and [websockets](https://github.com/kataras/go-websocket). So the `/kataras/iris/config` is no longer need to be there, we don't have core packages inside iris which need these configuration to other package-folder than the main anymore(in order to avoid the import-cycle), new file `/kataras/iris/configuration.go` is created for the configuration, which lives inside the main package, means that now:
|
||||||
|
|
||||||
|
- **if you want to pass directly configuration to a new custom iris instance, you don't have to import the github.com/kataras/iris/config package**
|
||||||
|
|
||||||
|
Naming changes:
|
||||||
|
|
||||||
|
- `config.Iris` -> `iris.Configuration`, which is the parent/main configuration. Added: `TimeFormat` and `Other` (pass any dynamic custom, other options there)
|
||||||
|
- `config.Sessions` -> `iris.SessionsConfiguration`
|
||||||
|
- `config.Websocket` -> `iris.WebscoketConfiguration`
|
||||||
|
- `config.Server` -> `iris.ServerConfiguration`
|
||||||
|
- `config.Tester` -> `iris.TesterConfiguration`
|
||||||
|
|
||||||
|
All these changes wasn't made only to remove the `./config` folder but to make easier for you to pass the exact configuration field/option you need to edit at the top of the default configuration, without need to pass the whole Configuration object. **Attention**: old way, pass `iris.Configuration` directly, is still valid object to pass to the `iris.New`, so don't be afraid for breaking change, the only thing you will need to edit is the names of the configuration you saw on the previous paragraph.
|
||||||
|
|
||||||
|
**Configuration Declaration**:
|
||||||
|
|
||||||
|
instead of old, but still valid to pass to the `iris.New`:
|
||||||
|
- `iris.New(iris.Configuration{Charset: "UTF-8", Sessions: iris.SessionsConfiguration{Cookie: "cookienameid"}})`
|
||||||
|
now you can just write this:
|
||||||
|
- `iris.New(iris.OptionCharset("UTF-8"), iris.OptionSessionsCookie("cookienameid"))`
|
||||||
|
|
||||||
|
`.New` **by configuration**
|
||||||
|
```go
|
||||||
|
import "github.com/kataras/iris"
|
||||||
|
//...
|
||||||
|
myConfig := iris.Configuration{Charset: "UTF-8", IsDevelopment:true, Sessions: iris.SessionsConfiguration{Cookie:"mycookie"}, Websocket: iris.WebsocketConfiguration{Endpoint: "/my_endpoint"}}
|
||||||
|
iris.New(myConfig)
|
||||||
|
```
|
||||||
|
|
||||||
|
`.New` **by options**
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/kataras/iris"
|
||||||
|
//...
|
||||||
|
iris.New(iris.OptionCharset("UTF-8"), iris.OptionIsDevelopment(true), iris.OptionSessionsCookie("mycookie"), iris.OptionWebsocketEndpoint("/my_endpoint"))
|
||||||
|
|
||||||
|
// if you want to set configuration after the .New use the .Set:
|
||||||
|
iris.Set(iris.OptionDisableBanner(true))
|
||||||
|
```
|
||||||
|
|
||||||
|
**List** of all available options:
|
||||||
|
```go
|
||||||
|
// OptionDisablePathCorrection corrects and redirects the requested path to the registed path
|
||||||
|
// for example, if /home/ path is requested but no handler for this Route found,
|
||||||
|
// then the Router checks if /home handler exists, if yes,
|
||||||
|
// (permant)redirects the client to the correct path /home
|
||||||
|
//
|
||||||
|
// Default is false
|
||||||
|
OptionDisablePathCorrection(val bool)
|
||||||
|
|
||||||
|
// OptionDisablePathEscape when is false then its escapes the path, the named parameters (if any).
|
||||||
|
OptionDisablePathEscape(val bool)
|
||||||
|
|
||||||
|
// OptionDisableBanner outputs the iris banner at startup
|
||||||
|
//
|
||||||
|
// Default is false
|
||||||
|
OptionDisableBanner(val bool)
|
||||||
|
|
||||||
|
// OptionLoggerOut is the destination for output
|
||||||
|
//
|
||||||
|
// Default is os.Stdout
|
||||||
|
OptionLoggerOut(val io.Writer)
|
||||||
|
|
||||||
|
// OptionLoggerPreffix is the logger's prefix to write at beginning of each line
|
||||||
|
//
|
||||||
|
// Default is [IRIS]
|
||||||
|
OptionLoggerPreffix(val string)
|
||||||
|
|
||||||
|
// OptionProfilePath a the route path, set it to enable http pprof tool
|
||||||
|
// Default is empty, if you set it to a $path, these routes will handled:
|
||||||
|
OptionProfilePath(val string)
|
||||||
|
|
||||||
|
// OptionDisableTemplateEngines set to true to disable loading the default template engine (html/template) and disallow the use of iris.UseEngine
|
||||||
|
// Default is false
|
||||||
|
OptionDisableTemplateEngines(val bool)
|
||||||
|
|
||||||
|
// OptionIsDevelopment iris will act like a developer, for example
|
||||||
|
// If true then re-builds the templates on each request
|
||||||
|
// Default is false
|
||||||
|
OptionIsDevelopment(val bool)
|
||||||
|
|
||||||
|
// OptionTimeFormat time format for any kind of datetime parsing
|
||||||
|
OptionTimeFormat(val string)
|
||||||
|
|
||||||
|
// OptionCharset character encoding for various rendering
|
||||||
|
// used for templates and the rest of the responses
|
||||||
|
// Default is "UTF-8"
|
||||||
|
OptionCharset(val string)
|
||||||
|
|
||||||
|
// OptionGzip enables gzip compression on your Render actions, this includes any type of render, templates and pure/raw content
|
||||||
|
// If you don't want to enable it globaly, you could just use the third parameter on context.Render("myfileOrResponse", structBinding{}, iris.RenderOptions{"gzip": true})
|
||||||
|
// Default is false
|
||||||
|
OptionGzip(val bool)
|
||||||
|
|
||||||
|
// OptionOther are the custom, dynamic options, can be empty
|
||||||
|
// this fill used only by you to set any app's options you want
|
||||||
|
// for each of an Iris instance
|
||||||
|
OptionOther(val ...options.Options) //map[string]interface{}, options is github.com/kataras/go-options
|
||||||
|
|
||||||
|
// OptionSessionsCookie string, the session's client cookie name, for example: "qsessionid"
|
||||||
|
OptionSessionsCookie(val string)
|
||||||
|
|
||||||
|
// OptionSessionsDecodeCookie set it to true to decode the cookie key with base64 URLEncoding
|
||||||
|
// Defaults to false
|
||||||
|
OptionSessionsDecodeCookie(val bool)
|
||||||
|
|
||||||
|
// OptionSessionsExpires the duration of which the cookie must expires (created_time.Add(Expires)).
|
||||||
|
// If you want to delete the cookie when the browser closes, set it to -1 but in this case, the server side's session duration is up to GcDuration
|
||||||
|
//
|
||||||
|
// Default infinitive/unlimited life duration(0)
|
||||||
|
OptionSessionsExpires(val time.Duration)
|
||||||
|
|
||||||
|
// OptionSessionsCookieLength the length of the sessionid's cookie's value, let it to 0 if you don't want to change it
|
||||||
|
// Defaults to 32
|
||||||
|
OptionSessionsCookieLength(val int)
|
||||||
|
|
||||||
|
// OptionSessionsGcDuration every how much duration(GcDuration) the memory should be clear for unused cookies (GcDuration)
|
||||||
|
// for example: time.Duration(2)*time.Hour. it will check every 2 hours if cookie hasn't be used for 2 hours,
|
||||||
|
// deletes it from backend memory until the user comes back, then the session continue to work as it was
|
||||||
|
//
|
||||||
|
// Default 2 hours
|
||||||
|
OptionSessionsGcDuration(val time.Duration)
|
||||||
|
|
||||||
|
// OptionSessionsDisableSubdomainPersistence set it to true in order dissallow your q subdomains to have access to the session cookie
|
||||||
|
// defaults to false
|
||||||
|
OptionSessionsDisableSubdomainPersistence(val bool)
|
||||||
|
|
||||||
|
// OptionWebsocketWriteTimeout time allowed to write a message to the connection.
|
||||||
|
// Default value is 15 * time.Second
|
||||||
|
OptionWebsocketWriteTimeout(val time.Duration)
|
||||||
|
|
||||||
|
// OptionWebsocketPongTimeout allowed to read the next pong message from the connection
|
||||||
|
// Default value is 60 * time.Second
|
||||||
|
OptionWebsocketPongTimeout(val time.Duration)
|
||||||
|
|
||||||
|
// OptionWebsocketPingPeriod send ping messages to the connection with this period. Must be less than PongTimeout
|
||||||
|
// Default value is (PongTimeout * 9) / 10
|
||||||
|
OptionWebsocketPingPeriod(val time.Duration)
|
||||||
|
|
||||||
|
// OptionWebsocketMaxMessageSize max message size allowed from connection
|
||||||
|
// Default value is 1024
|
||||||
|
OptionWebsocketMaxMessageSize(val int64)
|
||||||
|
|
||||||
|
// OptionWebsocketBinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text
|
||||||
|
// see https://github.com/kataras/iris/issues/387#issuecomment-243006022 for more
|
||||||
|
// defaults to false
|
||||||
|
OptionWebsocketBinaryMessages(val bool)
|
||||||
|
|
||||||
|
// OptionWebsocketEndpoint is the path which the websocket server will listen for clients/connections
|
||||||
|
// Default value is empty string, if you don't set it the Websocket server is disabled.
|
||||||
|
OptionWebsocketEndpoint(val string)
|
||||||
|
|
||||||
|
// OptionWebsocketReadBufferSize is the buffer size for the underline reader
|
||||||
|
OptionWebsocketReadBufferSize(val int)
|
||||||
|
|
||||||
|
// OptionWebsocketWriteBufferSize is the buffer size for the underline writer
|
||||||
|
OptionWebsocketWriteBufferSize(val int)
|
||||||
|
|
||||||
|
// OptionTesterListeningAddr is the virtual server's listening addr (host)
|
||||||
|
// Default is "iris-go.com:1993"
|
||||||
|
OptionTesterListeningAddr(val string)
|
||||||
|
|
||||||
|
// OptionTesterExplicitURL If true then the url (should) be prepended manually, useful when want to test subdomains
|
||||||
|
// Default is false
|
||||||
|
OptionTesterExplicitURL(val bool)
|
||||||
|
|
||||||
|
// OptionTesterDebug if true then debug messages from the httpexpect will be shown when a test runs
|
||||||
|
// Default is false
|
||||||
|
OptionTesterDebug(val bool)
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, some of you maybe use more than one server inside their iris instance/app, so you used the `iris.AddServer(config.Server{})`, which now becomes `iris.AddServer(iris.ServerConfiguration{})`, ServerConfiguration has also (optional) options to pass there and to `iris.ListenTo(OptionServerListeningAddr("mydomain.com"))`:
|
||||||
|
|
||||||
|
|
||||||
|
```go
|
||||||
|
// examples:
|
||||||
|
iris.AddServer(iris.OptionServerCertFile("file.cert"),iris.OptionServerKeyFile("file.key"))
|
||||||
|
iris.ListenTo(iris.OptionServerReadBufferSize(42000))
|
||||||
|
|
||||||
|
// or, old way but still valid:
|
||||||
|
iris.AddServer(iris.ServerConfiguration{ListeningAddr: "mydomain.com", CertFile: "file.cert", KeyFile: "file.key"})
|
||||||
|
iris.ListenTo(iris.ServerConfiguration{ReadBufferSize:42000, ListeningAddr: "mydomain.com"})
|
||||||
|
```
|
||||||
|
|
||||||
|
**List** of all Server's options:
|
||||||
|
|
||||||
|
```go
|
||||||
|
OptionServerListeningAddr(val string)
|
||||||
|
|
||||||
|
OptionServerCertFile(val string)
|
||||||
|
|
||||||
|
OptionServerKeyFile(val string)
|
||||||
|
|
||||||
|
// AutoTLS enable to get certifications from the Letsencrypt
|
||||||
|
// when this configuration field is true, the CertFile & KeyFile are empty, no need to provide a key.
|
||||||
|
//
|
||||||
|
// example: https://github.com/iris-contrib/examples/blob/master/letsencyrpt/main.go
|
||||||
|
OptionServerAutoTLS(val bool)
|
||||||
|
|
||||||
|
// Mode this is for unix only
|
||||||
|
OptionServerMode(val os.FileMode)
|
||||||
|
// OptionServerMaxRequestBodySize Maximum request body size.
|
||||||
|
//
|
||||||
|
// The server rejects requests with bodies exceeding this limit.
|
||||||
|
//
|
||||||
|
// By default request body size is 8MB.
|
||||||
|
OptionServerMaxRequestBodySize(val int)
|
||||||
|
|
||||||
|
// Per-connection buffer size for requests' reading.
|
||||||
|
// This also limits the maximum header size.
|
||||||
|
//
|
||||||
|
// Increase this buffer if your clients send multi-KB RequestURIs
|
||||||
|
// and/or multi-KB headers (for example, BIG cookies).
|
||||||
|
//
|
||||||
|
// Default buffer size is used if not set.
|
||||||
|
OptionServerReadBufferSize(val int)
|
||||||
|
|
||||||
|
// Per-connection buffer size for responses' writing.
|
||||||
|
//
|
||||||
|
// Default buffer size is used if not set.
|
||||||
|
OptionServerWriteBufferSize(val int)
|
||||||
|
|
||||||
|
// Maximum duration for reading the full request (including body).
|
||||||
|
//
|
||||||
|
// This also limits the maximum duration for idle keep-alive
|
||||||
|
// connections.
|
||||||
|
//
|
||||||
|
// By default request read timeout is unlimited.
|
||||||
|
OptionServerReadTimeout(val time.Duration)
|
||||||
|
|
||||||
|
// Maximum duration for writing the full response (including body).
|
||||||
|
//
|
||||||
|
// By default response write timeout is unlimited.
|
||||||
|
OptionServerWriteTimeout(val time.Duration)
|
||||||
|
|
||||||
|
// RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT)
|
||||||
|
//
|
||||||
|
// NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server
|
||||||
|
// which means that if you want to change this address you have to clear your browser's cache in order this to be able to change to the new addr.
|
||||||
|
//
|
||||||
|
// example: https://github.com/iris-contrib/examples/tree/master/multiserver_listening2
|
||||||
|
OptionServerRedirectTo(val string)
|
||||||
|
|
||||||
|
// OptionServerVirtual If this server is not really listens to a real host, it mostly used in order to achieve testing without system modifications
|
||||||
|
OptionServerVirtual(val bool)
|
||||||
|
|
||||||
|
// OptionServerVListeningAddr, can be used for both virtual = true or false,
|
||||||
|
// if it's setted to not empty, then the server's Host() will return this addr instead of the ListeningAddr.
|
||||||
|
// server's Host() is used inside global template helper funcs
|
||||||
|
// set it when you are sure you know what it does.
|
||||||
|
//
|
||||||
|
// Default is empty ""
|
||||||
|
OptionServerVListeningAddr(val string)
|
||||||
|
|
||||||
|
// OptionServerVScheme if setted to not empty value then all template's helper funcs prepends that as the url scheme instead of the real scheme
|
||||||
|
// server's .Scheme returns VScheme if not empty && differs from real scheme
|
||||||
|
//
|
||||||
|
// Default is empty ""
|
||||||
|
OptionServerVScheme(val string)
|
||||||
|
|
||||||
|
// OptionServerName the server's name, defaults to "iris".
|
||||||
|
// You're free to change it, but I will trust you to don't, this is the only setting whose somebody, like me, can see if iris web framework is used
|
||||||
|
OptionServerName(val string)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
View all configuration fields and options by navigating to the [kataras/iris/configuration.go source file](https://github.com/kataras/iris/blob/master/configuration.go)
|
||||||
|
|
||||||
|
[Book](https://kataras.gitbooks.io/iris/content/configuration.html) & [Examples](https://github.com/iris-contrib/examples) are updated (website docs will be updated soon).
|
||||||
|
|
||||||
## 4.1.6 -> 4.1.7
|
## 4.1.6 -> 4.1.7
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
|
||||||
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%204.1.7%20-blue.svg?style=flat-square" alt="Releases"></a>
|
<a href="https://github.com/kataras/iris/releases"><img src="https://img.shields.io/badge/%20version%20-%204.2.0%20-blue.svg?style=flat-square" alt="Releases"></a>
|
||||||
|
|
||||||
<a href="https://github.com/iris-contrib/examples"><img src="https://img.shields.io/badge/%20examples-repository-3362c2.svg?style=flat-square" alt="Examples"></a>
|
<a href="https://github.com/iris-contrib/examples"><img src="https://img.shields.io/badge/%20examples-repository-3362c2.svg?style=flat-square" alt="Examples"></a>
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
|
||||||
Versioning
|
Versioning
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Current: **v4.1.7**
|
Current: **v4.2.0**
|
||||||
|
|
||||||
> Iris is an active project
|
> Iris is an active project
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ Read more about Semantic Versioning 2.0.0
|
||||||
Todo
|
Todo
|
||||||
------------
|
------------
|
||||||
- [x] Use of the standard `log.Logger` instead of the `iris-contrib/logger`(colorful logger), make these changes to all middleware, examples and plugins.
|
- [x] Use of the standard `log.Logger` instead of the `iris-contrib/logger`(colorful logger), make these changes to all middleware, examples and plugins.
|
||||||
- [ ] Implement, even, a better way to manage configuration/options, devs will be able to set their own custom options inside there. ` I'm thinking of something the last days, but it will have breaking changes. `
|
- [x] Implement, even, a better way to manage configuration/options, devs will be able to set their own custom options inside there. ` I'm thinking of something the last days, but it will have breaking changes. `
|
||||||
- [ ] Implement an internal updater, as requested [here](https://github.com/kataras/iris/issues/401).
|
- [ ] Implement an internal updater, as requested [here](https://github.com/kataras/iris/issues/401).
|
||||||
|
|
||||||
Iris is a **Community-Driven** Project, waiting for your suggestions and [feature requests](https://github.com/kataras/iris/issues?utf8=%E2%9C%93&q=label%3A%22feature%20request%22)!
|
Iris is a **Community-Driven** Project, waiting for your suggestions and [feature requests](https://github.com/kataras/iris/issues?utf8=%E2%9C%93&q=label%3A%22feature%20request%22)!
|
||||||
|
@ -221,7 +221,7 @@ License can be found [here](LICENSE).
|
||||||
[Travis]: http://travis-ci.org/kataras/iris
|
[Travis]: http://travis-ci.org/kataras/iris
|
||||||
[License Widget]: https://img.shields.io/badge/license-MIT%20%20License%20-E91E63.svg?style=flat-square
|
[License Widget]: https://img.shields.io/badge/license-MIT%20%20License%20-E91E63.svg?style=flat-square
|
||||||
[License]: https://github.com/kataras/iris/blob/master/LICENSE
|
[License]: https://github.com/kataras/iris/blob/master/LICENSE
|
||||||
[Release Widget]: https://img.shields.io/badge/release-v4.1.7-blue.svg?style=flat-square
|
[Release Widget]: https://img.shields.io/badge/release-v4.2.0-blue.svg?style=flat-square
|
||||||
[Release]: https://github.com/kataras/iris/releases
|
[Release]: https://github.com/kataras/iris/releases
|
||||||
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
|
[Chat Widget]: https://img.shields.io/badge/community-chat-00BCD4.svg?style=flat-square
|
||||||
[Chat]: https://kataras.rocket.chat/channel/iris
|
[Chat]: https://kataras.rocket.chat/channel/iris
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
// Package config defines the default settings and semantic variables
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// TimeFormat default time format for any kind of datetime parsing
|
|
||||||
TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
|
|
||||||
// StaticCacheDuration expiration duration for INACTIVE file handlers
|
|
||||||
StaticCacheDuration = 20 * time.Second
|
|
||||||
// CompressedFileSuffix is the suffix to add to the name of
|
|
||||||
// cached compressed file when using the .StaticFS function.
|
|
||||||
//
|
|
||||||
// Defaults to iris-fasthttp.gz
|
|
||||||
CompressedFileSuffix = "iris-fasthttp.gz"
|
|
||||||
)
|
|
163
config/iris.go
163
config/iris.go
|
@ -1,163 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Default values for base Iris conf
|
|
||||||
const (
|
|
||||||
DefaultDisablePathCorrection = false
|
|
||||||
DefaultDisablePathEscape = false
|
|
||||||
DefaultCharset = "UTF-8"
|
|
||||||
DefaultLoggerPreffix = "[IRIS] "
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
DefaultLoggerOut = os.Stdout
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
|
||||||
// Iris configs for the station
|
|
||||||
Iris struct {
|
|
||||||
|
|
||||||
// DisablePathCorrection corrects and redirects the requested path to the registed path
|
|
||||||
// for example, if /home/ path is requested but no handler for this Route found,
|
|
||||||
// then the Router checks if /home handler exists, if yes,
|
|
||||||
// (permant)redirects the client to the correct path /home
|
|
||||||
//
|
|
||||||
// Default is false
|
|
||||||
DisablePathCorrection bool
|
|
||||||
|
|
||||||
// DisablePathEscape when is false then its escapes the path, the named parameters (if any).
|
|
||||||
// Change to true it if you want something like this https://github.com/kataras/iris/issues/135 to work
|
|
||||||
//
|
|
||||||
// When do you need to Disable(true) it:
|
|
||||||
// accepts parameters with slash '/'
|
|
||||||
// Request: http://localhost:8080/details/Project%2FDelta
|
|
||||||
// ctx.Param("project") returns the raw named parameter: Project%2FDelta
|
|
||||||
// which you can escape it manually with net/url:
|
|
||||||
// projectName, _ := url.QueryUnescape(c.Param("project").
|
|
||||||
// Look here: https://github.com/kataras/iris/issues/135 for more
|
|
||||||
//
|
|
||||||
// Default is false
|
|
||||||
DisablePathEscape bool
|
|
||||||
|
|
||||||
// DisableBanner outputs the iris banner at startup
|
|
||||||
//
|
|
||||||
// Default is false
|
|
||||||
DisableBanner bool
|
|
||||||
|
|
||||||
// LoggerOut is the destination for output
|
|
||||||
//
|
|
||||||
// defaults to os.Stdout
|
|
||||||
LoggerOut io.Writer
|
|
||||||
// LoggerOut is the logger's prefix to write at beginning of each line
|
|
||||||
//
|
|
||||||
// Defaults to [IRIS]
|
|
||||||
LoggerPreffix string
|
|
||||||
|
|
||||||
// ProfilePath a the route path, set it to enable http pprof tool
|
|
||||||
// Default is empty, if you set it to a $path, these routes will handled:
|
|
||||||
// $path/cmdline
|
|
||||||
// $path/profile
|
|
||||||
// $path/symbol
|
|
||||||
// $path/goroutine
|
|
||||||
// $path/heap
|
|
||||||
// $path/threadcreate
|
|
||||||
// $path/pprof/block
|
|
||||||
// for example if '/debug/pprof'
|
|
||||||
// http://yourdomain:PORT/debug/pprof/
|
|
||||||
// http://yourdomain:PORT/debug/pprof/cmdline
|
|
||||||
// http://yourdomain:PORT/debug/pprof/profile
|
|
||||||
// http://yourdomain:PORT/debug/pprof/symbol
|
|
||||||
// http://yourdomain:PORT/debug/pprof/goroutine
|
|
||||||
// http://yourdomain:PORT/debug/pprof/heap
|
|
||||||
// http://yourdomain:PORT/debug/pprof/threadcreate
|
|
||||||
// http://yourdomain:PORT/debug/pprof/pprof/block
|
|
||||||
// it can be a subdomain also, for example, if 'debug.'
|
|
||||||
// http://debug.yourdomain:PORT/
|
|
||||||
// http://debug.yourdomain:PORT/cmdline
|
|
||||||
// http://debug.yourdomain:PORT/profile
|
|
||||||
// http://debug.yourdomain:PORT/symbol
|
|
||||||
// http://debug.yourdomain:PORT/goroutine
|
|
||||||
// http://debug.yourdomain:PORT/heap
|
|
||||||
// http://debug.yourdomain:PORT/threadcreate
|
|
||||||
// http://debug.yourdomain:PORT/pprof/block
|
|
||||||
ProfilePath string
|
|
||||||
// DisableTemplateEngines set to true to disable loading the default template engine (html/template) and disallow the use of iris.UseEngine
|
|
||||||
// default is false
|
|
||||||
DisableTemplateEngines bool
|
|
||||||
// IsDevelopment iris will act like a developer, for example
|
|
||||||
// If true then re-builds the templates on each request
|
|
||||||
// default is false
|
|
||||||
IsDevelopment bool
|
|
||||||
|
|
||||||
// Charset character encoding for various rendering
|
|
||||||
// used for templates and the rest of the responses
|
|
||||||
// defaults to "UTF-8"
|
|
||||||
Charset string
|
|
||||||
|
|
||||||
// Gzip enables gzip compression on your Render actions, this includes any type of render, templates and pure/raw content
|
|
||||||
// If you don't want to enable it globaly, you could just use the third parameter on context.Render("myfileOrResponse", structBinding{}, iris.RenderOptions{"gzip": true})
|
|
||||||
// defaults to false
|
|
||||||
Gzip bool
|
|
||||||
|
|
||||||
// Sessions contains the configs for sessions
|
|
||||||
Sessions Sessions
|
|
||||||
|
|
||||||
// Websocket contains the configs for Websocket's server integration
|
|
||||||
Websocket *Websocket
|
|
||||||
|
|
||||||
// Tester contains the configs for the test framework, so far we have only one because all test framework's configs are setted by the iris itself
|
|
||||||
// You can find example on the https://github.com/kataras/iris/glob/master/context_test.go
|
|
||||||
Tester Tester
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Default returns the default configuration for the Iris staton
|
|
||||||
func Default() Iris {
|
|
||||||
return Iris{
|
|
||||||
DisablePathCorrection: DefaultDisablePathCorrection,
|
|
||||||
DisablePathEscape: DefaultDisablePathEscape,
|
|
||||||
DisableBanner: false,
|
|
||||||
LoggerOut: DefaultLoggerOut,
|
|
||||||
LoggerPreffix: DefaultLoggerPreffix,
|
|
||||||
DisableTemplateEngines: false,
|
|
||||||
IsDevelopment: false,
|
|
||||||
Charset: DefaultCharset,
|
|
||||||
Gzip: false,
|
|
||||||
ProfilePath: "",
|
|
||||||
Sessions: DefaultSessions(),
|
|
||||||
Websocket: DefaultWebsocket(),
|
|
||||||
Tester: DefaultTester(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge merges the default with the given config and returns the result
|
|
||||||
// receives an array because the func caller is variadic
|
|
||||||
func (c Iris) Merge(cfg []Iris) (config Iris) {
|
|
||||||
// I tried to make it more generic with interfaces for all configs, inside config.go but it fails,
|
|
||||||
// so do it foreach configuration np they aint so much...
|
|
||||||
|
|
||||||
if cfg != nil && len(cfg) > 0 {
|
|
||||||
config = cfg[0]
|
|
||||||
mergo.Merge(&config, c)
|
|
||||||
} else {
|
|
||||||
_default := c
|
|
||||||
config = _default
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MergeSingle merges the default with the given config and returns the result
|
|
||||||
func (c Iris) MergeSingle(cfg Iris) (config Iris) {
|
|
||||||
|
|
||||||
config = cfg
|
|
||||||
mergo.Merge(&config, c)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
185
config/server.go
185
config/server.go
|
@ -1,185 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
|
||||||
"github.com/valyala/fasthttp"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Default values for base Server conf
|
|
||||||
const (
|
|
||||||
// DefaultServerHostname returns the default hostname which is 0.0.0.0
|
|
||||||
DefaultServerHostname = "0.0.0.0"
|
|
||||||
// DefaultServerPort returns the default port which is 8080
|
|
||||||
DefaultServerPort = 8080
|
|
||||||
// DefaultMaxRequestBodySize is 8MB
|
|
||||||
DefaultMaxRequestBodySize = 2 * fasthttp.DefaultMaxRequestBodySize
|
|
||||||
|
|
||||||
// Per-connection buffer size for requests' reading.
|
|
||||||
// This also limits the maximum header size.
|
|
||||||
//
|
|
||||||
// Increase this buffer if your clients send multi-KB RequestURIs
|
|
||||||
// and/or multi-KB headers (for example, BIG cookies).
|
|
||||||
//
|
|
||||||
// Default buffer size is 8MB
|
|
||||||
DefaultReadBufferSize = 8096
|
|
||||||
|
|
||||||
// Per-connection buffer size for responses' writing.
|
|
||||||
//
|
|
||||||
// Default buffer size is 8MB
|
|
||||||
DefaultWriteBufferSize = 8096
|
|
||||||
|
|
||||||
// DefaultServerName the response header of the 'Server' value when writes to the client
|
|
||||||
DefaultServerName = "iris"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// DefaultServerAddr the default server addr which is: 0.0.0.0:8080
|
|
||||||
DefaultServerAddr = DefaultServerHostname + ":" + strconv.Itoa(DefaultServerPort)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Server used inside server for listening
|
|
||||||
type Server struct {
|
|
||||||
// ListenningAddr the addr that server listens to
|
|
||||||
ListeningAddr string
|
|
||||||
CertFile string
|
|
||||||
KeyFile string
|
|
||||||
// AutoTLS enable to get certifications from the Letsencrypt
|
|
||||||
// when this configuration field is true, the CertFile & KeyFile are empty, no need to provide a key.
|
|
||||||
//
|
|
||||||
// example: https://github.com/iris-contrib/examples/blob/master/letsencyrpt/main.go
|
|
||||||
AutoTLS bool
|
|
||||||
// Mode this is for unix only
|
|
||||||
Mode os.FileMode
|
|
||||||
// MaxRequestBodySize Maximum request body size.
|
|
||||||
//
|
|
||||||
// The server rejects requests with bodies exceeding this limit.
|
|
||||||
//
|
|
||||||
// By default request body size is 8MB.
|
|
||||||
MaxRequestBodySize int
|
|
||||||
|
|
||||||
// Per-connection buffer size for requests' reading.
|
|
||||||
// This also limits the maximum header size.
|
|
||||||
//
|
|
||||||
// Increase this buffer if your clients send multi-KB RequestURIs
|
|
||||||
// and/or multi-KB headers (for example, BIG cookies).
|
|
||||||
//
|
|
||||||
// Default buffer size is used if not set.
|
|
||||||
ReadBufferSize int
|
|
||||||
|
|
||||||
// Per-connection buffer size for responses' writing.
|
|
||||||
//
|
|
||||||
// Default buffer size is used if not set.
|
|
||||||
WriteBufferSize int
|
|
||||||
|
|
||||||
// Maximum duration for reading the full request (including body).
|
|
||||||
//
|
|
||||||
// This also limits the maximum duration for idle keep-alive
|
|
||||||
// connections.
|
|
||||||
//
|
|
||||||
// By default request read timeout is unlimited.
|
|
||||||
ReadTimeout time.Duration
|
|
||||||
|
|
||||||
// Maximum duration for writing the full response (including body).
|
|
||||||
//
|
|
||||||
// By default response write timeout is unlimited.
|
|
||||||
WriteTimeout time.Duration
|
|
||||||
|
|
||||||
// RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT)
|
|
||||||
//
|
|
||||||
// NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server
|
|
||||||
// which means that if you want to change this address you have to clear your browser's cache in order this to be able to change to the new addr.
|
|
||||||
//
|
|
||||||
// example: https://github.com/iris-contrib/examples/tree/master/multiserver_listening2
|
|
||||||
RedirectTo string
|
|
||||||
// Virtual If this server is not really listens to a real host, it mostly used in order to achieve testing without system modifications
|
|
||||||
Virtual bool
|
|
||||||
// VListeningAddr, can be used for both virtual = true or false,
|
|
||||||
// if it's setted to not empty, then the server's Host() will return this addr instead of the ListeningAddr.
|
|
||||||
// server's Host() is used inside global template helper funcs
|
|
||||||
// set it when you are sure you know what it does.
|
|
||||||
//
|
|
||||||
// Default is empty ""
|
|
||||||
VListeningAddr string
|
|
||||||
// VScheme if setted to not empty value then all template's helper funcs prepends that as the url scheme instead of the real scheme
|
|
||||||
// server's .Scheme returns VScheme if not empty && differs from real scheme
|
|
||||||
//
|
|
||||||
// Default is empty ""
|
|
||||||
VScheme string
|
|
||||||
// Name the server's name, defaults to "iris".
|
|
||||||
// You're free to change it, but I will trust you to don't, this is the only setting whose somebody, like me, can see if iris web framework is used
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ServerParseAddr parses the listening addr and returns this
|
|
||||||
func ServerParseAddr(listeningAddr string) string {
|
|
||||||
// check if addr has :port, if not do it +:80 ,we need the hostname for many cases
|
|
||||||
a := listeningAddr
|
|
||||||
if a == "" {
|
|
||||||
// check for os environments
|
|
||||||
if oshost := os.Getenv("HOST"); oshost != "" {
|
|
||||||
a = oshost
|
|
||||||
} else if oshost := os.Getenv("ADDR"); oshost != "" {
|
|
||||||
a = oshost
|
|
||||||
} else if osport := os.Getenv("PORT"); osport != "" {
|
|
||||||
a = ":" + osport
|
|
||||||
}
|
|
||||||
|
|
||||||
if a == "" {
|
|
||||||
a = DefaultServerAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if portIdx := strings.IndexByte(a, ':'); portIdx == 0 {
|
|
||||||
// if contains only :port ,then the : is the first letter, so we dont have setted a hostname, lets set it
|
|
||||||
a = DefaultServerHostname + a
|
|
||||||
}
|
|
||||||
if portIdx := strings.IndexByte(a, ':'); portIdx < 0 {
|
|
||||||
// missing port part, add it
|
|
||||||
a = a + ":80"
|
|
||||||
}
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultServer returns the default configs for the server
|
|
||||||
func DefaultServer() Server {
|
|
||||||
return Server{
|
|
||||||
ListeningAddr: DefaultServerAddr,
|
|
||||||
Name: DefaultServerName,
|
|
||||||
MaxRequestBodySize: DefaultMaxRequestBodySize,
|
|
||||||
ReadBufferSize: DefaultReadBufferSize,
|
|
||||||
WriteBufferSize: DefaultWriteBufferSize,
|
|
||||||
RedirectTo: "",
|
|
||||||
Virtual: false,
|
|
||||||
VListeningAddr: "",
|
|
||||||
VScheme: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge merges the default with the given config and returns the result
|
|
||||||
func (c Server) Merge(cfg []Server) (config Server) {
|
|
||||||
|
|
||||||
if len(cfg) > 0 {
|
|
||||||
config = cfg[0]
|
|
||||||
mergo.Merge(&config, c)
|
|
||||||
} else {
|
|
||||||
_default := c
|
|
||||||
config = _default
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MergeSingle merges the default with the given config and returns the result
|
|
||||||
func (c Server) MergeSingle(cfg Server) (config Server) {
|
|
||||||
|
|
||||||
config = cfg
|
|
||||||
mergo.Merge(&config, c)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/imdario/mergo"
|
|
||||||
"github.com/kataras/go-sessions"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
universe time.Time // 0001-01-01 00:00:00 +0000 UTC
|
|
||||||
// CookieExpireNever the default cookie's life for sessions, unlimited (23 years)
|
|
||||||
CookieExpireNever = time.Now().AddDate(23, 0, 0)
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// DefaultCookieName the secret cookie's name for sessions
|
|
||||||
DefaultCookieName = "irissessionid"
|
|
||||||
// DefaultSessionGcDuration is the default Session Manager's GCDuration , which is 2 hours
|
|
||||||
DefaultSessionGcDuration = time.Duration(2) * time.Hour
|
|
||||||
// DefaultCookieLength is the default Session Manager's CookieLength, which is 32
|
|
||||||
DefaultCookieLength = 32
|
|
||||||
)
|
|
||||||
|
|
||||||
// Sessions the configuration for sessions
|
|
||||||
// has 6 fields
|
|
||||||
// first is the cookieName, the session's name (string) ["mysessionsecretcookieid"]
|
|
||||||
// second enable if you want to decode the cookie's key also
|
|
||||||
// third is the time which the client's cookie expires
|
|
||||||
// forth is the cookie length (sessionid) int, defaults to 32, do not change if you don't have any reason to do
|
|
||||||
// fifth is the gcDuration (time.Duration) when this time passes it removes the unused sessions from the memory until the user come back
|
|
||||||
// sixth is the DisableSubdomainPersistence which you can set it to true in order dissallow your q subdomains to have access to the session cook
|
|
||||||
//
|
|
||||||
type Sessions sessions.Config
|
|
||||||
|
|
||||||
// DefaultSessions the default configs for Sessions
|
|
||||||
func DefaultSessions() Sessions {
|
|
||||||
return Sessions{
|
|
||||||
Cookie: DefaultCookieName,
|
|
||||||
CookieLength: DefaultCookieLength,
|
|
||||||
DecodeCookie: false,
|
|
||||||
Expires: 0,
|
|
||||||
GcDuration: DefaultSessionGcDuration,
|
|
||||||
DisableSubdomainPersistence: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge merges the default with the given config and returns the result
|
|
||||||
func (c Sessions) Merge(cfg []Sessions) (config Sessions) {
|
|
||||||
|
|
||||||
if len(cfg) > 0 {
|
|
||||||
config = cfg[0]
|
|
||||||
mergo.Merge(&config, c)
|
|
||||||
} else {
|
|
||||||
_default := c
|
|
||||||
config = _default
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MergeSingle merges the default with the given config and returns the result
|
|
||||||
func (c Sessions) MergeSingle(cfg Sessions) (config Sessions) {
|
|
||||||
|
|
||||||
config = cfg
|
|
||||||
mergo.Merge(&config, c)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
// Tester configuration
|
|
||||||
type Tester struct {
|
|
||||||
ListeningAddr string
|
|
||||||
ExplicitURL bool
|
|
||||||
Debug bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultTester returns the default configuration for a tester
|
|
||||||
// the ListeningAddr is used as virtual only when no running server is founded
|
|
||||||
func DefaultTester() Tester {
|
|
||||||
return Tester{ListeningAddr: "iris-go.com:1993", ExplicitURL: false, Debug: false}
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/imdario/mergo"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// DefaultWriteTimeout 15 * time.Second
|
|
||||||
DefaultWriteTimeout = 15 * time.Second
|
|
||||||
// DefaultPongTimeout 60 * time.Second
|
|
||||||
DefaultPongTimeout = 60 * time.Second
|
|
||||||
// DefaultPingPeriod (DefaultPongTimeout * 9) / 10
|
|
||||||
DefaultPingPeriod = (DefaultPongTimeout * 9) / 10
|
|
||||||
// DefaultMaxMessageSize 1024
|
|
||||||
DefaultMaxMessageSize = 1024
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
// Websocket the config contains options for the ../websocket.go
|
|
||||||
type Websocket struct {
|
|
||||||
// WriteTimeout time allowed to write a message to the connection.
|
|
||||||
// Default value is 15 * time.Second
|
|
||||||
WriteTimeout time.Duration
|
|
||||||
// PongTimeout allowed to read the next pong message from the connection
|
|
||||||
// Default value is 60 * time.Second
|
|
||||||
PongTimeout time.Duration
|
|
||||||
// PingPeriod send ping messages to the connection with this period. Must be less than PongTimeout
|
|
||||||
// Default value is (PongTimeout * 9) / 10
|
|
||||||
PingPeriod time.Duration
|
|
||||||
// MaxMessageSize max message size allowed from connection
|
|
||||||
// Default value is 1024
|
|
||||||
MaxMessageSize int64
|
|
||||||
// BinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text
|
|
||||||
// see https://github.com/kataras/iris/issues/387#issuecomment-243006022 for more
|
|
||||||
// defaults to false
|
|
||||||
BinaryMessages bool
|
|
||||||
// Endpoint is the path which the websocket server will listen for clients/connections
|
|
||||||
// Default value is empty string, if you don't set it the Websocket server is disabled.
|
|
||||||
Endpoint string
|
|
||||||
// Headers the response headers before upgrader
|
|
||||||
// Default is empty
|
|
||||||
Headers map[string]string
|
|
||||||
// ReadBufferSize is the buffer size for the underline reader
|
|
||||||
ReadBufferSize int
|
|
||||||
// WriteBufferSize is the buffer size for the underline writer
|
|
||||||
WriteBufferSize int
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultWebsocket returns the default config for iris-ws websocket package
|
|
||||||
func DefaultWebsocket() *Websocket {
|
|
||||||
return &Websocket{
|
|
||||||
WriteTimeout: DefaultWriteTimeout,
|
|
||||||
PongTimeout: DefaultPongTimeout,
|
|
||||||
PingPeriod: DefaultPingPeriod,
|
|
||||||
MaxMessageSize: DefaultMaxMessageSize,
|
|
||||||
BinaryMessages: false,
|
|
||||||
ReadBufferSize: 4096,
|
|
||||||
WriteBufferSize: 4096,
|
|
||||||
Headers: make(map[string]string, 0),
|
|
||||||
Endpoint: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Merge merges the default with the given config and returns the result
|
|
||||||
func (c *Websocket) Merge(cfg []*Websocket) (config *Websocket) {
|
|
||||||
|
|
||||||
if len(cfg) > 0 {
|
|
||||||
config = cfg[0]
|
|
||||||
mergo.Merge(config, c)
|
|
||||||
} else {
|
|
||||||
_default := c
|
|
||||||
config = _default
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MergeSingle merges the default with the given config and returns the result
|
|
||||||
func (c *Websocket) MergeSingle(cfg *Websocket) (config *Websocket) {
|
|
||||||
|
|
||||||
config = cfg
|
|
||||||
mergo.Merge(config, c)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
859
configuration.go
Normal file
859
configuration.go
Normal file
|
@ -0,0 +1,859 @@
|
||||||
|
package iris
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/imdario/mergo"
|
||||||
|
"github.com/kataras/go-options"
|
||||||
|
"github.com/kataras/go-sessions"
|
||||||
|
"github.com/valyala/fasthttp"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// OptionSetter sets a configuration field to the main configuration
|
||||||
|
// used to help developers to write less and configure only what they really want and nothing else
|
||||||
|
// example:
|
||||||
|
// iris.New(iris.Configuration{Sessions:iris.SessionConfiguration{Cookie:"mysessionid"}, Websocket: iris.WebsocketConfiguration{Endpoint:"/my_endpoint"}})
|
||||||
|
// now can be done also by using iris.Option$FIELD:
|
||||||
|
// iris.New(irisOptionSessionsCookie("mycookieid"),iris.OptionWebsocketEndpoint("my_endpoint"))
|
||||||
|
// benefits:
|
||||||
|
// 1. user/dev have no worries what option to pass, he/she can just press iris.Option and all options should be shown to her/his editor's autocomplete-popup window
|
||||||
|
// 2. can be passed with any order
|
||||||
|
// 3. Can override previous configuration
|
||||||
|
OptionSetter interface {
|
||||||
|
// Set receives a pointer to the global Configuration type and does the job of filling it
|
||||||
|
Set(c *Configuration)
|
||||||
|
}
|
||||||
|
// OptionSet implements the OptionSetter
|
||||||
|
OptionSet func(c *Configuration)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set is the func which makes the OptionSet an OptionSetter, this is used mostly
|
||||||
|
func (o OptionSet) Set(c *Configuration) {
|
||||||
|
o(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuration the whole configuration for an iris instance ($instance.Config) or global iris instance (iris.Config)
|
||||||
|
// these can be passed via options also, look at the top of this file(configuration.go)
|
||||||
|
//
|
||||||
|
// Configuration is also implements the OptionSet so it's a valid option itself, this is briliant enough
|
||||||
|
type Configuration struct {
|
||||||
|
// DisablePathCorrection corrects and redirects the requested path to the registed path
|
||||||
|
// for example, if /home/ path is requested but no handler for this Route found,
|
||||||
|
// then the Router checks if /home handler exists, if yes,
|
||||||
|
// (permant)redirects the client to the correct path /home
|
||||||
|
//
|
||||||
|
// Default is false
|
||||||
|
DisablePathCorrection bool
|
||||||
|
|
||||||
|
// DisablePathEscape when is false then its escapes the path, the named parameters (if any).
|
||||||
|
// Change to true it if you want something like this https://github.com/kataras/iris/issues/135 to work
|
||||||
|
//
|
||||||
|
// When do you need to Disable(true) it:
|
||||||
|
// accepts parameters with slash '/'
|
||||||
|
// Request: http://localhost:8080/details/Project%2FDelta
|
||||||
|
// ctx.Param("project") returns the raw named parameter: Project%2FDelta
|
||||||
|
// which you can escape it manually with net/url:
|
||||||
|
// projectName, _ := url.QueryUnescape(c.Param("project").
|
||||||
|
// Look here: https://github.com/kataras/iris/issues/135 for more
|
||||||
|
//
|
||||||
|
// Default is false
|
||||||
|
DisablePathEscape bool
|
||||||
|
|
||||||
|
// DisableBanner outputs the iris banner at startup
|
||||||
|
//
|
||||||
|
// Default is false
|
||||||
|
DisableBanner bool
|
||||||
|
|
||||||
|
// LoggerOut is the destination for output
|
||||||
|
//
|
||||||
|
// Default is os.Stdout
|
||||||
|
LoggerOut io.Writer
|
||||||
|
// LoggerPreffix is the logger's prefix to write at beginning of each line
|
||||||
|
//
|
||||||
|
// Default is [IRIS]
|
||||||
|
LoggerPreffix string
|
||||||
|
|
||||||
|
// ProfilePath a the route path, set it to enable http pprof tool
|
||||||
|
// Default is empty, if you set it to a $path, these routes will handled:
|
||||||
|
// $path/cmdline
|
||||||
|
// $path/profile
|
||||||
|
// $path/symbol
|
||||||
|
// $path/goroutine
|
||||||
|
// $path/heap
|
||||||
|
// $path/threadcreate
|
||||||
|
// $path/pprof/block
|
||||||
|
// for example if '/debug/pprof'
|
||||||
|
// http://yourdomain:PORT/debug/pprof/
|
||||||
|
// http://yourdomain:PORT/debug/pprof/cmdline
|
||||||
|
// http://yourdomain:PORT/debug/pprof/profile
|
||||||
|
// http://yourdomain:PORT/debug/pprof/symbol
|
||||||
|
// http://yourdomain:PORT/debug/pprof/goroutine
|
||||||
|
// http://yourdomain:PORT/debug/pprof/heap
|
||||||
|
// http://yourdomain:PORT/debug/pprof/threadcreate
|
||||||
|
// http://yourdomain:PORT/debug/pprof/pprof/block
|
||||||
|
// it can be a subdomain also, for example, if 'debug.'
|
||||||
|
// http://debug.yourdomain:PORT/
|
||||||
|
// http://debug.yourdomain:PORT/cmdline
|
||||||
|
// http://debug.yourdomain:PORT/profile
|
||||||
|
// http://debug.yourdomain:PORT/symbol
|
||||||
|
// http://debug.yourdomain:PORT/goroutine
|
||||||
|
// http://debug.yourdomain:PORT/heap
|
||||||
|
// http://debug.yourdomain:PORT/threadcreate
|
||||||
|
// http://debug.yourdomain:PORT/pprof/block
|
||||||
|
ProfilePath string
|
||||||
|
|
||||||
|
// DisableTemplateEngines set to true to disable loading the default template engine (html/template) and disallow the use of iris.UseEngine
|
||||||
|
// default is false
|
||||||
|
DisableTemplateEngines bool
|
||||||
|
|
||||||
|
// IsDevelopment iris will act like a developer, for example
|
||||||
|
// If true then re-builds the templates on each request
|
||||||
|
// default is false
|
||||||
|
IsDevelopment bool
|
||||||
|
|
||||||
|
// TimeFormat time format for any kind of datetime parsing
|
||||||
|
TimeFormat string
|
||||||
|
|
||||||
|
// Charset character encoding for various rendering
|
||||||
|
// used for templates and the rest of the responses
|
||||||
|
// defaults to "UTF-8"
|
||||||
|
Charset string
|
||||||
|
|
||||||
|
// Gzip enables gzip compression on your Render actions, this includes any type of render, templates and pure/raw content
|
||||||
|
// If you don't want to enable it globaly, you could just use the third parameter on context.Render("myfileOrResponse", structBinding{}, iris.RenderOptions{"gzip": true})
|
||||||
|
// defaults to false
|
||||||
|
Gzip bool
|
||||||
|
|
||||||
|
// Sessions contains the configs for sessions
|
||||||
|
Sessions SessionsConfiguration
|
||||||
|
|
||||||
|
// Websocket contains the configs for Websocket's server integration
|
||||||
|
Websocket WebsocketConfiguration
|
||||||
|
|
||||||
|
// Tester contains the configs for the test framework, so far we have only one because all test framework's configs are setted by the iris itself
|
||||||
|
// You can find example on the https://github.com/kataras/iris/glob/master/context_test.go
|
||||||
|
Tester TesterConfiguration
|
||||||
|
|
||||||
|
// Other are the custom, dynamic options, can be empty
|
||||||
|
// this fill used only by you to set any app's options you want
|
||||||
|
// for each of an Iris instance
|
||||||
|
Other options.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set implements the OptionSetter
|
||||||
|
func (c Configuration) Set(main *Configuration) {
|
||||||
|
mergo.MergeWithOverwrite(main, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// All options starts with "Option" preffix in order to be easier to find what dev searching for
|
||||||
|
var (
|
||||||
|
// OptionDisablePathCorrection corrects and redirects the requested path to the registed path
|
||||||
|
// for example, if /home/ path is requested but no handler for this Route found,
|
||||||
|
// then the Router checks if /home handler exists, if yes,
|
||||||
|
// (permant)redirects the client to the correct path /home
|
||||||
|
//
|
||||||
|
// Default is false
|
||||||
|
OptionDisablePathCorrection = func(val bool) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.DisablePathCorrection = val
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// OptionDisablePathEscape when is false then its escapes the path, the named parameters (if any).
|
||||||
|
OptionDisablePathEscape = func(val bool) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.DisablePathEscape = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionDisableBanner outputs the iris banner at startup
|
||||||
|
//
|
||||||
|
// Default is false
|
||||||
|
OptionDisableBanner = func(val bool) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.DisableBanner = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionLoggerOut is the destination for output
|
||||||
|
//
|
||||||
|
// Default is os.Stdout
|
||||||
|
OptionLoggerOut = func(val io.Writer) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.LoggerOut = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionLoggerPreffix is the logger's prefix to write at beginning of each line
|
||||||
|
//
|
||||||
|
// Default is [IRIS]
|
||||||
|
OptionLoggerPreffix = func(val string) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.LoggerPreffix = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionProfilePath a the route path, set it to enable http pprof tool
|
||||||
|
// Default is empty, if you set it to a $path, these routes will handled:
|
||||||
|
OptionProfilePath = func(val string) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.ProfilePath = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionDisableTemplateEngines set to true to disable loading the default template engine (html/template) and disallow the use of iris.UseEngine
|
||||||
|
// Default is false
|
||||||
|
OptionDisableTemplateEngines = func(val bool) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.DisableTemplateEngines = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionIsDevelopment iris will act like a developer, for example
|
||||||
|
// If true then re-builds the templates on each request
|
||||||
|
// Default is false
|
||||||
|
OptionIsDevelopment = func(val bool) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.IsDevelopment = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionTimeFormat time format for any kind of datetime parsing
|
||||||
|
OptionTimeFormat = func(val string) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.TimeFormat = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionCharset character encoding for various rendering
|
||||||
|
// used for templates and the rest of the responses
|
||||||
|
// Default is "UTF-8"
|
||||||
|
OptionCharset = func(val string) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Charset = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionGzip enables gzip compression on your Render actions, this includes any type of render, templates and pure/raw content
|
||||||
|
// If you don't want to enable it globaly, you could just use the third parameter on context.Render("myfileOrResponse", structBinding{}, iris.RenderOptions{"gzip": true})
|
||||||
|
// Default is false
|
||||||
|
OptionGzip = func(val bool) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Gzip = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionOther are the custom, dynamic options, can be empty
|
||||||
|
// this fill used only by you to set any app's options you want
|
||||||
|
// for each of an Iris instance
|
||||||
|
OptionOther = func(val ...options.Options) OptionSet {
|
||||||
|
opts := options.Options{}
|
||||||
|
for _, opt := range val {
|
||||||
|
for k, v := range opt {
|
||||||
|
opts[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Other = opts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultTimeFormat default time format for any kind of datetime parsing
|
||||||
|
DefaultTimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
|
||||||
|
// StaticCacheDuration expiration duration for INACTIVE file handlers, it's a global configuration field to all iris instances
|
||||||
|
StaticCacheDuration = 20 * time.Second
|
||||||
|
// CompressedFileSuffix is the suffix to add to the name of
|
||||||
|
// cached compressed file when using the .StaticFS function.
|
||||||
|
//
|
||||||
|
// Defaults to iris-fasthttp.gz
|
||||||
|
CompressedFileSuffix = "iris-fasthttp.gz"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default values for base Iris conf
|
||||||
|
const (
|
||||||
|
DefaultDisablePathCorrection = false
|
||||||
|
DefaultDisablePathEscape = false
|
||||||
|
DefaultCharset = "UTF-8"
|
||||||
|
DefaultLoggerPreffix = "[IRIS] "
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultLoggerOut is the default logger's output
|
||||||
|
DefaultLoggerOut = os.Stdout
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultConfiguration returns the default configuration for an Iris station, fills the main Configuration
|
||||||
|
func DefaultConfiguration() Configuration {
|
||||||
|
return Configuration{
|
||||||
|
DisablePathCorrection: DefaultDisablePathCorrection,
|
||||||
|
DisablePathEscape: DefaultDisablePathEscape,
|
||||||
|
DisableBanner: false,
|
||||||
|
LoggerOut: DefaultLoggerOut,
|
||||||
|
LoggerPreffix: DefaultLoggerPreffix,
|
||||||
|
DisableTemplateEngines: false,
|
||||||
|
IsDevelopment: false,
|
||||||
|
TimeFormat: DefaultTimeFormat,
|
||||||
|
Charset: DefaultCharset,
|
||||||
|
Gzip: false,
|
||||||
|
ProfilePath: "",
|
||||||
|
Sessions: DefaultSessionsConfiguration(),
|
||||||
|
Websocket: DefaultWebsocketConfiguration(),
|
||||||
|
Tester: DefaultTesterConfiguration(),
|
||||||
|
Other: options.Options{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionsConfiguration the configuration for sessions
|
||||||
|
// has 6 fields
|
||||||
|
// first is the cookieName, the session's name (string) ["mysessionsecretcookieid"]
|
||||||
|
// second enable if you want to decode the cookie's key also
|
||||||
|
// third is the time which the client's cookie expires
|
||||||
|
// forth is the cookie length (sessionid) int, defaults to 32, do not change if you don't have any reason to do
|
||||||
|
// fifth is the gcDuration (time.Duration) when this time passes it removes the unused sessions from the memory until the user come back
|
||||||
|
// sixth is the DisableSubdomainPersistence which you can set it to true in order dissallow your q subdomains to have access to the session cook
|
||||||
|
type SessionsConfiguration sessions.Config
|
||||||
|
|
||||||
|
var (
|
||||||
|
// OptionSessionsCookie string, the session's client cookie name, for example: "qsessionid"
|
||||||
|
OptionSessionsCookie = func(val string) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Sessions.Cookie = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionSessionsDecodeCookie set it to true to decode the cookie key with base64 URLEncoding
|
||||||
|
// Defaults to false
|
||||||
|
OptionSessionsDecodeCookie = func(val bool) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Sessions.DecodeCookie = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionSessionsExpires the duration of which the cookie must expires (created_time.Add(Expires)).
|
||||||
|
// If you want to delete the cookie when the browser closes, set it to -1 but in this case, the server side's session duration is up to GcDuration
|
||||||
|
//
|
||||||
|
// Default infinitive/unlimited life duration(0)
|
||||||
|
OptionSessionsExpires = func(val time.Duration) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Sessions.Expires = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionSessionsCookieLength the length of the sessionid's cookie's value, let it to 0 if you don't want to change it
|
||||||
|
// Defaults to 32
|
||||||
|
OptionSessionsCookieLength = func(val int) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Sessions.CookieLength = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionSessionsGcDuration every how much duration(GcDuration) the memory should be clear for unused cookies (GcDuration)
|
||||||
|
// for example: time.Duration(2)*time.Hour. it will check every 2 hours if cookie hasn't be used for 2 hours,
|
||||||
|
// deletes it from backend memory until the user comes back, then the session continue to work as it was
|
||||||
|
//
|
||||||
|
// Default 2 hours
|
||||||
|
OptionSessionsGcDuration = func(val time.Duration) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Sessions.GcDuration = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionSessionsDisableSubdomainPersistence set it to true in order dissallow your q subdomains to have access to the session cookie
|
||||||
|
// defaults to false
|
||||||
|
OptionSessionsDisableSubdomainPersistence = func(val bool) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Sessions.DisableSubdomainPersistence = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
universe time.Time // 0001-01-01 00:00:00 +0000 UTC
|
||||||
|
// CookieExpireNever the default cookie's life for sessions, unlimited (23 years)
|
||||||
|
CookieExpireNever = time.Now().AddDate(23, 0, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultCookieName the secret cookie's name for sessions
|
||||||
|
DefaultCookieName = "irissessionid"
|
||||||
|
// DefaultSessionGcDuration is the default Session Manager's GCDuration , which is 2 hours
|
||||||
|
DefaultSessionGcDuration = time.Duration(2) * time.Hour
|
||||||
|
// DefaultCookieLength is the default Session Manager's CookieLength, which is 32
|
||||||
|
DefaultCookieLength = 32
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultSessionsConfiguration the default configs for Sessions
|
||||||
|
func DefaultSessionsConfiguration() SessionsConfiguration {
|
||||||
|
return SessionsConfiguration{
|
||||||
|
Cookie: DefaultCookieName,
|
||||||
|
CookieLength: DefaultCookieLength,
|
||||||
|
DecodeCookie: false,
|
||||||
|
Expires: 0,
|
||||||
|
GcDuration: DefaultSessionGcDuration,
|
||||||
|
DisableSubdomainPersistence: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WebsocketConfiguration the config contains options for the Websocket main config field
|
||||||
|
type WebsocketConfiguration struct {
|
||||||
|
// WriteTimeout time allowed to write a message to the connection.
|
||||||
|
// Default value is 15 * time.Second
|
||||||
|
WriteTimeout time.Duration
|
||||||
|
// PongTimeout allowed to read the next pong message from the connection
|
||||||
|
// Default value is 60 * time.Second
|
||||||
|
PongTimeout time.Duration
|
||||||
|
// PingPeriod send ping messages to the connection with this period. Must be less than PongTimeout
|
||||||
|
// Default value is (PongTimeout * 9) / 10
|
||||||
|
PingPeriod time.Duration
|
||||||
|
// MaxMessageSize max message size allowed from connection
|
||||||
|
// Default value is 1024
|
||||||
|
MaxMessageSize int64
|
||||||
|
// BinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text
|
||||||
|
// see https://github.com/kataras/iris/issues/387#issuecomment-243006022 for more
|
||||||
|
// defaults to false
|
||||||
|
BinaryMessages bool
|
||||||
|
// Endpoint is the path which the websocket server will listen for clients/connections
|
||||||
|
// Default value is empty string, if you don't set it the Websocket server is disabled.
|
||||||
|
Endpoint string
|
||||||
|
// ReadBufferSize is the buffer size for the underline reader
|
||||||
|
ReadBufferSize int
|
||||||
|
// WriteBufferSize is the buffer size for the underline writer
|
||||||
|
WriteBufferSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// OptionWebsocketWriteTimeout time allowed to write a message to the connection.
|
||||||
|
// Default value is 15 * time.Second
|
||||||
|
OptionWebsocketWriteTimeout = func(val time.Duration) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Websocket.WriteTimeout = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionWebsocketPongTimeout allowed to read the next pong message from the connection
|
||||||
|
// Default value is 60 * time.Second
|
||||||
|
OptionWebsocketPongTimeout = func(val time.Duration) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Websocket.PongTimeout = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionWebsocketPingPeriod send ping messages to the connection with this period. Must be less than PongTimeout
|
||||||
|
// Default value is (PongTimeout * 9) / 10
|
||||||
|
OptionWebsocketPingPeriod = func(val time.Duration) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Websocket.PingPeriod = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionWebsocketMaxMessageSize max message size allowed from connection
|
||||||
|
// Default value is 1024
|
||||||
|
OptionWebsocketMaxMessageSize = func(val int64) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Websocket.MaxMessageSize = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionWebsocketBinaryMessages set it to true in order to denotes binary data messages instead of utf-8 text
|
||||||
|
// see https://github.com/kataras/iris/issues/387#issuecomment-243006022 for more
|
||||||
|
// defaults to false
|
||||||
|
OptionWebsocketBinaryMessages = func(val bool) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Websocket.BinaryMessages = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionWebsocketEndpoint is the path which the websocket server will listen for clients/connections
|
||||||
|
// Default value is empty string, if you don't set it the Websocket server is disabled.
|
||||||
|
OptionWebsocketEndpoint = func(val string) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Websocket.Endpoint = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionWebsocketReadBufferSize is the buffer size for the underline reader
|
||||||
|
OptionWebsocketReadBufferSize = func(val int) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Websocket.ReadBufferSize = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionWebsocketWriteBufferSize is the buffer size for the underline writer
|
||||||
|
OptionWebsocketWriteBufferSize = func(val int) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Websocket.WriteBufferSize = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultWriteTimeout 15 * time.Second
|
||||||
|
DefaultWriteTimeout = 15 * time.Second
|
||||||
|
// DefaultPongTimeout 60 * time.Second
|
||||||
|
DefaultPongTimeout = 60 * time.Second
|
||||||
|
// DefaultPingPeriod (DefaultPongTimeout * 9) / 10
|
||||||
|
DefaultPingPeriod = (DefaultPongTimeout * 9) / 10
|
||||||
|
// DefaultMaxMessageSize 1024
|
||||||
|
DefaultMaxMessageSize = 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultWebsocketConfiguration returns the default config for iris-ws websocket package
|
||||||
|
func DefaultWebsocketConfiguration() WebsocketConfiguration {
|
||||||
|
return WebsocketConfiguration{
|
||||||
|
WriteTimeout: DefaultWriteTimeout,
|
||||||
|
PongTimeout: DefaultPongTimeout,
|
||||||
|
PingPeriod: DefaultPingPeriod,
|
||||||
|
MaxMessageSize: DefaultMaxMessageSize,
|
||||||
|
BinaryMessages: false,
|
||||||
|
ReadBufferSize: 4096,
|
||||||
|
WriteBufferSize: 4096,
|
||||||
|
Endpoint: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TesterConfiguration configuration used inside main config field 'Tester'
|
||||||
|
type TesterConfiguration struct {
|
||||||
|
// ListeningAddr is the virtual server's listening addr (host)
|
||||||
|
// Default is "iris-go.com:1993"
|
||||||
|
ListeningAddr string
|
||||||
|
// ExplicitURL If true then the url (should) be prepended manually, useful when want to test subdomains
|
||||||
|
// Default is false
|
||||||
|
ExplicitURL bool
|
||||||
|
// Debug if true then debug messages from the httpexpect will be shown when a test runs
|
||||||
|
// Default is false
|
||||||
|
Debug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// OptionTesterListeningAddr is the virtual server's listening addr (host)
|
||||||
|
// Default is "iris-go.com:1993"
|
||||||
|
OptionTesterListeningAddr = func(val string) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Tester.ListeningAddr = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionTesterExplicitURL If true then the url (should) be prepended manually, useful when want to test subdomains
|
||||||
|
// Default is false
|
||||||
|
OptionTesterExplicitURL = func(val bool) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Tester.ExplicitURL = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionTesterDebug if true then debug messages from the httpexpect will be shown when a test runs
|
||||||
|
// Default is false
|
||||||
|
OptionTesterDebug = func(val bool) OptionSet {
|
||||||
|
return func(c *Configuration) {
|
||||||
|
c.Tester.Debug = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultTesterConfiguration returns the default configuration for a tester
|
||||||
|
// the ListeningAddr is used as virtual only when no running server is founded
|
||||||
|
func DefaultTesterConfiguration() TesterConfiguration {
|
||||||
|
return TesterConfiguration{ListeningAddr: "iris-go.com:1993", ExplicitURL: false, Debug: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerConfiguration is the configuration which is used inside iris' server(s) for listening to
|
||||||
|
type ServerConfiguration struct {
|
||||||
|
// ListenningAddr the addr that server listens to
|
||||||
|
ListeningAddr string
|
||||||
|
CertFile string
|
||||||
|
KeyFile string
|
||||||
|
// AutoTLS enable to get certifications from the Letsencrypt
|
||||||
|
// when this configuration field is true, the CertFile & KeyFile are empty, no need to provide a key.
|
||||||
|
//
|
||||||
|
// example: https://github.com/iris-contrib/examples/blob/master/letsencyrpt/main.go
|
||||||
|
AutoTLS bool
|
||||||
|
// Mode this is for unix only
|
||||||
|
Mode os.FileMode
|
||||||
|
// MaxRequestBodySize Maximum request body size.
|
||||||
|
//
|
||||||
|
// The server rejects requests with bodies exceeding this limit.
|
||||||
|
//
|
||||||
|
// By default request body size is 8MB.
|
||||||
|
MaxRequestBodySize int
|
||||||
|
|
||||||
|
// Per-connection buffer size for requests' reading.
|
||||||
|
// This also limits the maximum header size.
|
||||||
|
//
|
||||||
|
// Increase this buffer if your clients send multi-KB RequestURIs
|
||||||
|
// and/or multi-KB headers (for example, BIG cookies).
|
||||||
|
//
|
||||||
|
// Default buffer size is used if not set.
|
||||||
|
ReadBufferSize int
|
||||||
|
|
||||||
|
// Per-connection buffer size for responses' writing.
|
||||||
|
//
|
||||||
|
// Default buffer size is used if not set.
|
||||||
|
WriteBufferSize int
|
||||||
|
|
||||||
|
// Maximum duration for reading the full request (including body).
|
||||||
|
//
|
||||||
|
// This also limits the maximum duration for idle keep-alive
|
||||||
|
// connections.
|
||||||
|
//
|
||||||
|
// By default request read timeout is unlimited.
|
||||||
|
ReadTimeout time.Duration
|
||||||
|
|
||||||
|
// Maximum duration for writing the full response (including body).
|
||||||
|
//
|
||||||
|
// By default response write timeout is unlimited.
|
||||||
|
WriteTimeout time.Duration
|
||||||
|
|
||||||
|
// RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT)
|
||||||
|
//
|
||||||
|
// NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server
|
||||||
|
// which means that if you want to change this address you have to clear your browser's cache in order this to be able to change to the new addr.
|
||||||
|
//
|
||||||
|
// example: https://github.com/iris-contrib/examples/tree/master/multiserver_listening2
|
||||||
|
RedirectTo string
|
||||||
|
// Virtual If this server is not really listens to a real host, it mostly used in order to achieve testing without system modifications
|
||||||
|
Virtual bool
|
||||||
|
// VListeningAddr, can be used for both virtual = true or false,
|
||||||
|
// if it's setted to not empty, then the server's Host() will return this addr instead of the ListeningAddr.
|
||||||
|
// server's Host() is used inside global template helper funcs
|
||||||
|
// set it when you are sure you know what it does.
|
||||||
|
//
|
||||||
|
// Default is empty ""
|
||||||
|
VListeningAddr string
|
||||||
|
// VScheme if setted to not empty value then all template's helper funcs prepends that as the url scheme instead of the real scheme
|
||||||
|
// server's .Scheme returns VScheme if not empty && differs from real scheme
|
||||||
|
//
|
||||||
|
// Default is empty ""
|
||||||
|
VScheme string
|
||||||
|
// Name the server's name, defaults to "iris".
|
||||||
|
// You're free to change it, but I will trust you to don't, this is the only setting whose somebody, like me, can see if iris web framework is used
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: ServerConfiguration is the only one config which has its own option setter because
|
||||||
|
// it's independent from a specific iris instance:
|
||||||
|
// same server can run on multi iris instance
|
||||||
|
// one iris instance/station can have and listening to more than one server.
|
||||||
|
|
||||||
|
// OptionServerSettter server configuration option setter
|
||||||
|
type OptionServerSettter interface {
|
||||||
|
Set(c *ServerConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionServerSet is the func which implements the OptionServerSettter, this is used widely
|
||||||
|
type OptionServerSet func(c *ServerConfiguration)
|
||||||
|
|
||||||
|
// Set is the func which makes OptionServerSet implements the OptionServerSettter
|
||||||
|
func (o OptionServerSet) Set(c *ServerConfiguration) {
|
||||||
|
o(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set implements the OptionServerSettter to the ServerConfiguration
|
||||||
|
func (c ServerConfiguration) Set(main *ServerConfiguration) {
|
||||||
|
mergo.MergeWithOverwrite(main, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options for ServerConfiguration
|
||||||
|
var (
|
||||||
|
OptionServerListeningAddr = func(val string) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.ListeningAddr = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionServerCertFile = func(val string) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.CertFile = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionServerKeyFile = func(val string) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.KeyFile = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoTLS enable to get certifications from the Letsencrypt
|
||||||
|
// when this configuration field is true, the CertFile & KeyFile are empty, no need to provide a key.
|
||||||
|
//
|
||||||
|
// example: https://github.com/iris-contrib/examples/blob/master/letsencyrpt/main.go
|
||||||
|
OptionServerAutoTLS = func(val bool) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.AutoTLS = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode this is for unix only
|
||||||
|
OptionServerMode = func(val os.FileMode) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.Mode = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionServerMaxRequestBodySize Maximum request body size.
|
||||||
|
//
|
||||||
|
// The server rejects requests with bodies exceeding this limit.
|
||||||
|
//
|
||||||
|
// By default request body size is 8MB.
|
||||||
|
OptionServerMaxRequestBodySize = func(val int) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.MaxRequestBodySize = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-connection buffer size for requests' reading.
|
||||||
|
// This also limits the maximum header size.
|
||||||
|
//
|
||||||
|
// Increase this buffer if your clients send multi-KB RequestURIs
|
||||||
|
// and/or multi-KB headers (for example, BIG cookies).
|
||||||
|
//
|
||||||
|
// Default buffer size is used if not set.
|
||||||
|
OptionServerReadBufferSize = func(val int) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.ReadBufferSize = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-connection buffer size for responses' writing.
|
||||||
|
//
|
||||||
|
// Default buffer size is used if not set.
|
||||||
|
OptionServerWriteBufferSize = func(val int) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.WriteBufferSize = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum duration for reading the full request (including body).
|
||||||
|
//
|
||||||
|
// This also limits the maximum duration for idle keep-alive
|
||||||
|
// connections.
|
||||||
|
//
|
||||||
|
// By default request read timeout is unlimited.
|
||||||
|
OptionServerReadTimeout = func(val time.Duration) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.ReadTimeout = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum duration for writing the full response (including body).
|
||||||
|
//
|
||||||
|
// By default response write timeout is unlimited.
|
||||||
|
OptionServerWriteTimeout = func(val time.Duration) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.WriteTimeout = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedirectTo, defaults to empty, set it in order to override the station's handler and redirect all requests to this address which is of form(HOST:PORT or :PORT)
|
||||||
|
//
|
||||||
|
// NOTE: the http status is 'StatusMovedPermanently', means one-time-redirect(the browser remembers the new addr and goes to the new address without need to request something from this server
|
||||||
|
// which means that if you want to change this address you have to clear your browser's cache in order this to be able to change to the new addr.
|
||||||
|
//
|
||||||
|
// example: https://github.com/iris-contrib/examples/tree/master/multiserver_listening2
|
||||||
|
OptionServerRedirectTo = func(val string) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.RedirectTo = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionServerVirtual If this server is not really listens to a real host, it mostly used in order to achieve testing without system modifications
|
||||||
|
OptionServerVirtual = func(val bool) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.Virtual = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// OptionServerVListeningAddr, can be used for both virtual = true or false,
|
||||||
|
// if it's setted to not empty, then the server's Host() will return this addr instead of the ListeningAddr.
|
||||||
|
// server's Host() is used inside global template helper funcs
|
||||||
|
// set it when you are sure you know what it does.
|
||||||
|
//
|
||||||
|
// Default is empty ""
|
||||||
|
OptionServerVListeningAddr = func(val string) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.VListeningAddr = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionServerVScheme if setted to not empty value then all template's helper funcs prepends that as the url scheme instead of the real scheme
|
||||||
|
// server's .Scheme returns VScheme if not empty && differs from real scheme
|
||||||
|
//
|
||||||
|
// Default is empty ""
|
||||||
|
OptionServerVScheme = func(val string) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.VScheme = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OptionServerName the server's name, defaults to "iris".
|
||||||
|
// You're free to change it, but I will trust you to don't, this is the only setting whose somebody, like me, can see if iris web framework is used
|
||||||
|
OptionServerName = func(val string) OptionServerSet {
|
||||||
|
return func(c *ServerConfiguration) {
|
||||||
|
c.ListeningAddr = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServerParseAddr parses the listening addr and returns this
|
||||||
|
func ServerParseAddr(listeningAddr string) string {
|
||||||
|
// check if addr has :port, if not do it +:80 ,we need the hostname for many cases
|
||||||
|
a := listeningAddr
|
||||||
|
if a == "" {
|
||||||
|
// check for os environments
|
||||||
|
if oshost := os.Getenv("HOST"); oshost != "" {
|
||||||
|
a = oshost
|
||||||
|
} else if oshost := os.Getenv("ADDR"); oshost != "" {
|
||||||
|
a = oshost
|
||||||
|
} else if osport := os.Getenv("PORT"); osport != "" {
|
||||||
|
a = ":" + osport
|
||||||
|
}
|
||||||
|
|
||||||
|
if a == "" {
|
||||||
|
a = DefaultServerAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if portIdx := strings.IndexByte(a, ':'); portIdx == 0 {
|
||||||
|
// if contains only :port ,then the : is the first letter, so we dont have setted a hostname, lets set it
|
||||||
|
a = DefaultServerHostname + a
|
||||||
|
}
|
||||||
|
if portIdx := strings.IndexByte(a, ':'); portIdx < 0 {
|
||||||
|
// missing port part, add it
|
||||||
|
a = a + ":80"
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default values for base Server conf
|
||||||
|
const (
|
||||||
|
// DefaultServerHostname returns the default hostname which is 0.0.0.0
|
||||||
|
DefaultServerHostname = "0.0.0.0"
|
||||||
|
// DefaultServerPort returns the default port which is 8080
|
||||||
|
DefaultServerPort = 8080
|
||||||
|
// DefaultMaxRequestBodySize is 8MB
|
||||||
|
DefaultMaxRequestBodySize = 2 * fasthttp.DefaultMaxRequestBodySize
|
||||||
|
|
||||||
|
// Per-connection buffer size for requests' reading.
|
||||||
|
// This also limits the maximum header size.
|
||||||
|
//
|
||||||
|
// Increase this buffer if your clients send multi-KB RequestURIs
|
||||||
|
// and/or multi-KB headers (for example, BIG cookies).
|
||||||
|
//
|
||||||
|
// Default buffer size is 8MB
|
||||||
|
DefaultReadBufferSize = 8096
|
||||||
|
|
||||||
|
// Per-connection buffer size for responses' writing.
|
||||||
|
//
|
||||||
|
// Default buffer size is 8MB
|
||||||
|
DefaultWriteBufferSize = 8096
|
||||||
|
|
||||||
|
// DefaultServerName the response header of the 'Server' value when writes to the client
|
||||||
|
DefaultServerName = "iris"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultServerAddr the default server addr which is: 0.0.0.0:8080
|
||||||
|
DefaultServerAddr = DefaultServerHostname + ":" + strconv.Itoa(DefaultServerPort)
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultServerConfiguration returns the default configs for the server
|
||||||
|
func DefaultServerConfiguration() ServerConfiguration {
|
||||||
|
return ServerConfiguration{
|
||||||
|
ListeningAddr: DefaultServerAddr,
|
||||||
|
Name: DefaultServerName,
|
||||||
|
MaxRequestBodySize: DefaultMaxRequestBodySize,
|
||||||
|
ReadBufferSize: DefaultReadBufferSize,
|
||||||
|
WriteBufferSize: DefaultWriteBufferSize,
|
||||||
|
RedirectTo: "",
|
||||||
|
Virtual: false,
|
||||||
|
VListeningAddr: "",
|
||||||
|
VScheme: "",
|
||||||
|
}
|
||||||
|
}
|
102
configuration_test.go
Normal file
102
configuration_test.go
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package iris
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// go test -v -run TestConfig*
|
||||||
|
|
||||||
|
func TestConfigStatic(t *testing.T) {
|
||||||
|
def := DefaultConfiguration()
|
||||||
|
|
||||||
|
api := New(def)
|
||||||
|
afterNew := *api.Config
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(def, afterNew) {
|
||||||
|
t.Fatalf("Default configuration is not the same after NewFromConfig expected:\n %#v \ngot:\n %#v", def, afterNew)
|
||||||
|
}
|
||||||
|
|
||||||
|
afterNew.Charset = "changed"
|
||||||
|
|
||||||
|
if reflect.DeepEqual(def, afterNew) {
|
||||||
|
t.Fatalf("Configuration should be not equal, got: %#v", afterNew)
|
||||||
|
}
|
||||||
|
|
||||||
|
api = New(Configuration{IsDevelopment: true})
|
||||||
|
|
||||||
|
afterNew = *api.Config
|
||||||
|
|
||||||
|
if api.Config.IsDevelopment == false {
|
||||||
|
t.Fatalf("Passing a Configuration field as Option fails, expected IsDevelopment to be true but was false")
|
||||||
|
}
|
||||||
|
|
||||||
|
api = New() // empty , means defaults so
|
||||||
|
if !reflect.DeepEqual(def, *api.Config) {
|
||||||
|
t.Fatalf("Default configuration is not the same after NewFromConfig expected:\n %#v \ngot:\n %#v", def, *api.Config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigOptions(t *testing.T) {
|
||||||
|
charset := "MYCHARSET"
|
||||||
|
dev := true
|
||||||
|
|
||||||
|
api := New(OptionCharset(charset), OptionIsDevelopment(dev))
|
||||||
|
|
||||||
|
if got := api.Config.Charset; got != charset {
|
||||||
|
t.Fatalf("Expected configuration Charset to be: %s but got: %s", charset, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := api.Config.IsDevelopment; got != dev {
|
||||||
|
t.Fatalf("Expected configuration IsDevelopment to be: %#v but got: %#v", dev, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// now check if other default values are setted (should be setted automatically)
|
||||||
|
|
||||||
|
expected := DefaultConfiguration()
|
||||||
|
expected.Charset = charset
|
||||||
|
expected.IsDevelopment = dev
|
||||||
|
|
||||||
|
has := *api.Config
|
||||||
|
if !reflect.DeepEqual(has, expected) {
|
||||||
|
t.Fatalf("Default configuration is not the same after New expected:\n %#v \ngot:\n %#v", expected, has)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigOptionsDeep(t *testing.T) {
|
||||||
|
cookiename := "MYSESSIONID"
|
||||||
|
charset := "MYCHARSET"
|
||||||
|
dev := true
|
||||||
|
profilePath := "/mypprof"
|
||||||
|
// first session, after charset,dev and profilepath, no canonical order.
|
||||||
|
api := New(OptionSessionsCookie(cookiename), OptionCharset(charset), OptionIsDevelopment(dev), OptionProfilePath(profilePath))
|
||||||
|
|
||||||
|
expected := DefaultConfiguration()
|
||||||
|
expected.Sessions.Cookie = cookiename
|
||||||
|
expected.Charset = charset
|
||||||
|
expected.IsDevelopment = dev
|
||||||
|
expected.ProfilePath = profilePath
|
||||||
|
|
||||||
|
has := *api.Config
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(has, expected) {
|
||||||
|
t.Fatalf("DEEP configuration is not the same after New expected:\n %#v \ngot:\n %#v", expected, has)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerConfiguration is independent so make a small test for that
|
||||||
|
func TestConfigServerOptions(t *testing.T) {
|
||||||
|
expected := DefaultServerConfiguration()
|
||||||
|
expected.ListeningAddr = "mydomain.com:80"
|
||||||
|
expected.RedirectTo = "https://mydomain.com:443"
|
||||||
|
expected.Virtual = true
|
||||||
|
|
||||||
|
c := ServerConfiguration{ListeningAddr: expected.ListeningAddr, RedirectTo: expected.RedirectTo, Virtual: expected.Virtual}
|
||||||
|
// static config test
|
||||||
|
s := newServer(c)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(s.Config, expected) {
|
||||||
|
t.Fatalf("Static Server Configuration not equal after newServer, expected:\n%#v \nwhile got:\n%#v", expected, s.Config)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
56
context.go
56
context.go
|
@ -10,6 +10,13 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/iris-contrib/formBinder"
|
||||||
|
"github.com/kataras/go-errors"
|
||||||
|
"github.com/kataras/go-fs"
|
||||||
|
"github.com/kataras/go-sessions"
|
||||||
|
"github.com/kataras/iris/context"
|
||||||
|
"github.com/kataras/iris/utils"
|
||||||
|
"github.com/valyala/fasthttp"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
@ -18,18 +25,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/iris-contrib/formBinder"
|
|
||||||
"github.com/kataras/go-errors"
|
|
||||||
"github.com/kataras/go-fs"
|
|
||||||
"github.com/kataras/go-sessions"
|
|
||||||
"github.com/kataras/iris/config"
|
|
||||||
"github.com/kataras/iris/context"
|
|
||||||
"github.com/kataras/iris/utils"
|
|
||||||
"github.com/klauspost/compress/gzip"
|
|
||||||
"github.com/valyala/fasthttp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -80,9 +76,6 @@ const (
|
||||||
cookieHeaderIDLen = len(cookieHeaderID)
|
cookieHeaderIDLen = len(cookieHeaderID)
|
||||||
)
|
)
|
||||||
|
|
||||||
// this pool is used everywhere needed in the iris for example inside party-> Static
|
|
||||||
var gzipWriterPool = sync.Pool{New: func() interface{} { return &gzip.Writer{} }}
|
|
||||||
|
|
||||||
// errors
|
// errors
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -541,14 +534,29 @@ func (ctx *Context) Gzip(b []byte, status int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RenderTemplateSource serves a template source(raw string contents) from the first template engines which supports raw parsing returns its result as string
|
||||||
|
func (ctx *Context) RenderTemplateSource(status int, src string, binding interface{}, options ...map[string]interface{}) error {
|
||||||
|
err := ctx.framework.templates.renderSource(ctx, src, binding, options...)
|
||||||
|
if err == nil {
|
||||||
|
ctx.SetStatusCode(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// RenderWithStatus builds up the response from the specified template or a response engine.
|
// RenderWithStatus builds up the response from the specified template or a response engine.
|
||||||
// Note: the options: "gzip" and "charset" are built'n support by Iris, so you can pass these on any template engine or response engine
|
// Note: the options: "gzip" and "charset" are built'n support by Iris, so you can pass these on any template engine or response engine
|
||||||
func (ctx *Context) RenderWithStatus(status int, name string, binding interface{}, options ...map[string]interface{}) error {
|
func (ctx *Context) RenderWithStatus(status int, name string, binding interface{}, options ...map[string]interface{}) (err error) {
|
||||||
ctx.SetStatusCode(status)
|
|
||||||
if strings.IndexByte(name, '.') > -1 { //we have template
|
if strings.IndexByte(name, '.') > -1 { //we have template
|
||||||
return ctx.framework.templates.render(ctx, name, binding, options...)
|
err = ctx.framework.templates.render(ctx, name, binding, options...)
|
||||||
}
|
}
|
||||||
return ctx.framework.responses.getBy(name).render(ctx, binding, options...)
|
err = ctx.framework.responses.getBy(name).render(ctx, binding, options...)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
ctx.SetStatusCode(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render same as .RenderWithStatus but with status to iris.StatusOK (200) if no previous status exists
|
// Render same as .RenderWithStatus but with status to iris.StatusOK (200) if no previous status exists
|
||||||
|
@ -634,7 +642,7 @@ func (ctx *Context) Markdown(status int, markdown string) {
|
||||||
// You can define your own "Content-Type" header also, after this function call
|
// You can define your own "Content-Type" header also, after this function call
|
||||||
// Doesn't implements resuming (by range), use ctx.SendFile instead
|
// Doesn't implements resuming (by range), use ctx.SendFile instead
|
||||||
func (ctx *Context) ServeContent(content io.ReadSeeker, filename string, modtime time.Time, gzipCompression bool) error {
|
func (ctx *Context) ServeContent(content io.ReadSeeker, filename string, modtime time.Time, gzipCompression bool) error {
|
||||||
if t, err := time.Parse(config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && modtime.Before(t.Add(1*time.Second)) {
|
if t, err := time.Parse(ctx.framework.Config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && modtime.Before(t.Add(1*time.Second)) {
|
||||||
ctx.RequestCtx.Response.Header.Del(contentType)
|
ctx.RequestCtx.Response.Header.Del(contentType)
|
||||||
ctx.RequestCtx.Response.Header.Del(contentLength)
|
ctx.RequestCtx.Response.Header.Del(contentLength)
|
||||||
ctx.RequestCtx.SetStatusCode(StatusNotModified)
|
ctx.RequestCtx.SetStatusCode(StatusNotModified)
|
||||||
|
@ -642,20 +650,18 @@ func (ctx *Context) ServeContent(content io.ReadSeeker, filename string, modtime
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.RequestCtx.Response.Header.Set(contentType, fs.TypeByExtension(filename))
|
ctx.RequestCtx.Response.Header.Set(contentType, fs.TypeByExtension(filename))
|
||||||
ctx.RequestCtx.Response.Header.Set(lastModified, modtime.UTC().Format(config.TimeFormat))
|
ctx.RequestCtx.Response.Header.Set(lastModified, modtime.UTC().Format(ctx.framework.Config.TimeFormat))
|
||||||
ctx.RequestCtx.SetStatusCode(StatusOK)
|
ctx.RequestCtx.SetStatusCode(StatusOK)
|
||||||
var out io.Writer
|
var out io.Writer
|
||||||
if gzipCompression && ctx.clientAllowsGzip() {
|
if gzipCompression && ctx.clientAllowsGzip() {
|
||||||
ctx.RequestCtx.Response.Header.Add(varyHeader, acceptEncodingHeader)
|
ctx.RequestCtx.Response.Header.Add(varyHeader, acceptEncodingHeader)
|
||||||
ctx.SetHeader(contentEncodingHeader, "gzip")
|
ctx.SetHeader(contentEncodingHeader, "gzip")
|
||||||
gzipWriter := gzipWriterPool.Get().(*gzip.Writer)
|
|
||||||
gzipWriter.Reset(ctx.RequestCtx.Response.BodyWriter())
|
gzipWriter := fs.AcquireGzipWriter(ctx.RequestCtx.Response.BodyWriter())
|
||||||
defer gzipWriter.Close()
|
defer fs.ReleaseGzipWriter(gzipWriter)
|
||||||
defer gzipWriterPool.Put(gzipWriter)
|
|
||||||
out = gzipWriter
|
out = gzipWriter
|
||||||
} else {
|
} else {
|
||||||
out = ctx.RequestCtx.Response.BodyWriter()
|
out = ctx.RequestCtx.Response.BodyWriter()
|
||||||
|
|
||||||
}
|
}
|
||||||
_, err := io.Copy(out, content)
|
_, err := io.Copy(out, content)
|
||||||
return errServeContent.With(err)
|
return errServeContent.With(err)
|
||||||
|
|
26
http.go
26
http.go
|
@ -17,7 +17,6 @@ import (
|
||||||
|
|
||||||
"github.com/iris-contrib/letsencrypt"
|
"github.com/iris-contrib/letsencrypt"
|
||||||
"github.com/kataras/go-errors"
|
"github.com/kataras/go-errors"
|
||||||
"github.com/kataras/iris/config"
|
|
||||||
"github.com/kataras/iris/utils"
|
"github.com/kataras/iris/utils"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
"github.com/valyala/fasthttp/fasthttpadaptor"
|
"github.com/valyala/fasthttp/fasthttpadaptor"
|
||||||
|
@ -244,7 +243,7 @@ type (
|
||||||
Server struct {
|
Server struct {
|
||||||
*fasthttp.Server
|
*fasthttp.Server
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
Config config.Server
|
Config ServerConfiguration
|
||||||
tls bool
|
tls bool
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
@ -256,11 +255,13 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
// newServer returns a pointer to a Server object, and set it's options if any, nothing more
|
// newServer returns a pointer to a Server object, and set it's options if any, nothing more
|
||||||
func newServer(cfg config.Server) *Server {
|
func newServer(setters ...OptionServerSettter) *Server {
|
||||||
if cfg.Name == "" {
|
c := DefaultServerConfiguration()
|
||||||
cfg.Name = config.DefaultServerName
|
for _, setter := range setters {
|
||||||
|
setter.Set(&c)
|
||||||
}
|
}
|
||||||
s := &Server{Server: &fasthttp.Server{Name: cfg.Name}, Config: cfg}
|
|
||||||
|
s := &Server{Server: &fasthttp.Server{Name: c.Name}, Config: c}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -436,6 +437,7 @@ func (s *Server) Open(h fasthttp.RequestHandler) error {
|
||||||
s.Server.WriteBufferSize = s.Config.WriteBufferSize
|
s.Server.WriteBufferSize = s.Config.WriteBufferSize
|
||||||
s.Server.ReadTimeout = s.Config.ReadTimeout
|
s.Server.ReadTimeout = s.Config.ReadTimeout
|
||||||
s.Server.WriteTimeout = s.Config.WriteTimeout
|
s.Server.WriteTimeout = s.Config.WriteTimeout
|
||||||
|
|
||||||
if s.Config.RedirectTo != "" {
|
if s.Config.RedirectTo != "" {
|
||||||
// override the handler and redirect all requests to this addr
|
// override the handler and redirect all requests to this addr
|
||||||
s.Server.Handler = func(reqCtx *fasthttp.RequestCtx) {
|
s.Server.Handler = func(reqCtx *fasthttp.RequestCtx) {
|
||||||
|
@ -454,7 +456,7 @@ func (s *Server) Open(h fasthttp.RequestHandler) error {
|
||||||
return s.listenUNIX()
|
return s.listenUNIX()
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Config.ListeningAddr = config.ServerParseAddr(s.Config.ListeningAddr)
|
s.Config.ListeningAddr = ServerParseAddr(s.Config.ListeningAddr)
|
||||||
|
|
||||||
if s.Config.Virtual {
|
if s.Config.Virtual {
|
||||||
return nil
|
return nil
|
||||||
|
@ -482,8 +484,8 @@ func (s *Server) Close() (err error) {
|
||||||
|
|
||||||
// Add adds a server to the list by its config
|
// Add adds a server to the list by its config
|
||||||
// returns the new server
|
// returns the new server
|
||||||
func (s *ServerList) Add(cfg config.Server) *Server {
|
func (s *ServerList) Add(setters ...OptionServerSettter) *Server {
|
||||||
srv := newServer(cfg)
|
srv := newServer(setters...)
|
||||||
s.servers = append(s.servers, srv)
|
s.servers = append(s.servers, srv)
|
||||||
return srv
|
return srv
|
||||||
}
|
}
|
||||||
|
@ -1356,9 +1358,9 @@ func newServeMux(logger *log.Logger) *serveMux {
|
||||||
mux := &serveMux{
|
mux := &serveMux{
|
||||||
lookups: make([]*route, 0),
|
lookups: make([]*route, 0),
|
||||||
errorHandlers: make(map[int]Handler, 0),
|
errorHandlers: make(map[int]Handler, 0),
|
||||||
hostname: config.DefaultServerHostname, // these are changing when the server is up
|
hostname: DefaultServerHostname, // these are changing when the server is up
|
||||||
escapePath: !config.DefaultDisablePathEscape,
|
escapePath: !DefaultDisablePathEscape,
|
||||||
correctPath: !config.DefaultDisablePathCorrection,
|
correctPath: !DefaultDisablePathCorrection,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
http_test.go
23
http_test.go
|
@ -14,7 +14,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gavv/httpexpect"
|
"github.com/gavv/httpexpect"
|
||||||
"github.com/kataras/iris/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -72,14 +71,14 @@ func TestServerHost(t *testing.T) {
|
||||||
var server1, server2, server3 Server
|
var server1, server2, server3 Server
|
||||||
var expectedHost1 = "mydomain.com:1993"
|
var expectedHost1 = "mydomain.com:1993"
|
||||||
var expectedHost2 = "mydomain.com:80"
|
var expectedHost2 = "mydomain.com:80"
|
||||||
var expectedHost3 = config.DefaultServerHostname + ":9090"
|
var expectedHost3 = DefaultServerHostname + ":9090"
|
||||||
server1.Config.ListeningAddr = expectedHost1
|
server1.Config.ListeningAddr = expectedHost1
|
||||||
server2.Config.ListeningAddr = "mydomain.com"
|
server2.Config.ListeningAddr = "mydomain.com"
|
||||||
server3.Config.ListeningAddr = ":9090"
|
server3.Config.ListeningAddr = ":9090"
|
||||||
|
|
||||||
server1.Config.ListeningAddr = config.ServerParseAddr(server1.Config.ListeningAddr)
|
server1.Config.ListeningAddr = ServerParseAddr(server1.Config.ListeningAddr)
|
||||||
server2.Config.ListeningAddr = config.ServerParseAddr(server2.Config.ListeningAddr)
|
server2.Config.ListeningAddr = ServerParseAddr(server2.Config.ListeningAddr)
|
||||||
server3.Config.ListeningAddr = config.ServerParseAddr(server3.Config.ListeningAddr)
|
server3.Config.ListeningAddr = ServerParseAddr(server3.Config.ListeningAddr)
|
||||||
|
|
||||||
if server1.Host() != expectedHost1 {
|
if server1.Host() != expectedHost1 {
|
||||||
t.Fatalf("Expecting server 1's host to be %s but we got %s", expectedHost1, server1.Host())
|
t.Fatalf("Expecting server 1's host to be %s but we got %s", expectedHost1, server1.Host())
|
||||||
|
@ -96,7 +95,7 @@ func TestServerHostname(t *testing.T) {
|
||||||
var server Server
|
var server Server
|
||||||
var expectedHostname = "mydomain.com"
|
var expectedHostname = "mydomain.com"
|
||||||
server.Config.ListeningAddr = expectedHostname + ":1993"
|
server.Config.ListeningAddr = expectedHostname + ":1993"
|
||||||
server.Config.ListeningAddr = config.ServerParseAddr(server.Config.ListeningAddr)
|
server.Config.ListeningAddr = ServerParseAddr(server.Config.ListeningAddr)
|
||||||
if server.Hostname() != expectedHostname {
|
if server.Hostname() != expectedHostname {
|
||||||
t.Fatalf("Expecting server's hostname to be %s but we got %s", expectedHostname, server.Hostname())
|
t.Fatalf("Expecting server's hostname to be %s but we got %s", expectedHostname, server.Hostname())
|
||||||
}
|
}
|
||||||
|
@ -126,8 +125,8 @@ func TestServerPort(t *testing.T) {
|
||||||
expectedPort2 := 80
|
expectedPort2 := 80
|
||||||
server1.Config.ListeningAddr = "mydomain.com:8080"
|
server1.Config.ListeningAddr = "mydomain.com:8080"
|
||||||
server2.Config.ListeningAddr = "mydomain.com"
|
server2.Config.ListeningAddr = "mydomain.com"
|
||||||
server1.Config.ListeningAddr = config.ServerParseAddr(server1.Config.ListeningAddr)
|
server1.Config.ListeningAddr = ServerParseAddr(server1.Config.ListeningAddr)
|
||||||
server2.Config.ListeningAddr = config.ServerParseAddr(server2.Config.ListeningAddr)
|
server2.Config.ListeningAddr = ServerParseAddr(server2.Config.ListeningAddr)
|
||||||
|
|
||||||
if server1.Port() != expectedPort1 {
|
if server1.Port() != expectedPort1 {
|
||||||
t.Fatalf("Expecting server 1's port to be %d but we got %d", expectedPort1, server1.Port())
|
t.Fatalf("Expecting server 1's port to be %d but we got %d", expectedPort1, server1.Port())
|
||||||
|
@ -172,9 +171,9 @@ func TestMultiRunningServers_v1(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// start the secondary server
|
// start the secondary server
|
||||||
AddServer(config.Server{ListeningAddr: "mydomain.com:80", RedirectTo: "https://" + host, Virtual: true})
|
AddServer(ServerConfiguration{ListeningAddr: "mydomain.com:80", RedirectTo: "https://" + host, Virtual: true})
|
||||||
// start the main server
|
// start the main server
|
||||||
go ListenTo(config.Server{ListeningAddr: host, CertFile: certFile.Name(), KeyFile: keyFile.Name(), Virtual: true})
|
go ListenTo(ServerConfiguration{ListeningAddr: host, CertFile: certFile.Name(), KeyFile: keyFile.Name(), Virtual: true})
|
||||||
// prepare test framework
|
// prepare test framework
|
||||||
if ok := <-Available; !ok {
|
if ok := <-Available; !ok {
|
||||||
t.Fatal("Unexpected error: server cannot start, please report this as bug!!")
|
t.Fatal("Unexpected error: server cannot start, please report this as bug!!")
|
||||||
|
@ -224,9 +223,9 @@ func TestMultiRunningServers_v2(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// add a secondary server
|
// add a secondary server
|
||||||
Servers.Add(config.Server{ListeningAddr: domain + ":80", RedirectTo: "https://" + host, Virtual: true})
|
Servers.Add(ServerConfiguration{ListeningAddr: domain + ":80", RedirectTo: "https://" + host, Virtual: true})
|
||||||
// add our primary/main server
|
// add our primary/main server
|
||||||
Servers.Add(config.Server{ListeningAddr: host, CertFile: certFile.Name(), KeyFile: keyFile.Name(), Virtual: true})
|
Servers.Add(ServerConfiguration{ListeningAddr: host, CertFile: certFile.Name(), KeyFile: keyFile.Name(), Virtual: true})
|
||||||
|
|
||||||
go Go()
|
go Go()
|
||||||
|
|
||||||
|
|
217
iris.go
217
iris.go
|
@ -76,7 +76,6 @@ import (
|
||||||
"github.com/kataras/go-sessions"
|
"github.com/kataras/go-sessions"
|
||||||
"github.com/kataras/go-template"
|
"github.com/kataras/go-template"
|
||||||
"github.com/kataras/go-template/html"
|
"github.com/kataras/go-template/html"
|
||||||
"github.com/kataras/iris/config"
|
|
||||||
"github.com/kataras/iris/context"
|
"github.com/kataras/iris/context"
|
||||||
"github.com/kataras/iris/utils"
|
"github.com/kataras/iris/utils"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
|
@ -84,7 +83,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Version of the iris
|
// Version of the iris
|
||||||
Version = "4.1.7"
|
Version = "4.2.0"
|
||||||
|
|
||||||
banner = ` _____ _
|
banner = ` _____ _
|
||||||
|_ _| (_)
|
|_ _| (_)
|
||||||
|
@ -97,7 +96,7 @@ const (
|
||||||
// Default entry, use it with iris.$anyPublicFunc
|
// Default entry, use it with iris.$anyPublicFunc
|
||||||
var (
|
var (
|
||||||
Default *Framework
|
Default *Framework
|
||||||
Config *config.Iris
|
Config *Configuration
|
||||||
Logger *log.Logger // if you want colors in your console then you should use this https://github.com/iris-contrib/logger instead.
|
Logger *log.Logger // if you want colors in your console then you should use this https://github.com/iris-contrib/logger instead.
|
||||||
Plugins PluginContainer
|
Plugins PluginContainer
|
||||||
Websocket *WebsocketServer
|
Websocket *WebsocketServer
|
||||||
|
@ -143,8 +142,9 @@ type (
|
||||||
FrameworkAPI interface {
|
FrameworkAPI interface {
|
||||||
MuxAPI
|
MuxAPI
|
||||||
Must(error)
|
Must(error)
|
||||||
AddServer(config.Server) *Server
|
Set(...OptionSetter)
|
||||||
ListenTo(config.Server) error
|
AddServer(...OptionServerSettter) *Server
|
||||||
|
ListenTo(...OptionServerSettter) error
|
||||||
Listen(string)
|
Listen(string)
|
||||||
ListenTLS(string, string, string)
|
ListenTLS(string, string, string)
|
||||||
ListenUNIX(string, os.FileMode)
|
ListenUNIX(string, os.FileMode)
|
||||||
|
@ -161,8 +161,9 @@ type (
|
||||||
Path(string, ...interface{}) string
|
Path(string, ...interface{}) string
|
||||||
URL(string, ...interface{}) string
|
URL(string, ...interface{}) string
|
||||||
TemplateString(string, interface{}, ...map[string]interface{}) string
|
TemplateString(string, interface{}, ...map[string]interface{}) string
|
||||||
|
TemplateSourceString(string, interface{}) string
|
||||||
ResponseString(string, interface{}, ...map[string]interface{}) string
|
ResponseString(string, interface{}, ...map[string]interface{}) string
|
||||||
Tester(t *testing.T) *httpexpect.Expect
|
Tester(*testing.T) *httpexpect.Expect
|
||||||
}
|
}
|
||||||
|
|
||||||
// Framework is our God |\| Google.Search('Greek mythology Iris')
|
// Framework is our God |\| Google.Search('Greek mythology Iris')
|
||||||
|
@ -171,7 +172,7 @@ type (
|
||||||
Framework struct {
|
Framework struct {
|
||||||
*muxAPI
|
*muxAPI
|
||||||
contextPool sync.Pool
|
contextPool sync.Pool
|
||||||
Config *config.Iris
|
Config *Configuration
|
||||||
sessions sessions.Sessions
|
sessions sessions.Sessions
|
||||||
responses *responseEngines
|
responses *responseEngines
|
||||||
templates *templateEngines
|
templates *templateEngines
|
||||||
|
@ -191,55 +192,76 @@ type (
|
||||||
|
|
||||||
var _ FrameworkAPI = &Framework{}
|
var _ FrameworkAPI = &Framework{}
|
||||||
|
|
||||||
// New creates and returns a new Iris station aka Framework.
|
// New creates and returns a new Iris instance.
|
||||||
//
|
//
|
||||||
// Receives an optional config.Iris as parameter
|
// Receives (optional) multi options, use iris.Option and your editor should show you the available options to set
|
||||||
// If empty then config.Default() is used instead
|
// all options are inside ./configuration.go
|
||||||
func New(cfg ...config.Iris) *Framework {
|
// example 1: iris.New(iris.OptionIsDevelopment(true), iris.OptionCharset("UTF-8"), irisOptionSessionsCookie("mycookieid"),iris.OptionWebsocketEndpoint("my_endpoint"))
|
||||||
c := config.Default().Merge(cfg)
|
// example 2: iris.New(iris.Configuration{IsDevelopment:true, Charset: "UTF-8", Sessions: iris.SessionsConfiguration{Cookie:"mycookieid"}, Websocket: iris.WebsocketConfiguration{Endpoint:"/my_endpoint"}})
|
||||||
|
// both ways are totally valid and equal
|
||||||
|
func New(setters ...OptionSetter) *Framework {
|
||||||
|
|
||||||
// we always use 's' no 'f' because 's' is easier for me to remember because of 'station'
|
s := &Framework{}
|
||||||
// some things never change :)
|
s.Set(setters...)
|
||||||
s := &Framework{
|
|
||||||
Config: &c,
|
// logger, plugins & ssh
|
||||||
// set the Logger
|
|
||||||
Logger: log.New(c.LoggerOut, c.LoggerPreffix, log.LstdFlags),
|
|
||||||
responses: &responseEngines{},
|
|
||||||
Available: make(chan bool),
|
|
||||||
SSH: &SSHServer{},
|
|
||||||
// set the sessions, configuration willbe updated on the initialization also, in order to give the user the opportunity to change its config at runtime.
|
|
||||||
sessions: sessions.New(sessions.Config(c.Sessions)),
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
s.contextPool.New = func() interface{} {
|
// set the Logger, which it's configuration should be declared before .Listen because the servemux and plugins needs that
|
||||||
return &Context{framework: s}
|
s.Logger = log.New(s.Config.LoggerOut, s.Config.LoggerPreffix, log.LstdFlags)
|
||||||
}
|
|
||||||
///NOTE: set all with s.Config pointer
|
|
||||||
|
|
||||||
// set the plugin container
|
|
||||||
s.Plugins = newPluginContainer(s.Logger)
|
s.Plugins = newPluginContainer(s.Logger)
|
||||||
|
s.SSH = NewSSHServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// rendering
|
||||||
|
{
|
||||||
|
s.responses = newResponseEngines()
|
||||||
// set the templates
|
// set the templates
|
||||||
s.templates = newTemplateEngines(map[string]interface{}{
|
s.templates = newTemplateEngines(map[string]interface{}{
|
||||||
"url": s.URL,
|
"url": s.URL,
|
||||||
"urlpath": s.Path,
|
"urlpath": s.Path,
|
||||||
})
|
})
|
||||||
// set the websocket server
|
}
|
||||||
s.Websocket = NewWebsocketServer(s.Config.Websocket)
|
|
||||||
|
// websocket
|
||||||
|
{
|
||||||
|
s.Websocket = NewWebsocketServer() // in order to be able to call $instance.Websocket.OnConnection
|
||||||
|
}
|
||||||
|
|
||||||
|
// routing & http server
|
||||||
|
{
|
||||||
// set the servemux, which will provide us the public API also, with its context pool
|
// set the servemux, which will provide us the public API also, with its context pool
|
||||||
mux := newServeMux(s.Logger)
|
mux := newServeMux(s.Logger)
|
||||||
mux.onLookup = s.Plugins.DoPreLookup
|
mux.onLookup = s.Plugins.DoPreLookup
|
||||||
// set the public router API (and party)
|
// set the public router API (and party)
|
||||||
s.muxAPI = &muxAPI{mux: mux, relativePath: "/"}
|
s.muxAPI = &muxAPI{mux: mux, relativePath: "/"}
|
||||||
|
s.contextPool.New = func() interface{} {
|
||||||
|
return &Context{framework: s}
|
||||||
|
}
|
||||||
s.Servers = &ServerList{mux: mux, servers: make([]*Server, 0)}
|
s.Servers = &ServerList{mux: mux, servers: make([]*Server, 0)}
|
||||||
|
s.Available = make(chan bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Framework) initialize() {
|
// Set sets an option aka configuration field to the default iris instance
|
||||||
|
func Set(setters ...OptionSetter) {
|
||||||
|
Default.Set(setters...)
|
||||||
|
}
|
||||||
|
|
||||||
s.sessions.UpdateConfig(sessions.Config(s.Config.Sessions))
|
// Set sets an option aka configuration field to this iris instance
|
||||||
|
func (s *Framework) Set(setters ...OptionSetter) {
|
||||||
|
if s.Config == nil {
|
||||||
|
defaultConfiguration := DefaultConfiguration()
|
||||||
|
s.Config = &defaultConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, setter := range setters {
|
||||||
|
setter.Set(s.Config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Framework) initialize() {
|
||||||
// prepare the response engines, if no response engines setted for the default content-types
|
// prepare the response engines, if no response engines setted for the default content-types
|
||||||
// then add them
|
// then add them
|
||||||
|
|
||||||
|
@ -276,8 +298,13 @@ func (s *Framework) initialize() {
|
||||||
s.Logger.Panic(err) // panic on templates loading before listening if we have an error.
|
s.Logger.Panic(err) // panic on templates loading before listening if we have an error.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// listen to websocket connections
|
// set the sessions
|
||||||
RegisterWebsocketServer(s, s.Websocket, s.Logger)
|
s.sessions = sessions.New(sessions.Config(s.Config.Sessions))
|
||||||
|
|
||||||
|
if s.Config.Websocket.Endpoint != "" {
|
||||||
|
// register the websocket server and listen to websocket connections when/if $instance.Websocket.OnConnection called by the dev
|
||||||
|
s.Websocket.RegisterTo(s, s.Config.Websocket)
|
||||||
|
}
|
||||||
|
|
||||||
// prepare the mux & the server
|
// prepare the mux & the server
|
||||||
s.mux.setCorrectPath(!s.Config.DisablePathCorrection)
|
s.mux.setCorrectPath(!s.Config.DisablePathCorrection)
|
||||||
|
@ -328,7 +355,7 @@ func (s *Framework) Go() error {
|
||||||
hosts[i] = srv.Host()
|
hosts[i] = srv.Host()
|
||||||
}
|
}
|
||||||
|
|
||||||
bannerMessage := fmt.Sprintf("%s: Running at %s", time.Now().Format(config.TimeFormat), strings.Join(hosts, ", "))
|
bannerMessage := fmt.Sprintf("%s: Running at %s", time.Now().Format(s.Config.TimeFormat), strings.Join(hosts, ", "))
|
||||||
// we don't print it via Logger because:
|
// we don't print it via Logger because:
|
||||||
// 1. The banner is only 'useful' when the developer logs to terminal and no file
|
// 1. The banner is only 'useful' when the developer logs to terminal and no file
|
||||||
// 2. Prefix & LstdFlags options of the default s.Logger
|
// 2. Prefix & LstdFlags options of the default s.Logger
|
||||||
|
@ -358,68 +385,60 @@ func (s *Framework) Must(err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddServer same as .Servers.Add(config.Server)
|
// AddServer same as .Servers.Add(ServerConfiguration)
|
||||||
//
|
//
|
||||||
// AddServer starts a server which listens to this station
|
// AddServer starts a server which listens to this station
|
||||||
// Note that the view engine's functions {{ url }} and {{ urlpath }} will return the first's registered server's scheme (http/https)
|
// Note that the view engine's functions {{ url }} and {{ urlpath }} will return the first's registered server's scheme (http/https)
|
||||||
//
|
//
|
||||||
// this is useful mostly when you want to have two or more listening ports ( two or more servers ) for the same station
|
// this is useful mostly when you want to have two or more listening ports ( two or more servers ) for the same station
|
||||||
//
|
//
|
||||||
// receives one parameter which is the config.Server for the new server
|
// receives one parameter which is the ServerConfiguration for the new server
|
||||||
// returns the new standalone server( you can close this server by the returning reference)
|
// returns the new standalone server( you can close this server by the returning reference)
|
||||||
//
|
//
|
||||||
// If you need only one server you can use the blocking-funcs: .Listen/ListenTLS/ListenUNIX/ListenTo
|
// If you need only one server you can use the blocking-funcs: .Listen/ListenTLS/ListenUNIX/ListenTo
|
||||||
//
|
//
|
||||||
// this is a NOT A BLOCKING version, the main .Listen/ListenTLS/ListenUNIX/ListenTo should be always executed LAST, so this function goes before the main .Listen/ListenTLS/ListenUNIX/ListenTo
|
// this is a NOT A BLOCKING version, the main .Listen/ListenTLS/ListenUNIX/ListenTo should be always executed LAST, so this function goes before the main .Listen/ListenTLS/ListenUNIX/ListenTo
|
||||||
func AddServer(cfg config.Server) *Server {
|
func AddServer(setters ...OptionServerSettter) *Server {
|
||||||
return Default.AddServer(cfg)
|
return Default.AddServer(setters...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddServer same as .Servers.Add(config.Server)
|
// AddServer same as .Servers.Add(ServerConfiguration)
|
||||||
//
|
//
|
||||||
// AddServer starts a server which listens to this station
|
// AddServer starts a server which listens to this station
|
||||||
// Note that the view engine's functions {{ url }} and {{ urlpath }} will return the last registered server's scheme (http/https)
|
// Note that the view engine's functions {{ url }} and {{ urlpath }} will return the last registered server's scheme (http/https)
|
||||||
//
|
//
|
||||||
// this is useful mostly when you want to have two or more listening ports ( two or more servers ) for the same station
|
// this is useful mostly when you want to have two or more listening ports ( two or more servers ) for the same station
|
||||||
//
|
//
|
||||||
// receives one parameter which is the config.Server for the new server
|
// receives one parameter which is the ServerConfiguration for the new server
|
||||||
// returns the new standalone server( you can close this server by the returning reference)
|
// returns the new standalone server( you can close this server by the returning reference)
|
||||||
//
|
//
|
||||||
// If you need only one server you can use the blocking-funcs: .Listen/ListenTLS/ListenUNIX/ListenTo
|
// If you need only one server you can use the blocking-funcs: .Listen/ListenTLS/ListenUNIX/ListenTo
|
||||||
//
|
//
|
||||||
// this is a NOT A BLOCKING version, the main .Listen/ListenTLS/ListenUNIX/ListenTo should be always executed LAST, so this function goes before the main .Listen/ListenTLS/ListenUNIX/ListenTo
|
// this is a NOT A BLOCKING version, the main .Listen/ListenTLS/ListenUNIX/ListenTo should be always executed LAST, so this function goes before the main .Listen/ListenTLS/ListenUNIX/ListenTo
|
||||||
func (s *Framework) AddServer(cfg config.Server) *Server {
|
func (s *Framework) AddServer(setters ...OptionServerSettter) *Server {
|
||||||
return s.Servers.Add(cfg)
|
return s.Servers.Add(setters...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenTo listens to a server but accepts the full server's configuration
|
// ListenTo listens to a server but accepts the full server's configuration
|
||||||
// returns an error, you're responsible to handle that
|
// returns an error, you're responsible to handle that
|
||||||
// or use the iris.Must(iris.ListenTo(config.Server{}))
|
// ex: ris.ListenTo(iris.ServerConfiguration{ListeningAddr:":8080"})
|
||||||
|
// ex2: err := iris.ListenTo(iris.OptionServerListeningAddr(":8080"))
|
||||||
|
// or use the iris.Must(iris.ListenTo(iris.ServerConfiguration{ListeningAddr:":8080"}))
|
||||||
//
|
//
|
||||||
// it's a blocking func
|
// it's a blocking func
|
||||||
func ListenTo(cfg config.Server) error {
|
func ListenTo(setters ...OptionServerSettter) error {
|
||||||
return Default.ListenTo(cfg)
|
return Default.ListenTo(setters...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenTo listens to a server but acceots the full server's configuration
|
// ListenTo listens to a server but acceots the full server's configuration
|
||||||
// returns an error, you're responsible to handle that
|
// returns an error, you're responsible to handle that
|
||||||
// or use the iris.Must(iris.ListenTo(config.Server{}))
|
// ex: ris.ListenTo(iris.ServerConfiguration{ListeningAddr:":8080"})
|
||||||
|
// ex2: err := iris.ListenTo(iris.OptionServerListeningAddr(":8080"))
|
||||||
|
// or use the iris.Must(iris.ListenTo(iris.ServerConfiguration{ListeningAddr:":8080"}))
|
||||||
//
|
//
|
||||||
// it's a blocking func
|
// it's a blocking func
|
||||||
func (s *Framework) ListenTo(cfg config.Server) (err error) {
|
func (s *Framework) ListenTo(setters ...OptionServerSettter) (err error) {
|
||||||
if cfg.ReadBufferSize == 0 {
|
s.Servers.Add(setters...)
|
||||||
cfg.ReadBufferSize = config.DefaultReadBufferSize
|
|
||||||
}
|
|
||||||
if cfg.WriteBufferSize == 0 {
|
|
||||||
cfg.WriteBufferSize = config.DefaultWriteBufferSize
|
|
||||||
}
|
|
||||||
if cfg.MaxRequestBodySize == 0 {
|
|
||||||
cfg.MaxRequestBodySize = config.DefaultMaxRequestBodySize
|
|
||||||
}
|
|
||||||
if cfg.ListeningAddr == "" {
|
|
||||||
cfg.ListeningAddr = config.DefaultServerAddr
|
|
||||||
}
|
|
||||||
s.Servers.Add(cfg)
|
|
||||||
return s.Go()
|
return s.Go()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,7 +447,6 @@ func (s *Framework) ListenTo(cfg config.Server) (err error) {
|
||||||
// host:port
|
// host:port
|
||||||
//
|
//
|
||||||
// It panics on error if you need a func to return an error, use the ListenTo
|
// It panics on error if you need a func to return an error, use the ListenTo
|
||||||
// ex: err := iris.ListenTo(config.Server{ListeningAddr:":8080"})
|
|
||||||
func Listen(addr string) {
|
func Listen(addr string) {
|
||||||
Default.Listen(addr)
|
Default.Listen(addr)
|
||||||
}
|
}
|
||||||
|
@ -438,9 +456,8 @@ func Listen(addr string) {
|
||||||
// host:port
|
// host:port
|
||||||
//
|
//
|
||||||
// It panics on error if you need a func to return an error, use the ListenTo
|
// It panics on error if you need a func to return an error, use the ListenTo
|
||||||
// ex: err := iris.ListenTo(config.Server{ListeningAddr:":8080"})
|
|
||||||
func (s *Framework) Listen(addr string) {
|
func (s *Framework) Listen(addr string) {
|
||||||
s.Must(s.ListenTo(config.Server{ListeningAddr: addr}))
|
s.Must(s.ListenTo(ServerConfiguration{ListeningAddr: addr}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenTLS Starts a https server with certificates,
|
// ListenTLS Starts a https server with certificates,
|
||||||
|
@ -450,7 +467,7 @@ func (s *Framework) Listen(addr string) {
|
||||||
// host:port
|
// host:port
|
||||||
//
|
//
|
||||||
// It panics on error if you need a func to return an error, use the ListenTo
|
// It panics on error if you need a func to return an error, use the ListenTo
|
||||||
// ex: err := iris.ListenTo(":8080","yourfile.cert","yourfile.key")
|
// ex: iris.ListenTLS(":8080","yourfile.cert","yourfile.key")
|
||||||
func ListenTLS(addr string, certFile string, keyFile string) {
|
func ListenTLS(addr string, certFile string, keyFile string) {
|
||||||
Default.ListenTLS(addr, certFile, keyFile)
|
Default.ListenTLS(addr, certFile, keyFile)
|
||||||
}
|
}
|
||||||
|
@ -461,7 +478,7 @@ func ListenTLS(addr string, certFile string, keyFile string) {
|
||||||
//
|
//
|
||||||
// Notes:
|
// Notes:
|
||||||
// if you don't want the last feature you should use this method:
|
// if you don't want the last feature you should use this method:
|
||||||
// iris.ListenTo(config.Server{ListeningAddr: "mydomain.com:443", AutoTLS: true})
|
// iris.ListenTo(iris.ServerConfiguration{ListeningAddr: "mydomain.com:443", AutoTLS: true})
|
||||||
// it's a blocking function
|
// it's a blocking function
|
||||||
// Limit : https://github.com/iris-contrib/letsencrypt/blob/master/lets.go#L142
|
// Limit : https://github.com/iris-contrib/letsencrypt/blob/master/lets.go#L142
|
||||||
//
|
//
|
||||||
|
@ -477,12 +494,12 @@ func ListenTLSAuto(addr string) {
|
||||||
// host:port
|
// host:port
|
||||||
//
|
//
|
||||||
// It panics on error if you need a func to return an error, use the ListenTo
|
// It panics on error if you need a func to return an error, use the ListenTo
|
||||||
// ex: err := iris.ListenTo(":8080","yourfile.cert","yourfile.key")
|
// ex: iris.ListenTLS(":8080","yourfile.cert","yourfile.key")
|
||||||
func (s *Framework) ListenTLS(addr string, certFile, keyFile string) {
|
func (s *Framework) ListenTLS(addr string, certFile, keyFile string) {
|
||||||
if certFile == "" || keyFile == "" {
|
if certFile == "" || keyFile == "" {
|
||||||
s.Logger.Panic("You should provide certFile and keyFile for TLS/SSL")
|
s.Logger.Panic("You should provide certFile and keyFile for TLS/SSL")
|
||||||
}
|
}
|
||||||
s.Must(s.ListenTo(config.Server{ListeningAddr: addr, CertFile: certFile, KeyFile: keyFile}))
|
s.Must(s.ListenTo(ServerConfiguration{ListeningAddr: addr, CertFile: certFile, KeyFile: keyFile}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenTLSAuto starts a server listening at the specific nat address
|
// ListenTLSAuto starts a server listening at the specific nat address
|
||||||
|
@ -491,7 +508,7 @@ func (s *Framework) ListenTLS(addr string, certFile, keyFile string) {
|
||||||
//
|
//
|
||||||
// Notes:
|
// Notes:
|
||||||
// if you don't want the last feature you should use this method:
|
// if you don't want the last feature you should use this method:
|
||||||
// iris.ListenTo(config.Server{ListeningAddr: "mydomain.com:443", AutoTLS: true})
|
// iris.ListenTo(iris.ServerConfiguration{ListeningAddr: "mydomain.com:443", AutoTLS: true})
|
||||||
// it's a blocking function
|
// it's a blocking function
|
||||||
// Limit : https://github.com/iris-contrib/letsencrypt/blob/master/lets.go#L142
|
// Limit : https://github.com/iris-contrib/letsencrypt/blob/master/lets.go#L142
|
||||||
//
|
//
|
||||||
|
@ -500,18 +517,18 @@ func (s *Framework) ListenTLSAuto(addr string) {
|
||||||
if portIdx := strings.IndexByte(addr, ':'); portIdx == -1 {
|
if portIdx := strings.IndexByte(addr, ':'); portIdx == -1 {
|
||||||
addr += ":443"
|
addr += ":443"
|
||||||
}
|
}
|
||||||
addr = config.ServerParseAddr(addr)
|
addr = ServerParseAddr(addr)
|
||||||
|
|
||||||
// start a secondary server (HTTP) on port 80, this is a non-blocking func
|
// start a secondary server (HTTP) on port 80, this is a non-blocking func
|
||||||
// redirects all http to the main server which is tls/ssl on port :443
|
// redirects all http to the main server which is tls/ssl on port :443
|
||||||
s.AddServer(config.Server{ListeningAddr: ":80", RedirectTo: "https://" + addr})
|
s.AddServer(ServerConfiguration{ListeningAddr: ":80", RedirectTo: "https://" + addr})
|
||||||
s.Must(s.ListenTo(config.Server{ListeningAddr: addr, AutoTLS: true}))
|
s.Must(s.ListenTo(ServerConfiguration{ListeningAddr: addr, AutoTLS: true}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenUNIX starts the process of listening to the new requests using a 'socket file', this works only on unix
|
// ListenUNIX starts the process of listening to the new requests using a 'socket file', this works only on unix
|
||||||
//
|
//
|
||||||
// It panics on error if you need a func to return an error, use the ListenTo
|
// It panics on error if you need a func to return an error, use the ListenTo
|
||||||
// ex: err := iris.ListenTo(":8080", Mode: os.FileMode)
|
// ex: iris.ListenUNIX(":8080", Mode: os.FileMode)
|
||||||
func ListenUNIX(addr string, mode os.FileMode) {
|
func ListenUNIX(addr string, mode os.FileMode) {
|
||||||
Default.ListenUNIX(addr, mode)
|
Default.ListenUNIX(addr, mode)
|
||||||
}
|
}
|
||||||
|
@ -519,9 +536,9 @@ func ListenUNIX(addr string, mode os.FileMode) {
|
||||||
// ListenUNIX starts the process of listening to the new requests using a 'socket file', this works only on unix
|
// ListenUNIX starts the process of listening to the new requests using a 'socket file', this works only on unix
|
||||||
//
|
//
|
||||||
// It panics on error if you need a func to return an error, use the ListenTo
|
// It panics on error if you need a func to return an error, use the ListenTo
|
||||||
// ex: err := iris.ListenTo(":8080", Mode: os.FileMode)
|
// ex: ris.ListenUNIX(":8080", Mode: os.FileMode)
|
||||||
func (s *Framework) ListenUNIX(addr string, mode os.FileMode) {
|
func (s *Framework) ListenUNIX(addr string, mode os.FileMode) {
|
||||||
s.Must(ListenTo(config.Server{ListeningAddr: addr, Mode: mode}))
|
s.Must(ListenTo(ServerConfiguration{ListeningAddr: addr, Mode: mode}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListenVirtual is useful only when you want to test Iris, it doesn't starts the server but it configures and returns it
|
// ListenVirtual is useful only when you want to test Iris, it doesn't starts the server but it configures and returns it
|
||||||
|
@ -536,7 +553,7 @@ func ListenVirtual(optionalAddr ...string) *Server {
|
||||||
// it is not blocking the app
|
// it is not blocking the app
|
||||||
func (s *Framework) ListenVirtual(optionalAddr ...string) *Server {
|
func (s *Framework) ListenVirtual(optionalAddr ...string) *Server {
|
||||||
s.Config.DisableBanner = true
|
s.Config.DisableBanner = true
|
||||||
cfg := config.DefaultServer()
|
cfg := DefaultServerConfiguration()
|
||||||
|
|
||||||
if len(optionalAddr) > 0 && optionalAddr[0] != "" {
|
if len(optionalAddr) > 0 && optionalAddr[0] != "" {
|
||||||
cfg.ListeningAddr = optionalAddr[0]
|
cfg.ListeningAddr = optionalAddr[0]
|
||||||
|
@ -901,6 +918,27 @@ func (s *Framework) TemplateString(templateFile string, pageContext interface{},
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TemplateSourceString executes a template source(raw string contents) from the first template engines which supports raw parsing returns its result as string,
|
||||||
|
// useful when you want it for sending rich e-mails
|
||||||
|
// returns empty string on error
|
||||||
|
func TemplateSourceString(src string, pageContext interface{}) string {
|
||||||
|
return Default.TemplateSourceString(src, pageContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TemplateSourceString executes a template source(raw string contents) from the first template engines which supports raw parsing returns its result as string,
|
||||||
|
// useful when you want it for sending rich e-mails
|
||||||
|
// returns empty string on error
|
||||||
|
func (s *Framework) TemplateSourceString(src string, pageContext interface{}) string {
|
||||||
|
if s.Config.DisableTemplateEngines {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
res, err := s.templates.ExecuteRawString(src, pageContext)
|
||||||
|
if err != nil {
|
||||||
|
res = ""
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
// ResponseString returns the string of a response engine,
|
// ResponseString returns the string of a response engine,
|
||||||
// does not render it to the client
|
// does not render it to the client
|
||||||
// returns empty string on error
|
// returns empty string on error
|
||||||
|
@ -1527,8 +1565,8 @@ func (api *muxAPI) StaticHandler(systemPath string, stripSlashes int, compress b
|
||||||
|
|
||||||
// Enable transparent compression to save network traffic.
|
// Enable transparent compression to save network traffic.
|
||||||
Compress: compress,
|
Compress: compress,
|
||||||
CacheDuration: config.StaticCacheDuration,
|
CacheDuration: StaticCacheDuration,
|
||||||
CompressedFileSuffix: config.CompressedFileSuffix,
|
CompressedFileSuffix: CompressedFileSuffix,
|
||||||
}
|
}
|
||||||
|
|
||||||
if stripSlashes > 0 {
|
if stripSlashes > 0 {
|
||||||
|
@ -1713,9 +1751,13 @@ func StaticContent(reqPath string, contentType string, content []byte) RouteName
|
||||||
// a good example of this is how the websocket server uses that to auto-register the /iris-ws.js
|
// a good example of this is how the websocket server uses that to auto-register the /iris-ws.js
|
||||||
func (api *muxAPI) StaticContent(reqPath string, cType string, content []byte) RouteNameFunc { // func(string) because we use that on websockets
|
func (api *muxAPI) StaticContent(reqPath string, cType string, content []byte) RouteNameFunc { // func(string) because we use that on websockets
|
||||||
modtime := time.Now()
|
modtime := time.Now()
|
||||||
modtimeStr := modtime.UTC().Format(config.TimeFormat)
|
modtimeStr := ""
|
||||||
h := func(ctx *Context) {
|
h := func(ctx *Context) {
|
||||||
if t, err := time.Parse(config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && modtime.Before(t.Add(config.StaticCacheDuration)) {
|
if modtimeStr == "" {
|
||||||
|
modtimeStr = modtime.UTC().Format(ctx.framework.Config.TimeFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, err := time.Parse(ctx.framework.Config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && modtime.Before(t.Add(StaticCacheDuration)) {
|
||||||
ctx.Response.Header.Del(contentType)
|
ctx.Response.Header.Del(contentType)
|
||||||
ctx.Response.Header.Del(contentLength)
|
ctx.Response.Header.Del(contentLength)
|
||||||
ctx.SetStatusCode(StatusNotModified)
|
ctx.SetStatusCode(StatusNotModified)
|
||||||
|
@ -1772,16 +1814,19 @@ func (api *muxAPI) Favicon(favPath string, requestPath ...string) RouteNameFunc
|
||||||
favPath = fav
|
favPath = fav
|
||||||
fi, _ = f.Stat()
|
fi, _ = f.Stat()
|
||||||
}
|
}
|
||||||
modtime := fi.ModTime().UTC().Format(config.TimeFormat)
|
|
||||||
cType := fs.TypeByExtension(favPath)
|
cType := fs.TypeByExtension(favPath)
|
||||||
// copy the bytes here in order to cache and not read the ico on each request.
|
// copy the bytes here in order to cache and not read the ico on each request.
|
||||||
cacheFav := make([]byte, fi.Size())
|
cacheFav := make([]byte, fi.Size())
|
||||||
if _, err = f.Read(cacheFav); err != nil {
|
if _, err = f.Read(cacheFav); err != nil {
|
||||||
panic(errDirectoryFileNotFound.Format(favPath, "Couldn't read the data bytes for Favicon: "+err.Error()))
|
panic(errDirectoryFileNotFound.Format(favPath, "Couldn't read the data bytes for Favicon: "+err.Error()))
|
||||||
}
|
}
|
||||||
|
modtime := ""
|
||||||
h := func(ctx *Context) {
|
h := func(ctx *Context) {
|
||||||
if t, err := time.Parse(config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && fi.ModTime().Before(t.Add(config.StaticCacheDuration)) {
|
if modtime == "" {
|
||||||
|
modtime = fi.ModTime().UTC().Format(ctx.framework.Config.TimeFormat)
|
||||||
|
}
|
||||||
|
if t, err := time.Parse(ctx.framework.Config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && fi.ModTime().Before(t.Add(StaticCacheDuration)) {
|
||||||
ctx.Response.Header.Del(contentType)
|
ctx.Response.Header.Del(contentType)
|
||||||
ctx.Response.Header.Del(contentLength)
|
ctx.Response.Header.Del(contentLength)
|
||||||
ctx.SetStatusCode(StatusNotModified)
|
ctx.SetStatusCode(StatusNotModified)
|
||||||
|
|
|
@ -184,6 +184,10 @@ type responseEngines struct {
|
||||||
engines []*responseEngineMap
|
engines []*responseEngineMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newResponseEngines() *responseEngines {
|
||||||
|
return &responseEngines{}
|
||||||
|
}
|
||||||
|
|
||||||
// add accepts a simple response engine with its content type or key, key should not contains a dot('.').
|
// add accepts a simple response engine with its content type or key, key should not contains a dot('.').
|
||||||
// if key is a content type then it's the content type, but if it not, set the content type from the returned function,
|
// if key is a content type then it's the content type, but if it not, set the content type from the returned function,
|
||||||
// if it not called/changed then the default content type text/plain will be used.
|
// if it not called/changed then the default content type text/plain will be used.
|
||||||
|
|
5
ssh.go
5
ssh.go
|
@ -428,6 +428,11 @@ type SSHServer struct {
|
||||||
Logger *log.Logger // log.New(...)/ $qinstance.Logger, fill it when you want to receive debug and info/warnings messages
|
Logger *log.Logger // log.New(...)/ $qinstance.Logger, fill it when you want to receive debug and info/warnings messages
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSSHServer returns a new empty SSHServer
|
||||||
|
func NewSSHServer() *SSHServer {
|
||||||
|
return &SSHServer{}
|
||||||
|
}
|
||||||
|
|
||||||
// Enabled returns true if SSH can be started, if Host != ""
|
// Enabled returns true if SSH can be started, if Host != ""
|
||||||
func (s *SSHServer) Enabled() bool {
|
func (s *SSHServer) Enabled() bool {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
|
|
28
template.go
28
template.go
|
@ -73,3 +73,31 @@ func (t *templateEngines) render(ctx *Context, filename string, binding interfac
|
||||||
err = t.ExecuteWriter(out, filename, binding, options...)
|
err = t.ExecuteWriter(out, filename, binding, options...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// renderSource executes a template source raw contents (string) and write its result to the context's body
|
||||||
|
// note that gzip option is an iris dynamic option which exists for all template engines
|
||||||
|
// the gzip and charset options are built'n with iris
|
||||||
|
func (t *templateEngines) renderSource(ctx *Context, src string, binding interface{}, options ...map[string]interface{}) (err error) {
|
||||||
|
// we do all these because we don't want to initialize a new map for each execution...
|
||||||
|
gzipEnabled := ctx.framework.Config.Gzip
|
||||||
|
charset := ctx.framework.Config.Charset
|
||||||
|
if len(options) > 0 {
|
||||||
|
gzipEnabled = template.GetGzipOption(gzipEnabled, options[0])
|
||||||
|
charset = template.GetCharsetOption(charset, options[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.SetContentType(contentHTML + "; charset=" + charset)
|
||||||
|
|
||||||
|
var out io.Writer
|
||||||
|
if gzipEnabled && ctx.clientAllowsGzip() {
|
||||||
|
ctx.RequestCtx.Response.Header.Add(varyHeader, acceptEncodingHeader)
|
||||||
|
ctx.SetHeader(contentEncodingHeader, "gzip")
|
||||||
|
|
||||||
|
gzipWriter := fs.AcquireGzipWriter(ctx.Response.BodyWriter())
|
||||||
|
defer fs.ReleaseGzipWriter(gzipWriter)
|
||||||
|
out = gzipWriter
|
||||||
|
} else {
|
||||||
|
out = ctx.Response.BodyWriter()
|
||||||
|
}
|
||||||
|
return t.ExecuteRaw(src, out, binding)
|
||||||
|
}
|
||||||
|
|
135
websocket.go
135
websocket.go
|
@ -1,86 +1,10 @@
|
||||||
package iris
|
package iris
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
|
|
||||||
irisWebsocket "github.com/iris-contrib/websocket"
|
irisWebsocket "github.com/iris-contrib/websocket"
|
||||||
"github.com/kataras/go-websocket"
|
"github.com/kataras/go-websocket"
|
||||||
"github.com/kataras/iris/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------
|
|
||||||
// ---------------------------------------------------------------------------------------------------------
|
|
||||||
// --------------------------------Websocket implementation-------------------------------------------------
|
|
||||||
// Global functions in order to be able to use unlimitted number of websocket servers on each iris station--
|
|
||||||
// ---------------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Note I keep this code only to no change the front-end API, we could only use the go-websocket and set our custom upgrader
|
|
||||||
|
|
||||||
// NewWebsocketServer creates a websocket server and returns it
|
|
||||||
func NewWebsocketServer(c *config.Websocket) *WebsocketServer {
|
|
||||||
wsConfig := websocket.Config{
|
|
||||||
WriteTimeout: c.WriteTimeout,
|
|
||||||
PongTimeout: c.PongTimeout,
|
|
||||||
PingPeriod: c.PingPeriod,
|
|
||||||
MaxMessageSize: c.MaxMessageSize,
|
|
||||||
BinaryMessages: c.BinaryMessages,
|
|
||||||
ReadBufferSize: c.ReadBufferSize,
|
|
||||||
WriteBufferSize: c.WriteBufferSize,
|
|
||||||
}
|
|
||||||
|
|
||||||
wsServer := websocket.New(wsConfig)
|
|
||||||
|
|
||||||
upgrader := irisWebsocket.Custom(wsServer.HandleConnection, c.ReadBufferSize, c.WriteBufferSize, false)
|
|
||||||
|
|
||||||
srv := &WebsocketServer{Server: wsServer, Config: c, upgrader: upgrader}
|
|
||||||
|
|
||||||
return srv
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterWebsocketServer registers the handlers for the websocket server
|
|
||||||
// it's a bridge between station and websocket server
|
|
||||||
func RegisterWebsocketServer(station FrameworkAPI, server *WebsocketServer, logger *log.Logger) {
|
|
||||||
c := server.Config
|
|
||||||
if c.Endpoint == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
websocketHandler := func(ctx *Context) {
|
|
||||||
|
|
||||||
if err := server.Upgrade(ctx); err != nil {
|
|
||||||
if ctx.framework.Config.IsDevelopment {
|
|
||||||
logger.Printf("Websocket error while trying to Upgrade the connection. Trace: %s", err.Error())
|
|
||||||
}
|
|
||||||
ctx.EmitError(StatusBadRequest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Headers != nil && len(c.Headers) > 0 { // only for performance matter just re-create the websocketHandler if we have headers to set
|
|
||||||
websocketHandler = func(ctx *Context) {
|
|
||||||
for k, v := range c.Headers {
|
|
||||||
ctx.SetHeader(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := server.Upgrade(ctx); err != nil {
|
|
||||||
if ctx.framework.Config.IsDevelopment {
|
|
||||||
logger.Printf("Websocket error while trying to Upgrade the connection. Trace: %s", err.Error())
|
|
||||||
}
|
|
||||||
ctx.EmitError(StatusBadRequest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clientSideLookupName := "iris-websocket-client-side"
|
|
||||||
station.Get(c.Endpoint, websocketHandler)
|
|
||||||
// check if client side already exists
|
|
||||||
if station.Lookup(clientSideLookupName) == nil {
|
|
||||||
// serve the client side on domain:port/iris-ws.js
|
|
||||||
station.StaticContent("/iris-ws.js", contentJavascript, websocket.ClientSource)(clientSideLookupName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// run the ws server
|
|
||||||
server.Serve()
|
|
||||||
}
|
|
||||||
|
|
||||||
// conversionals
|
// conversionals
|
||||||
const (
|
const (
|
||||||
// All is the string which the Emmiter use to send a message to all
|
// All is the string which the Emmiter use to send a message to all
|
||||||
|
@ -91,16 +15,38 @@ const (
|
||||||
Broadcast = websocket.Broadcast
|
Broadcast = websocket.Broadcast
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// newUnderlineWsServer returns a new go-websocket.Server from configuration, used internaly by Iris.
|
||||||
|
func newUnderlineWsServer(c WebsocketConfiguration) websocket.Server {
|
||||||
|
wsConfig := websocket.Config{
|
||||||
|
WriteTimeout: c.WriteTimeout,
|
||||||
|
PongTimeout: c.PongTimeout,
|
||||||
|
PingPeriod: c.PingPeriod,
|
||||||
|
MaxMessageSize: c.MaxMessageSize,
|
||||||
|
BinaryMessages: c.BinaryMessages,
|
||||||
|
ReadBufferSize: c.ReadBufferSize,
|
||||||
|
WriteBufferSize: c.WriteBufferSize,
|
||||||
|
}
|
||||||
|
|
||||||
|
return websocket.New(wsConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note I keep this code only to no change the front-end API, we could only use the go-websocket and set our custom upgrader
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// WebsocketServer is the iris websocket server, expose the websocket.Server
|
// WebsocketServer is the iris websocket server, expose the websocket.Server
|
||||||
// the below code is a wrapper and bridge between iris-contrib/websocket and kataras/go-websocket
|
// the below code is a wrapper and bridge between iris-contrib/websocket and kataras/go-websocket
|
||||||
WebsocketServer struct {
|
WebsocketServer struct {
|
||||||
websocket.Server
|
websocket.Server
|
||||||
Config *config.Websocket
|
config WebsocketConfiguration
|
||||||
upgrader irisWebsocket.Upgrader
|
upgrader irisWebsocket.Upgrader
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// NewWebsocketServer returns an empty WebsocketServer, nothing special here.
|
||||||
|
func NewWebsocketServer() *WebsocketServer {
|
||||||
|
return &WebsocketServer{}
|
||||||
|
}
|
||||||
|
|
||||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
||||||
//
|
//
|
||||||
// The responseHeader is included in the response to the client's upgrade
|
// The responseHeader is included in the response to the client's upgrade
|
||||||
|
@ -113,6 +59,30 @@ func (s *WebsocketServer) Upgrade(ctx *Context) error {
|
||||||
return s.upgrader.Upgrade(ctx)
|
return s.upgrader.Upgrade(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler is the iris Handler to upgrade the request
|
||||||
|
// used inside RegisterRoutes
|
||||||
|
func (s *WebsocketServer) Handler(ctx *Context) {
|
||||||
|
if err := s.Upgrade(ctx); err != nil {
|
||||||
|
if ctx.framework.Config.IsDevelopment {
|
||||||
|
ctx.Log("Websocket error while trying to Upgrade the connection. Trace: %s", err.Error())
|
||||||
|
}
|
||||||
|
ctx.EmitError(StatusBadRequest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterTo creates the client side source route and the route path Endpoint with the correct Handler
|
||||||
|
// receives the websocket configuration and the iris station
|
||||||
|
func (s *WebsocketServer) RegisterTo(station *Framework, c WebsocketConfiguration) {
|
||||||
|
s.config = c // save the configuration, we will need that on the .OnConnection
|
||||||
|
clientSideLookupName := "iris-websocket-client-side"
|
||||||
|
station.Get(s.config.Endpoint, s.Handler)
|
||||||
|
// check if client side already exists
|
||||||
|
if station.Lookup(clientSideLookupName) == nil {
|
||||||
|
// serve the client side on domain:port/iris-ws.js
|
||||||
|
station.StaticContent("/iris-ws.js", contentJavascript, websocket.ClientSource)(clientSideLookupName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WebsocketConnection is the front-end API that you will use to communicate with the client side
|
// WebsocketConnection is the front-end API that you will use to communicate with the client side
|
||||||
type WebsocketConnection interface {
|
type WebsocketConnection interface {
|
||||||
websocket.Connection
|
websocket.Connection
|
||||||
|
@ -120,6 +90,17 @@ type WebsocketConnection interface {
|
||||||
|
|
||||||
// OnConnection this is the main event you, as developer, will work with each of the websocket connections
|
// OnConnection this is the main event you, as developer, will work with each of the websocket connections
|
||||||
func (s *WebsocketServer) OnConnection(connectionListener func(WebsocketConnection)) {
|
func (s *WebsocketServer) OnConnection(connectionListener func(WebsocketConnection)) {
|
||||||
|
// let's initialize here the ws server, the user/dev is free to change its config before this step.
|
||||||
|
if s.Server == nil {
|
||||||
|
s.Server = newUnderlineWsServer(s.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.upgrader.Receiver == nil {
|
||||||
|
s.upgrader = irisWebsocket.Custom(s.Server.HandleConnection, s.config.ReadBufferSize, s.config.WriteBufferSize, false)
|
||||||
|
// run the ws server
|
||||||
|
s.Server.Serve()
|
||||||
|
}
|
||||||
|
|
||||||
s.Server.OnConnection(func(c websocket.Connection) {
|
s.Server.OnConnection(func(c websocket.Connection) {
|
||||||
connectionListener(c)
|
connectionListener(c)
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue
Block a user