Skip to content

Commit

Permalink
Merge pull request #4 for v0.3.0 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
jeevatkm committed Jul 7, 2018
2 parents 84af826 + 0ade879 commit b9882fc
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 69 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ branches:

go:
- 1.9.x
- 1.10.x
- 1.x
- tip

go_import_path: aahframework.org/ws.v0

before_install:
- bash <(curl -s https://aahframework.org/base-before-install) "ahttp config ainsp essentials valpar log router"
- bash <(curl -s https://aahframework.org/base-before-install) "forge ahttp config ainsp essentials valpar log router security"

install:
- go get -t -v ./...
Expand Down
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
# WebSocket Library by aah framework
<p align="center">
<img src="https://cdn.aahframework.org/assets/img/aah-logo-64x64.png" />
<h2 align="center">WebSocket library by aah framework</h2>
</p>
<p align="center">
<p align="center"><a href="https://travis-ci.org/go-aah/ws"><img src="https://travis-ci.org/go-aah/ws.svg?branch=master" alt="Build Status"></a> <a href="https://codecov.io/gh/go-aah/ws/branch/master"><img src="https://codecov.io/gh/go-aah/ws/branch/master/graph/badge.svg" alt="Code Coverage"></a> <a href="https://goreportcard.com/report/aahframework.org/ws.v0"><img src="https://goreportcard.com/badge/aahframework.org/ws.v0" alt="Go Report Card"></a> <a href="https://github.com/go-aah/ws/releases/latest"><img src="https://img.shields.io/badge/version-0.3.0-blue.svg" alt="Release Version"></a> <a href="https://godoc.org/aahframework.org/ws.v0"><img src="https://godoc.org/aahframework.org/ws.v0?status.svg" alt="Godoc"></a> <a href="https://twitter.com/aahframework"><img src="https://img.shields.io/badge/[email protected]" alt="Twitter @aahframework"></a></p>
</p>

[![Build Status](https://travis-ci.org/go-aah/ws.svg?branch=master)](https://travis-ci.org/go-aah/ws) [![codecov](https://codecov.io/gh/go-aah/ws/branch/master/graph/badge.svg)](https://codecov.io/gh/go-aah/ws/branch/master) [![Go Report Card](https://goreportcard.com/badge/aahframework.org/ws.v0)](https://goreportcard.com/report/aahframework.org/ws.v0) [![Version](https://img.shields.io/badge/version-0.2.0-blue.svg)](https://github.com/go-aah/ws/releases/latest) [![GoDoc](https://godoc.org/aahframework.org/ws.v0?status.svg)](https://godoc.org/aahframework.org/ws.v0) [![License](https://img.shields.io/github/license/go-aah/ws.svg)](LICENSE) [![Twitter](https://img.shields.io/badge/[email protected])](https://twitter.com/aahframework)
aah WS is RFC 6455 compliant; internally it uses tiny, efficient [WebSocket library](http://github.com/gobwas/ws) developed by [Sergey Kamardin](https://github.com/gobwas).

***v0.2.0 [released](https://github.com/go-aah/ws/releases/latest) and tagged on TBD***
### News

aah ws RFC 6455 compliant; internally uses tiny, efficient [WebSocket library](http://github.com/gobwas/ws) developed by [Sergey Kamardin](https://github.com/gobwas).
* `v0.3.0` [released](https://github.com/go-aah/ws/releases/latest) and tagged on Jul 06, 2018.

# Installation
## Installation

```bash
# install the library
go get -u aahframework.org/ws.v0
```

Visit official website https://aahframework.org to learn more.
Visit official website https://aahframework.org to learn more about `aah` framework.
11 changes: 2 additions & 9 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ package ws
import (
"encoding/json"
"encoding/xml"
"fmt"
"html"
"net"
"net/http"
Expand Down Expand Up @@ -35,7 +34,7 @@ var (
type Context struct {
Req *Request
Conn net.Conn
Header http.Header
Header http.Header // These headers are sent to WS client during Connection upgrade

e *Engine
hs gws.Handshake
Expand Down Expand Up @@ -213,7 +212,7 @@ func (ctx *Context) parseParameters() error {
for k, v := range ctx.Req.pathParams {
params.Set(k, v)
}
for k, v := range ctx.Req.queryParams {
for k, v := range ctx.Req.URL().Query() {
params[k] = v
}

Expand All @@ -224,12 +223,6 @@ func (ctx *Context) parseParameters() error {
var result reflect.Value
if vpFn, found := valpar.ValueParser(val.Type); found {
result, err = vpFn(val.Name, val.Type, params)
if rule, found := ctx.route.ValidationRule(val.Name); found {
if !valpar.ValidateValue(result.Interface(), rule) {
return fmt.Errorf("Path param validation failed [name: %s, rule: %s, value: %v]",
val.Name, rule, result.Interface())
}
}
} else if val.Kind == reflect.Struct {
result, err = valpar.Struct("", val.Type, params)
}
Expand Down
36 changes: 25 additions & 11 deletions engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"aahframework.org/essentials.v0"
"aahframework.org/log.v0"
"aahframework.org/router.v0"
"aahframework.org/valpar.v0"

gws "github.com/gobwas/ws"
)
Expand Down Expand Up @@ -62,6 +63,13 @@ type IDGenerator func(ctx *Context) string
// EventCallbackFunc func type used for all WebSocket event callback.
type EventCallbackFunc func(eventName string, ctx *Context)

// aah application interface for minimal purpose
type application interface {
Config() *config.Config
Router() *router.Router
Log() log.Loggerer
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Engine type and its methods
//______________________________________________________________________________
Expand All @@ -70,15 +78,13 @@ type EventCallbackFunc func(eventName string, ctx *Context)
type Engine struct {
checkOrigin bool
originWhitelist []*url.URL
cfg *config.Config
router *router.Router
app application
registry *ainsp.TargetRegistry
onPreConnect EventCallbackFunc
onPostConnect EventCallbackFunc
onPostDisconnect EventCallbackFunc
onError EventCallbackFunc
idGenerator IDGenerator
logger log.Loggerer
}

// AddWebSocket method adds the given WebSocket implementation into engine.
Expand Down Expand Up @@ -133,7 +139,7 @@ func (e *Engine) SetIDGenerator(g IDGenerator) {
// Along with Check Origin, aah WebSocket events such as `OnPreConnect`,
// `OnPostConnect`, `OnPostDisconnect` and `OnError`.
func (e *Engine) Handle(w http.ResponseWriter, r *http.Request) {
domain := e.router.Lookup(ahttp.Host(r))
domain := e.app.Router().Lookup(ahttp.Host(r))
if domain == nil {
e.Log().Errorf("WS: domain not found: %s", ahttp.Host(r))
e.replyError(w, http.StatusNotFound)
Expand Down Expand Up @@ -173,7 +179,7 @@ func (e *Engine) Handle(w http.ResponseWriter, r *http.Request) {

// Log method provides logging methods at WebSocket engine.
func (e *Engine) Log() log.Loggerer {
return e.logger
return e.app.Log()
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Expand All @@ -183,6 +189,15 @@ func (e *Engine) Log() log.Loggerer {
func (e *Engine) connect(w http.ResponseWriter, r *http.Request, route *router.Route, pathParams ahttp.PathParams) (*Context, error) {
ctx := e.newContext(r, route, pathParams)

// Route constraints validation
if errs := valpar.ValidateValues(pathParams, route.Constraints); len(errs) > 0 {
ctx.Log().Error("WS: Route constraints failed")
ctx.reason = router.ErrRouteConstraintFailed
e.publishOnErrorEvent(ctx)
e.replyError(w, http.StatusBadRequest)
return nil, router.ErrRouteConstraintFailed
}

// Check Origin
if e.checkOrigin && !e.isSameOrigin(ctx) {
ctx.Log().Error("WS: Origin mismatch")
Expand Down Expand Up @@ -242,12 +257,11 @@ func (e *Engine) newContext(r *http.Request, route *router.Route, pathParams aht
Header: make(http.Header),
route: route,
Req: &Request{
Host: ahttp.Host(r),
Path: r.URL.Path,
Header: r.Header,
pathParams: pathParams,
queryParams: r.URL.Query(),
raw: r,
Host: ahttp.Host(r),
Path: r.URL.Path,
Header: r.Header,
pathParams: pathParams,
raw: r,
},
}
ctx.Req.ID = e.createID(ctx)
Expand Down
84 changes: 65 additions & 19 deletions engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
Expand Down Expand Up @@ -42,19 +43,8 @@ func TestEngineWSClient(t *testing.T) {
}
`

cfg, _ := config.ParseString(cfgStr)
wse := newEngine(t, cfg)

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get(ahttp.HeaderOrigin) == "" {
r.Header.Set(ahttp.HeaderOrigin, fmt.Sprintf("http://%s", ahttp.Host(r)))
}
wse.Handle(w, r)
}))
assert.NotNil(t, ts)
t.Logf("Test WS server running here : %s", ts.URL)

wsURL := strings.Replace(ts.URL, "http", "ws", -1)
ts := createWSTestServer(t, cfgStr, "routes.conf")
wsURL := strings.Replace(ts.ts.URL, "http", "ws", -1)

// test cases
testcases := []struct {
Expand Down Expand Up @@ -110,7 +100,7 @@ func TestEngineWSClient(t *testing.T) {
for _, tc := range testcases {
t.Run(tc.label, func(t *testing.T) {
if tc.customID {
wse.SetIDGenerator(func(ctx *Context) string {
ts.wse.SetIDGenerator(func(ctx *Context) string {
return ess.RandomString(32)
})
}
Expand Down Expand Up @@ -141,20 +131,76 @@ func TestEngineWSClient(t *testing.T) {
assert.Equal(t, tc.content, b)
})
}

}

func newEngine(t *testing.T, cfg *config.Config) *Engine {
func TestEngineWSErrors(t *testing.T) {
cfgStr := `
server {
websocket {
enable = true
}
}
`

ts := createWSTestServer(t, cfgStr, "routes-multi.conf")

resp, err := http.Get(ts.ts.URL + "/ws/text")
assert.Nil(t, err)
assert.Equal(t, http.StatusNotFound, resp.StatusCode)

// 405 Method Not Allowed
w := httptest.NewRecorder()
r := httptest.NewRequest(ahttp.MethodPost, "http://localhost:8080/ws/text", strings.NewReader("error=notsupported"))
ts.wse.Handle(w, r)
assert.Equal(t, "405 Method Not Allowed", w.Body.String())
}

type testServer struct {
ts *httptest.Server
wse *Engine
}

type app struct {
cfg *config.Config
r *router.Router
l log.Loggerer
}

func (a *app) Config() *config.Config { return a.cfg }
func (a *app) Router() *router.Router { return a.r }
func (a *app) Log() log.Loggerer { return a.l }

func createWSTestServer(t *testing.T, cfgStr, routeFile string) *testServer {
cfg, _ := config.ParseString(cfgStr)
wse := newEngine(t, cfg, routeFile)

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get(ahttp.HeaderOrigin) == "" {
r.Header.Set(ahttp.HeaderOrigin, fmt.Sprintf("http://%s", ahttp.Host(r)))
}
wse.Handle(w, r)
}))
assert.NotNil(t, ts)

t.Logf("Test WS server running here : %s", ts.URL)

return &testServer{ts: ts, wse: wse}
}

func newEngine(t *testing.T, cfg *config.Config, routeFile string) *Engine {
l, err := log.New(cfg)
assert.Nil(t, err)

r := router.New(filepath.Join(testdataBaseDir(), "routes.conf"), config.NewEmptyConfig())
l.SetWriter(ioutil.Discard)

r := router.New(filepath.Join(testdataBaseDir(), routeFile), config.NewEmpty())
err = r.Load()
assert.Nil(t, err)

wse, err := New(cfg, l, r)
wse, err := New(&app{cfg: cfg, r: r, l: l})
assert.Nil(t, err)
assert.NotNil(t, wse.logger)
assert.NotNil(t, wse.cfg)
assert.NotNil(t, wse.app)

// Adding events
addWebSocketEvents(t, wse)
Expand Down
18 changes: 11 additions & 7 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ type Request struct {
// Header holds the values of HTTP headers when WebSocket connection made.
Header http.Header

pathParams ahttp.PathParams
queryParams url.Values
raw *http.Request
pathParams ahttp.PathParams
raw *http.Request
}

// URL method returns HTTP request URL instance.
func (r *Request) URL() *url.URL {
return r.raw.URL
}

// PathValue method returns value for given Path param key otherwise empty string.
Expand All @@ -50,13 +54,13 @@ func (r *Request) PathValue(key string) string {
// QueryValue method returns value for given URL query param key
// otherwise empty string.
func (r *Request) QueryValue(key string) string {
return r.queryParams.Get(key)
return r.URL().Query().Get(key)
}

// QueryArrayValue method returns array value for given URL query param key
// otherwise empty string slice.
func (r *Request) QueryArrayValue(key string) []string {
if values, found := r.queryParams[key]; found {
if values, found := r.URL().Query()[key]; found {
return values
}
return []string{}
Expand All @@ -72,10 +76,10 @@ func (r *Request) ClientIP() string {

// String request stringer interface.
func (r Request) String() string {
return fmt.Sprintf("ReqID: %s, Host: %s, Path: %s, Query String: %s",
return fmt.Sprintf("request(id:%s host:%s path:%s querystring:%s)",
r.ID,
r.Host,
r.Path,
r.queryParams.Encode(),
r.URL().Query().Encode(),
)
}
Loading

0 comments on commit b9882fc

Please sign in to comment.