diff --git a/DONATIONS.md b/DONATIONS.md
index ee561d42..0fc2d171 100644
--- a/DONATIONS.md
+++ b/DONATIONS.md
@@ -10,12 +10,13 @@ Feel free to send **any** amount through paypal
[![](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=kataras2006%40hotmail%2ecom&lc=GR&item_name=Iris%20web%20framework&item_number=iriswebframeworkdonationid2016¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted&return=http://iris-go.com/assets/v4-book/iris.pdf&cancel_return=https://www.gitbook.com/book/kataras/iris/details)
-> Please check your e-mail after your donation, I will ask you if you want to public your donation
+> Please check your e-mail after your donation
Benefits:
-- Your github username is visible to the very-top of the README page
+- Your github username,after your approval, is visible here . I respect your privacy at any case.
- Access to the 'donors' [private chat room](https://kataras.rocket.chat/group/donors), real-time assistance by me.
+- Each donate gives lifetime to the Iris web framework. The author works full-time on this project, no time for any part-time job.
**Thank you**!
@@ -33,7 +34,7 @@ I'm grateful for all the generous donations. Iris is fully funded by these dona
- ANONYMOUS(Waiting For Approval) donated 6 EUR at October 1
- [Ankur Srivastava](https://github.com/ansrivas) donated 20 EUR at October 2
-> The name of the donator added after his/her permission.
+> The name or/and github username link added after donator's approvement.
#### Report, so far
diff --git a/HISTORY.md b/HISTORY.md
index e88fa8da..b3a9c426 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -2,6 +2,62 @@
**How to upgrade**: remove your `$GOPATH/src/github.com/kataras` folder, open your command-line and execute this command: `go get -u github.com/kataras/iris/iris`.
+## 4.4.9 -> 4.5.0
+
+- **NEW**: Websocket configuration fields:
+ - `Error func(ctx *Context, status int, reason string)`. Manually catch any handshake errors. Default calls the `ctx.EmitError(status)` with a stored error message in the `WsError` key(`ctx.Set("WsError", reason)`), as before.
+ - `CheckOrigin func(ctx *Context)`. Manually allow or dissalow client's websocket access, ex: via header **Origin**. Default allow all origins(CORS-like) as before.
+ - `Headers bool`. Allow websocket handler to copy request's headers on the handshake. Default is true
+ With these in-mind the `WebsocketConfiguration` seems like this now :
+ ```go
+ 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
+ // Headers if true then the client's headers are copy to the websocket connection
+ //
+ // Default is true
+ Headers bool
+ // Error specifies the function for generating HTTP error responses.
+ //
+ // The default behavior is to store the reason in the context (ctx.Set(reason)) and fire any custom error (ctx.EmitError(status))
+ Error func(ctx *Context, status int, reason string)
+ // CheckOrigin returns true if the request Origin header is acceptable. If
+ // CheckOrigin is nil, the host in the Origin header must not be set or
+ // must match the host of the request.
+ //
+ // The default behavior is to allow all origins
+ // you can change this behavior by setting the iris.Config.Websocket.CheckOrigin = iris.WebsocketCheckSameOrigin
+ CheckOrigin func(ctx *Context) bool
+ }
+ ```
+
+- **REMOVE**: `github.com/kataras/iris/context/context.go` , this is no needed anymore. Its only usage was inside `sessions` and `websockets`, a month ago I did improvements to the sessions as a standalone package, the IContext interface is not being used there. With the today's changes, the iris-contrib/websocket doesn't needs the IContext interface too, so the whole folder `./context` is useless and removed now. Users developers don't have any side-affects from this change.
+
+
+[Examples](https://github.com/iris-contrib/examples), [Book](https://github.com/iris-contrib/gitbook) are up-to-date, just new configuration fields.
+
+
## 4.4.8 -> 4.4.9
- **FIX**: Previous CORS fix wasn't enough and produces error before server's startup[*](https://github.com/kataras/iris/issues/461) if many paths were trying to auto-register an `.OPTIONS` route, now this is fixed in combined with some improvements on the [cors middleware](https://github.com/iris-contrib/middleware/tree/master/cors) too.
diff --git a/README.md b/README.md
index 42652318..dd9934ad 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@
-
+
@@ -41,14 +41,6 @@ Ideally suited for both experienced and novice Developers.
- Check out [this](https://t.co/Y7bK0THScG) Meetup with GoLang User Group. Contains ~30 minutes talk about the Iris web framework. Happens at Thursday, October 13, 2016.
-#### Donations
-I'm grateful for all the generous donations. Iris is fully funded by these donations[*](DONATIONS.md).
-
-- [Ryan Brooks](https://github.com/ryanbyyc) donated 50 EUR at May 11
-- [Juan Sebastián Suárez Valencia](https://github.com/Juanses) donated 20 EUR at September 11
-- [Bob Lee](https://github.com/li3p) donated 20 EUR at September 16
-- [Celso Luiz](https://github.com/celsosz) donated 50 EUR at September 29
-- [Ankur Srivastava](https://github.com/ansrivas) donated 20 EUR at October 2
## Feature Overview
@@ -870,7 +862,7 @@ I recommend writing your API tests using this new library, [httpexpect](https://
Versioning
------------
-Current: **v4.4.9**
+Current: **v4.5.0**
> Iris is an active project
@@ -906,7 +898,7 @@ This project is licensed under the [MIT License](LICENSE), Copyright (c) 2016 Ge
[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]: https://github.com/kataras/iris/blob/master/LICENSE
-[Release Widget]: https://img.shields.io/badge/release-4.4.9%20-blue.svg?style=flat-square
+[Release Widget]: https://img.shields.io/badge/release-4.5.0%20-blue.svg?style=flat-square
[Release]: https://github.com/kataras/iris/releases
[Chat Widget]: https://img.shields.io/badge/community-chat%20-00BCD4.svg?style=flat-square
[Chat]: https://kataras.rocket.chat/channel/iris
diff --git a/configuration.go b/configuration.go
index a0f14e72..c644f445 100644
--- a/configuration.go
+++ b/configuration.go
@@ -6,6 +6,7 @@ import (
"github.com/kataras/go-sessions"
"github.com/valyala/fasthttp"
"io"
+ "net/url"
"os"
"strconv"
"time"
@@ -659,6 +660,21 @@ type WebsocketConfiguration struct {
ReadBufferSize int
// WriteBufferSize is the buffer size for the underline writer
WriteBufferSize int
+ // Headers if true then the client's headers are copy to the websocket connection
+ //
+ // Default is true
+ Headers bool
+ // Error specifies the function for generating HTTP error responses.
+ //
+ // The default behavior is to store the reason in the context (ctx.Set(reason)) and fire any custom error (ctx.EmitError(status))
+ Error func(ctx *Context, status int, reason string)
+ // CheckOrigin returns true if the request Origin header is acceptable. If
+ // CheckOrigin is nil, the host in the Origin header must not be set or
+ // must match the host of the request.
+ //
+ // The default behavior is to allow all origins
+ // you can change this behavior by setting the iris.Config.Websocket.CheckOrigin = iris.WebsocketCheckSameOrigin
+ CheckOrigin func(ctx *Context) bool
}
var (
@@ -717,6 +733,26 @@ var (
c.Websocket.WriteBufferSize = val
}
}
+ // OptionWebsocketHeaders if true then the client's headers are copy to the websocket connection
+ OptionWebsocketHeaders = func(val bool) OptionSet {
+ return func(c *Configuration) {
+ c.Websocket.Headers = val
+ }
+ }
+ // OptionWebsocketError specifies the function for generating HTTP error responses.
+ OptionWebsocketError = func(val func(*Context, int, string)) OptionSet {
+ return func(c *Configuration) {
+ c.Websocket.Error = val
+ }
+ }
+ // OptionWebsocketCheckOrigin returns true if the request Origin header is acceptable. If
+ // CheckOrigin is nil, the host in the Origin header must not be set or
+ // must match the host of the request.
+ OptionWebsocketCheckOrigin = func(val func(*Context) bool) OptionSet {
+ return func(c *Configuration) {
+ c.Websocket.CheckOrigin = val
+ }
+ }
)
const (
@@ -730,6 +766,31 @@ const (
DefaultMaxMessageSize = 1024
)
+var (
+ // DefaultWebsocketError is the default method to manage the handshake websocket errors
+ DefaultWebsocketError = func(ctx *Context, status int, reason string) {
+ ctx.Set("WsError", reason)
+ ctx.EmitError(status)
+ }
+ // DefaultWebsocketCheckOrigin is the default method to allow websocket clients to connect to this server
+ // you can change this behavior by setting the iris.Config.Websocket.CheckOrigin = iris.WebsocketCheckSameOrigin
+ DefaultWebsocketCheckOrigin = func(ctx *Context) bool {
+ return true
+ }
+ // WebsocketCheckSameOrigin returns true if the origin is not set or is equal to the request host
+ WebsocketCheckSameOrigin = func(ctx *Context) bool {
+ origin := ctx.RequestHeader("origin")
+ if len(origin) == 0 {
+ return true
+ }
+ u, err := url.Parse(origin)
+ if err != nil {
+ return false
+ }
+ return u.Host == ctx.HostString()
+ }
+)
+
// DefaultWebsocketConfiguration returns the default config for iris-ws websocket package
func DefaultWebsocketConfiguration() WebsocketConfiguration {
return WebsocketConfiguration{
@@ -741,6 +802,7 @@ func DefaultWebsocketConfiguration() WebsocketConfiguration {
ReadBufferSize: 4096,
WriteBufferSize: 4096,
Endpoint: "",
+ Headers: true,
}
}
diff --git a/context.go b/context.go
index e56ad261..f7f3c76b 100644
--- a/context.go
+++ b/context.go
@@ -1,7 +1,3 @@
-/*
-Context.go Implements: ./context/context.go
-*/
-
package iris
import (
@@ -24,7 +20,6 @@ import (
"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"
)
@@ -109,8 +104,6 @@ type (
}
)
-var _ context.IContext = &Context{}
-
// GetRequestCtx returns the current fasthttp context
func (ctx *Context) GetRequestCtx() *fasthttp.RequestCtx {
return ctx.RequestCtx
diff --git a/context/context.go b/context/context.go
deleted file mode 100644
index 08adb093..00000000
--- a/context/context.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package context
-
-import (
- "bufio"
- "github.com/kataras/go-sessions"
- "github.com/valyala/fasthttp"
- "io"
- "time"
-)
-
-type (
-
- // IContext the interface for the iris/context
- // Used mostly inside packages which shouldn't be import ,directly, the kataras/iris.
- IContext interface {
- Param(string) string
- ParamInt(string) (int, error)
- ParamInt64(string) (int64, error)
- URLParam(string) string
- URLParamInt(string) (int, error)
- URLParamInt64(string) (int64, error)
- URLParams() map[string]string
- MethodString() string
- HostString() string
- Subdomain() string
- PathString() string
- RequestPath(bool) string
- RequestIP() string
- RemoteAddr() string
- RequestHeader(k string) string
- IsAjax() bool
- FormValueString(string) string
- FormValues(string) []string
- PostValuesAll() map[string][]string
- PostValues(name string) []string
- PostValue(name string) string
- SetStatusCode(int)
- SetContentType(string)
- SetHeader(string, string)
- Redirect(string, ...int)
- RedirectTo(string, ...interface{})
- NotFound()
- Panic()
- EmitError(int)
- Write(string, ...interface{})
- HTML(int, string)
- Data(int, []byte) error
- RenderWithStatus(int, string, interface{}, ...map[string]interface{}) error
- Render(string, interface{}, ...map[string]interface{}) error
- MustRender(string, interface{}, ...map[string]interface{})
- TemplateString(string, interface{}, ...map[string]interface{}) string
- MarkdownString(string) string
- Markdown(int, string)
- JSON(int, interface{}) error
- JSONP(int, string, interface{}) error
- Text(int, string) error
- XML(int, interface{}) error
- ServeContent(io.ReadSeeker, string, time.Time, bool) error
- ServeFile(string, bool) error
- SendFile(string, string)
- Stream(func(*bufio.Writer))
- StreamWriter(cb func(*bufio.Writer))
- StreamReader(io.Reader, int)
- ReadJSON(interface{}) error
- ReadXML(interface{}) error
- ReadForm(interface{}) error
- Get(string) interface{}
- GetString(string) string
- GetInt(string) int
- Set(string, interface{})
- VisitAllCookies(func(string, string))
- SetCookie(*fasthttp.Cookie)
- SetCookieKV(string, string)
- RemoveCookie(string)
- GetFlashes() map[string]string
- GetFlash(string) (string, error)
- SetFlash(string, string)
- Session() sessions.Session
- SessionDestroy()
- Log(string, ...interface{})
- GetRequestCtx() *fasthttp.RequestCtx
- Do()
- Next()
- StopExecution()
- IsStopped() bool
- GetHandlerName() string
- }
-)
diff --git a/iris.go b/iris.go
index 3855c005..3d5685f3 100644
--- a/iris.go
+++ b/iris.go
@@ -79,7 +79,7 @@ import (
const (
// Version is the current version of the Iris web framework
- Version = "4.4.9"
+ Version = "4.5.0"
banner = ` _____ _
|_ _| (_)
diff --git a/ssh.go b/ssh.go
index 51d263bf..e303e03d 100644
--- a/ssh.go
+++ b/ssh.go
@@ -27,6 +27,8 @@ package iris
// log
// help
// exit
+//
+// Keep note that I will re-write this file, ssh.go because, as you can see, it's not well-written and not maintainable*
import (
"bytes"
diff --git a/websocket.go b/websocket.go
index 3f8204e3..9b7d62fa 100644
--- a/websocket.go
+++ b/websocket.go
@@ -23,6 +23,16 @@ type (
WebsocketServer struct {
websocket.Server
upgrader irisWebsocket.Upgrader
+
+ // the only fields we need at runtime here for iris-specific error and check origin funcs
+ // they comes from WebsocketConfiguration
+
+ // Error specifies the function for generating HTTP error responses.
+ Error func(ctx *Context, status int, reason string)
+ // CheckOrigin returns true if the request Origin header is acceptable. If
+ // CheckOrigin is nil, the host in the Origin header must not be set or
+ // must match the host of the request.
+ CheckOrigin func(ctx *Context) bool
}
)
@@ -40,17 +50,32 @@ func NewWebsocketServer() *WebsocketServer {
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
// response.
func (s *WebsocketServer) Upgrade(ctx *Context) error {
- return s.upgrader.Upgrade(ctx)
+ return s.upgrader.Upgrade(ctx.RequestCtx)
}
// Handler is the iris Handler to upgrade the request
// used inside RegisterRoutes
func (s *WebsocketServer) Handler(ctx *Context) {
+ // first, check origin
+ if !s.CheckOrigin(ctx) {
+ s.Error(ctx, StatusForbidden, "websocket: origin not allowed")
+ return
+ }
+
+ // all other errors comes from the underline iris-contrib/websocket
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)
+
+ statusErrCode := StatusBadRequest
+ if herr, isHandshake := err.(irisWebsocket.HandshakeError); isHandshake {
+ statusErrCode = herr.Status()
+ }
+ // if not handshake error just fire the custom(if any) StatusBadRequest
+ // with the websocket's error message in the ctx.Get("WsError")
+ DefaultWebsocketError(ctx, statusErrCode, err.Error())
+
}
}
@@ -64,7 +89,7 @@ func (s *WebsocketServer) RegisterTo(station *Framework, c WebsocketConfiguratio
s.Server = websocket.New()
}
// is just a conversional type for kataras/go-websocket.Connection
- s.upgrader = irisWebsocket.Custom(s.Server.HandleConnection, c.ReadBufferSize, c.WriteBufferSize, false)
+ s.upgrader = irisWebsocket.Custom(s.Server.HandleConnection, c.ReadBufferSize, c.WriteBufferSize, c.Headers)
// set the routing for client-side source (javascript) (optional)
clientSideLookupName := "iris-websocket-client-side"
@@ -85,6 +110,17 @@ func (s *WebsocketServer) RegisterTo(station *Framework, c WebsocketConfiguratio
WriteBufferSize: c.WriteBufferSize,
})
+ s.Error = c.Error
+ s.CheckOrigin = c.CheckOrigin
+
+ if s.Error == nil {
+ s.Error = DefaultWebsocketError
+ }
+
+ if s.CheckOrigin == nil {
+ s.CheckOrigin = DefaultWebsocketCheckOrigin
+ }
+
// run the ws server
s.Server.Serve()