From a95c7a60aa830a780e7eb019f90f5c75c2bfdd84 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Fri, 5 Jul 2019 16:22:20 +0300 Subject: [PATCH] add support for iris-specific form of generating connection IDs as requested at: https://github.com/kataras/neffos/issues/1#issuecomment-508689819 --- _examples/authentication/request/main.go | 13 ++++---- context/context.go | 22 ++++++------- core/router/api_builder.go | 2 +- crypto/crypto.go | 10 ++++-- crypto/json.go | 6 ++-- crypto/sign/sign.go | 4 +-- go.mod | 2 +- view/README.md | 2 +- websocket/websocket.go | 41 ++++++++++++++++++------ 9 files changed, 64 insertions(+), 38 deletions(-) diff --git a/_examples/authentication/request/main.go b/_examples/authentication/request/main.go index 1d3ff8f4b..c097dff8c 100644 --- a/_examples/authentication/request/main.go +++ b/_examples/authentication/request/main.go @@ -8,7 +8,8 @@ import ( ) var ( - // Change that to your owns, usally you have an ECDSA private key + // Change that to your own key. + // Usually you have an ECDSA private key // per identify, let's say a user, stored in a database // or somewhere else and you use its public key // to sign a user's payload and when this client @@ -17,11 +18,9 @@ var ( // with the user's public key. // // Use the crypto.MustGenerateKey to generate a random key - // or import - // the "github.com/kataras/iris/crypto/sign" - // and use its - // sign.ParsePrivateKey/ParsePublicKey(theKey []byte) - // to convert data or local file to an *ecdsa.PrivateKey. + // or + // crypto.ParsePrivateKey to convert data or local file to an *ecdsa.PrivateKey. + // and `crypto.ParsePublicKey` if you only have access to the public one. testPrivateKey = crypto.MustGenerateKey() testPublicKey = &testPrivateKey.PublicKey ) @@ -34,7 +33,7 @@ type testPayloadStructure struct { // The Iris crypto package offers // authentication (with optional encryption in top of) and verification // of raw []byte data with `crypto.Marshal/Unmarshal` functions -// and JSON payloads with `crypto.SignJSON/VerifyJSON functions. +// and JSON payloads with `crypto.SignJSON/VerifyJSON` functions. // // Let's use the `SignJSON` and `VerifyJSON` here as an example, // as this is the most common scenario for a web application. diff --git a/context/context.go b/context/context.go index c06fe5cac..a7ff58ce7 100644 --- a/context/context.go +++ b/context/context.go @@ -28,7 +28,7 @@ import ( "github.com/fatih/structs" "github.com/iris-contrib/blackfriday" formbinder "github.com/iris-contrib/formBinder" - "github.com/json-iterator/go" + jsoniter "github.com/json-iterator/go" "github.com/microcosm-cc/bluemonday" "gopkg.in/yaml.v2" ) @@ -826,7 +826,7 @@ type Context interface { // // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic SetCookieKV(name, value string, options ...CookieOption) - // GetCookie returns cookie's value by it's name + // GetCookie returns cookie's value by its name // returns empty string if nothing was found. // // If you want more than the value then: @@ -834,12 +834,12 @@ type Context interface { // // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic GetCookie(name string, options ...CookieOption) string - // RemoveCookie deletes a cookie by it's name and path = "/". + // RemoveCookie deletes a cookie by its name and path = "/". // Tip: change the cookie's path to the current one by: RemoveCookie("name", iris.CookieCleanPath) // // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic RemoveCookie(name string, options ...CookieOption) - // VisitAllCookies takes a visitor which loops + // VisitAllCookies accepts a visitor function which is called // on each (request's) cookies' name and value. VisitAllCookies(visitor func(name string, value string)) @@ -3233,7 +3233,7 @@ func CookieHTTPOnly(httpOnly bool) CookieOption { type ( // CookieEncoder should encode the cookie value. - // Should accept as first argument the cookie name + // Should accept the cookie's name as its first argument // and as second argument the cookie value ptr. // Should return an encoded value or an empty one if encode operation failed. // Should return an error if encode operation failed. @@ -3245,7 +3245,7 @@ type ( // See `CookieDecoder` too. CookieEncoder func(cookieName string, value interface{}) (string, error) // CookieDecoder should decode the cookie value. - // Should accept as first argument the cookie name, + // Should accept the cookie's name as its first argument, // as second argument the encoded cookie value and as third argument the decoded value ptr. // Should return a decoded value or an empty one if decode operation failed. // Should return an error if decode operation failed. @@ -3329,7 +3329,7 @@ func (ctx *context) SetCookieKV(name, value string, options ...CookieOption) { ctx.SetCookie(c, options...) } -// GetCookie returns cookie's value by it's name +// GetCookie returns cookie's value by its name // returns empty string if nothing was found. // // If you want more than the value then: @@ -3350,13 +3350,13 @@ func (ctx *context) GetCookie(name string, options ...CookieOption) string { return value } -// SetCookieKVExpiration is 2 hours by-default +// SetCookieKVExpiration is 365 days by-default // you can change it or simple, use the SetCookie for more control. // // See `SetCookieKVExpiration` and `CookieExpires` for more. -var SetCookieKVExpiration = time.Duration(120) * time.Minute +var SetCookieKVExpiration = time.Duration(8760) * time.Hour -// RemoveCookie deletes a cookie by it's name and path = "/". +// RemoveCookie deletes a cookie by its name and path = "/". // Tip: change the cookie's path to the current one by: RemoveCookie("name", iris.CookieCleanPath) // // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic @@ -3375,7 +3375,7 @@ func (ctx *context) RemoveCookie(name string, options ...CookieOption) { ctx.request.Header.Set("Cookie", "") } -// VisitAllCookies takes a visitor which loops +// VisitAllCookies takes a visitor function which is called // on each (request's) cookies' name and value. func (ctx *context) VisitAllCookies(visitor func(name string, value string)) { for _, cookie := range ctx.request.Cookies() { diff --git a/core/router/api_builder.go b/core/router/api_builder.go index 13b61ec64..35005f873 100644 --- a/core/router/api_builder.go +++ b/core/router/api_builder.go @@ -397,7 +397,7 @@ func (api *APIBuilder) HandleMany(methodOrMulti string, relativePathorMulti stri // second parameter : the system or the embedded directory that needs to be served // third parameter : not required, the directory options, set fields is optional. // -// for more options look router.FileServer. +// Alternatively, to get just the handler for that look the FileServer function instead. // // api.HandleDir("/static", "./assets", DirOptions {ShowList: true, Gzip: true, IndexName: "index.html"}) // diff --git a/crypto/crypto.go b/crypto/crypto.go index ca7889e1d..ed41ee1c6 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -9,9 +9,13 @@ import ( ) var ( - // MustGenerateKey generates an ecdsa public and private key pair. + // MustGenerateKey generates an ecdsa private and public key pair. // It panics if any error occurred. MustGenerateKey = sign.MustGenerateKey + // ParsePrivateKey accepts a pem x509-encoded private key and decodes to *ecdsa.PrivateKey. + ParsePrivateKey = sign.ParsePrivateKey + // ParsePublicKey accepts a pem x509-encoded public key and decodes to *ecdsa.PrivateKey. + ParsePublicKey = sign.ParsePublicKey // MustGenerateAESKey generates an aes key. // It panics if any error occurred. @@ -79,9 +83,9 @@ func Decrypt(aesKey, additionalData []byte) Decryption { // Returns non-nil error if any error occurred. // // Usage: -// data, _ := ioutil.ReadAll(r.Body) +// data, _ := ioutil.ReadAll(ctx.Request().Body) // signedData, err := crypto.Marshal(testPrivateKey, data, nil) -// w.Write(signedData) +// ctx.Write(signedData) // Or if data should be encrypted: // signedEncryptedData, err := crypto.Marshal(testPrivateKey, data, crypto.Encrypt(aesKey, nil)) func Marshal(privateKey *ecdsa.PrivateKey, data []byte, encrypt Encryption) ([]byte, error) { diff --git a/crypto/json.go b/crypto/json.go index 09f97f91c..e5190186c 100644 --- a/crypto/json.go +++ b/crypto/json.go @@ -53,11 +53,11 @@ func SignJSON(privateKey *ecdsa.PrivateKey, r io.Reader) (Ticket, error) { // VerifyJSON verifies the incoming JSON request, // by reading the "r" which should decodes to a `Ticket`. // The `Ticket` is verified against the given "publicKey", the `Ticket#Signature` and -// `Ticket#Payload` data (original request's payload data which was signed by `SignPayload`). +// `Ticket#Payload` data (original request's payload data which was signed by `SignJSON`). // -// Returns true wether the verification succeed or not. +// Returns true whether the verification succeed or not. // The "toPayloadPtr" should be a pointer to a value of the same payload structure the client signed on. -// If and only if the verification succeed the payload value is filled from the `Ticket#Payload` raw data. +// If and only if the verification succeed the payload value is filled from the `Ticket.Payload` raw data. // // Check for both output arguments in order to: // 1. verification (true/false and error) and diff --git a/crypto/sign/sign.go b/crypto/sign/sign.go index 9c4a1d3c9..45d235a2e 100644 --- a/crypto/sign/sign.go +++ b/crypto/sign/sign.go @@ -20,7 +20,7 @@ import ( "golang.org/x/crypto/sha3" ) -// MustGenerateKey generates a public and private key pair. +// MustGenerateKey generates a private and public key pair. // It panics if any error occurred. func MustGenerateKey() *ecdsa.PrivateKey { privateKey, err := GenerateKey() @@ -31,7 +31,7 @@ func MustGenerateKey() *ecdsa.PrivateKey { return privateKey } -// GenerateKey generates a public and private key pair. +// GenerateKey generates a private and public key pair. func GenerateKey() (*ecdsa.PrivateKey, error) { return ecdsa.GenerateKey(elliptic.P256(), rand.Reader) } diff --git a/go.mod b/go.mod index 9afb9364e..b364ad4c1 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/iris-contrib/go.uuid v2.0.0+incompatible github.com/json-iterator/go v1.1.6 // vendor removed. github.com/kataras/golog v0.0.0-20180321173939-03be10146386 - github.com/kataras/neffos v0.0.2 + github.com/kataras/neffos v0.0.3 github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d // indirect github.com/microcosm-cc/bluemonday v1.0.2 github.com/ryanuber/columnize v2.1.0+incompatible diff --git a/view/README.md b/view/README.md index fedcb9e8a..b5a5318b8 100644 --- a/view/README.md +++ b/view/README.md @@ -3,7 +3,7 @@ Iris supports 6 template engines out-of-the-box, developers can still use any external golang template engine, as `context/context#ResponseWriter()` is an `io.Writer`. -All of these five template engines have common features with common API, +All of these six template engines have common features with common API, like Layout, Template Funcs, Party-specific layout, partial rendering and more. - The standard html, its template parser is the [golang.org/pkg/html/template/](https://golang.org/pkg/html/template/) diff --git a/websocket/websocket.go b/websocket/websocket.go index 2c1012786..893c8e2e6 100644 --- a/websocket/websocket.go +++ b/websocket/websocket.go @@ -28,8 +28,10 @@ var ( // See examples for more. New = neffos.New // DefaultIDGenerator returns a universal unique identifier for a new connection. - // It's the default `IDGenerator` for `Server`. - DefaultIDGenerator = neffos.DefaultIDGenerator + // It's the default `IDGenerator` if missing. + DefaultIDGenerator = func(ctx context.Context) string { + return neffos.DefaultIDGenerator(ctx.ResponseWriter(), ctx.Request()) + } // GorillaDialer is a `Dialer` type for the gorilla/websocket subprotocol implementation. // Should be used on `Dial` to create a new client/client-side connection. @@ -113,18 +115,39 @@ func SetDefaultUnmarshaler(fn func(data []byte, v interface{}) error) { neffos.DefaultUnmarshaler = fn } +// IDGenerator is an iris-specific IDGenerator for new connections. +type IDGenerator func(context.Context) string + // Handler returns an Iris handler to be served in a route of an Iris application. -func Handler(s *neffos.Server) context.Handler { +// Accepts the neffos websocket server as its first input argument +// and optionally an Iris-specific `IDGenerator` as its second one. +func Handler(s *neffos.Server, IDGenerator ...IDGenerator) context.Handler { + idGen := DefaultIDGenerator + if len(IDGenerator) > 0 { + idGen = IDGenerator[0] + } + return func(ctx context.Context) { - s.Upgrade(ctx.ResponseWriter(), ctx.Request(), func(socket neffos.Socket) neffos.Socket { - return &socketWrapper{ - Socket: socket, - ctx: ctx, - } - }) + if ctx.IsStopped() { + return + } + Upgrade(ctx, idGen(ctx), s) } } +// Upgrade upgrades the request and returns a new websocket Conn. +// Use `Handler` for higher-level implementation instead. +func Upgrade(ctx context.Context, customID string, s *neffos.Server) *neffos.Conn { + conn, _ := s.Upgrade(ctx.ResponseWriter(), ctx.Request(), func(socket neffos.Socket) neffos.Socket { + return &socketWrapper{ + Socket: socket, + ctx: ctx, + } + }, customID) + + return conn +} + type socketWrapper struct { neffos.Socket ctx context.Context