Skip to content

Commit 231c1ae

Browse files
committed
change the project name
documentation
1 parent 7e79d53 commit 231c1ae

34 files changed

+337
-134
lines changed

Diff for: README.md

+129-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,133 @@
1-
# GOW
1+
<h1 align="center">GOFRE - A Sweet Web Framework for Go</h1>
22

3-
GOW is a go web framework with middleware support, templating, SSE and more.
3+
<p align="center">
4+
<img src="docs/img/gopher.png" />
5+
</p>
6+
7+
_Gofre_ if a web framework for Go that makes the development of the web applications a joy. Gofre integrates with `http.Server` and supports the standard handlers: `http.Handler` and `http.HandlerFunc`.
8+
9+
This framework was developed around simplicity of usage and extensibility and offers the following features:
10+
* **Middleware**
11+
* **Path pattern matching** - including path variable extraction and validation
12+
* **Templating** - including static resources
13+
* **Authentication** - OAUTH2 flow included for GitHub and Google
14+
* **Authorization** - RBAC implementation
15+
* **SSE (Server Sent-Events)**
16+
* **Security** - CSRF Middleware protection
17+
18+
## Architecture Overview
19+
20+
_Gofre_ has the following components:
21+
* **HttpRequest** - an object that encapsulates the initial `http.Request` and the path variables, if exists
22+
* **HttpResponse** - an object that encapsulates the response and knows how to write it back to the client
23+
* **Handler** - a function that receives a `Context` and an `HttpRequest` and returns an `HttpResponse` or an `error`
24+
* **Middleware** - a function that receives a `Handler` and returns another `Handler`
25+
* **Router** - an object that knows how to parse the `http.Request` and to route it to the corresponding `Handler`
26+
27+
![Architecture](docs/img/gofre-architecture.png)
28+
29+
### Path Pattern Matching
30+
31+
_Gofre_ supports a complex path matching where the most specific pattern is chosen first.
32+
33+
Supported path patterns matching:
34+
35+
1. **exact matching** - `/a/b/c`
36+
2. **capture variable without constraints**
37+
1. `/a/{b}/{c}`
38+
1. `/a/john/doe` => b: john, c: doe
39+
3. **capture variable with constraints**
40+
1. `/a/{uuid:^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$}` - UUID matching
41+
1. `/a/zyw3040f-0f1c-4e98-b71c-d3cd61213f90` => false (z,x and w are not part of UUID regex)
42+
2. `/a/fbd3040f-0f1c-4e98-b71c-d3cd61213f90` => true
43+
2. `/a/{number:^[0-9]{3}$}` - number with 3 digits
44+
1. `/a/12` => false
45+
1. `/a/123` => true
46+
1. `/a/012` => true
47+
1. `/a/0124` => false
48+
4. **literal match regex**
49+
1. **&ast;** - matches any number of characters or a single segment path
50+
1. `/a/abc*hij`
51+
1. `/a/abcdhij` => true
52+
2. `/a/abcdefghij` => true
53+
3`/a/abcdefgij` => false (the path doesn't end with `hij`)
54+
2. `/a/*/c`
55+
1. `/a/b/c` => true
56+
2. `/a/b/c/c` => false (max 3 path segments allowed, and we have 4)
57+
3. `/a/abc*hij/*`
58+
1. `/a/abcdefghij/abc` => true
59+
2. `/a/abcdefghij/abc/xyz` => false (`*` matches a single path segment and, we have 2 `abc/xyz`)
60+
2. **?** - matches a single character
61+
1. `/a/abc?hij`
62+
1. `/a/abcdhij` => true
63+
2. `/a/abcdehij` => false (the character `e` will not match)
64+
5. **greedy match**
65+
1. **&ast;&ast;** - matches multiple path segments
66+
1. `/a/**/z`
67+
1. `/a/b/c/d/e/f/z` => true
68+
1. `/a/b/c/d/e/f` => false (the path should end in `/z`)
69+
2. `/a/**`
70+
1. `/a/b/c/d/e/f` => true
71+
72+
Comparing with other libraries, _Gofre_ does not require to declare the path patterns in a specific order so that the match to work as you expect.
73+
74+
For example, these path matching patterns (assuming we handle only GET requests) can be declared in any order in your code:
75+
76+
1. `/users/john/{lastName}`
77+
2. `/users/john/doe`
78+
3. `/users/*/doe`
79+
80+
Let's consider the following HTTP requests:
81+
82+
* `https://www.website.com/users/john/doe` - the second pattern will match
83+
* `https://www.website.com/users/john/wick` - the first pattern will match, where the lastName will be `wick`
84+
* `https://www.website.com/users/jane/doe` - the third pattern will match
85+
86+
_Gofre_ includes also support for greedy path matching: `**`
87+
88+
* `/users/**/doe` - matches any path that starts with `/users` and ends with `/doe`
89+
* `/users/**` - matches any path that starts with `/users`
90+
91+
The path matching can be **case-sensitive** (default) or **case-insensitive**
92+
93+
## Installation
94+
95+
You can install this repo with `go get`:
96+
```sh
97+
go get github.com/ixtendio/gofre
98+
```
99+
## Usage
100+
101+
```go
102+
gowConfig := &gofre.Config{
103+
CaseInsensitivePathMatch: false,
104+
ContextPath: "",
105+
ErrLogFunc: func(err error) {
106+
log.Printf("An error occurred in the gofre framework: %v", err)
107+
},
108+
}
109+
gowMux, err := gofre.NewMuxHandler(gowConfig)
110+
if err != nil {
111+
log.Fatalf("Failed to create gofre mux handler, err: %v", err)
112+
}
113+
114+
// JSON with vars path
115+
gowMux.HandleGet("/hello/{firstName}/{lastName}", func(ctx context.Context, r *request.HttpRequest) (response.HttpResponse, error) {
116+
return response.JsonHttpResponseOK(r.UriVars), nil
117+
})
118+
119+
httpServer := http.Server{
120+
Addr: ":8080",
121+
Handler: gowMux,
122+
}
123+
if err := httpServer.ListenAndServe(); err != nil {
124+
log.Fatalf("Failed starting the HTTP server, err: %v", err)
125+
}
126+
```
127+
128+
```shell
129+
curl -vvv "https://localhost:8080/hello/John/Doe"
130+
```
4131

5132
# Run the examples
6133

Diff for: auth/oauth/github.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7-
"github.com/ixtendio/gow/auth"
7+
"github.com/ixtendio/gofre/auth"
88
"net/http"
99
"net/url"
1010
"strconv"

Diff for: auth/oauth/google.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"context"
66
"encoding/json"
77
"fmt"
8-
"github.com/ixtendio/gow/auth"
8+
"github.com/ixtendio/gofre/auth"
99
"net/http"
1010
"net/url"
1111
"strconv"

Diff for: auth/oauth/oauth2.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package oauth
22

33
import (
44
"context"
5-
"github.com/ixtendio/gow/auth"
6-
"github.com/ixtendio/gow/cache"
5+
"github.com/ixtendio/gofre/auth"
6+
"github.com/ixtendio/gofre/cache"
77
"net"
88
"net/http"
99
"time"

Diff for: examples/cmd.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ package main
22

33
import (
44
"context"
5-
"github.com/ixtendio/gow"
6-
"github.com/ixtendio/gow/auth"
7-
"github.com/ixtendio/gow/auth/oauth"
8-
"github.com/ixtendio/gow/cache"
9-
"github.com/ixtendio/gow/middleware"
10-
"github.com/ixtendio/gow/request"
11-
"github.com/ixtendio/gow/response"
12-
"github.com/ixtendio/gow/router"
5+
"github.com/ixtendio/gofre"
6+
"github.com/ixtendio/gofre/auth"
7+
"github.com/ixtendio/gofre/auth/oauth"
8+
"github.com/ixtendio/gofre/cache"
9+
"github.com/ixtendio/gofre/handler"
10+
"github.com/ixtendio/gofre/middleware"
11+
"github.com/ixtendio/gofre/request"
12+
"github.com/ixtendio/gofre/response"
1313
"log"
1414
"net"
1515
"net/http"
@@ -133,7 +133,7 @@ func main() {
133133
gowMux.HandleGet("/security/authorize/{permission}", func(ctx context.Context, r *request.HttpRequest) (response.HttpResponse, error) {
134134
return response.JsonHttpResponseOK(map[string]string{"authorized": "true"}), nil
135135
}, middleware.ErrJsonResponse(),
136-
func(handler router.Handler) router.Handler {
136+
func(handler handler.Handler) handler.Handler {
137137
// authentication provider
138138
return func(ctx context.Context, req *request.HttpRequest) (resp response.HttpResponse, err error) {
139139
permission, err := auth.ParsePermission("domain/subdomain/resource:" + req.UriVars["permission"])

Diff for: examples/evtgen.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package main
33
import (
44
"context"
55
"fmt"
6-
"github.com/ixtendio/gow/response"
6+
"github.com/ixtendio/gofre/response"
77
"sync/atomic"
88
"time"
99
)

Diff for: go.mod

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
module github.com/ixtendio/gow
1+
module github.com/ixtendio/gofre
22

3-
go 1.19
4-
5-
require github.com/dimfeld/httptreemux/v5 v5.4.0
3+
go 1.19

Diff for: go.sum

-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +0,0 @@
1-
github.com/dimfeld/httptreemux/v5 v5.4.0 h1:IiHYEjh+A7pYbhWyjmGnj5HZK6gpOOvyBXCJ+BE8/Gs=
2-
github.com/dimfeld/httptreemux/v5 v5.4.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=

Diff for: gow.go renamed to gofre.go

+34-33
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ import (
44
"context"
55
"expvar"
66
"fmt"
7-
"github.com/ixtendio/gow/auth"
8-
"github.com/ixtendio/gow/auth/oauth"
9-
"github.com/ixtendio/gow/errors"
10-
"github.com/ixtendio/gow/middleware"
11-
"github.com/ixtendio/gow/request"
12-
"github.com/ixtendio/gow/response"
13-
"github.com/ixtendio/gow/router"
7+
"github.com/ixtendio/gofre/auth"
8+
"github.com/ixtendio/gofre/auth/oauth"
9+
"github.com/ixtendio/gofre/errors"
10+
"github.com/ixtendio/gofre/handler"
11+
"github.com/ixtendio/gofre/middleware"
12+
"github.com/ixtendio/gofre/request"
13+
"github.com/ixtendio/gofre/response"
14+
"github.com/ixtendio/gofre/router"
1415
"html/template"
1516
"log"
1617
"math/rand"
@@ -97,7 +98,7 @@ func NewMuxHandler(config *Config) (*MuxHandler, error) {
9798
}
9899
assetsPath := config.ResourcesConfig.AssetsPath
99100
assetsDirPath := config.ResourcesConfig.AssetsDirPath
100-
r.Handle(http.MethodGet, fmt.Sprintf("%s/%s/*", contextPath, assetsPath), router.Handler2Handler(http.StripPrefix(fmt.Sprintf("%s/%s/", contextPath, assetsPath), http.FileServer(http.Dir(assetsDirPath)))))
101+
r.Handle(http.MethodGet, fmt.Sprintf("%s/%s/*", contextPath, assetsPath), handler.Handler2Handler(http.StripPrefix(fmt.Sprintf("%s/%s/", contextPath, assetsPath), http.FileServer(http.Dir(assetsDirPath)))))
101102
}
102103
return &MuxHandler{
103104
router: r,
@@ -121,12 +122,12 @@ func (m *MuxHandler) RegisterCommonMiddlewares(middlewares ...middleware.Middlew
121122
// If the OAUTH2 flow successfully completes, then the oauth.AccessToken will be passed to context.Context
122123
// to extract it, you have to use the method oauth.GetAccessTokenFromContext(context.Context)
123124

124-
func (m *MuxHandler) HandleOAUTH2(oauthConfig oauth.Config, handler router.Handler, middlewares ...middleware.Middleware) {
125+
func (m *MuxHandler) HandleOAUTH2(oauthConfig oauth.Config, handler handler.Handler, middlewares ...middleware.Middleware) {
125126
m.HandleOAUTH2WithCustomPaths("/oauth/initiate", "/oauth/authorize", oauthConfig, handler, middlewares...)
126127
}
127128

128129
// HandleOAUTH2WithCustomPaths registers the necessary handlers to initiate and complete the OAUTH2 flow using custom paths
129-
func (m *MuxHandler) HandleOAUTH2WithCustomPaths(initiatePath string, authorizeBasePath string, oauthConfig oauth.Config, handler router.Handler, middlewares ...middleware.Middleware) {
130+
func (m *MuxHandler) HandleOAUTH2WithCustomPaths(initiatePath string, authorizeBasePath string, oauthConfig oauth.Config, handler handler.Handler, middlewares ...middleware.Middleware) {
130131
cache := oauthConfig.CacheConfig.Cache
131132
// initiate OAUTH flow handler
132133
authorizationFlowBasePath := authorizeBasePath
@@ -195,73 +196,73 @@ func (m *MuxHandler) HandleOAUTH2WithCustomPaths(initiatePath string, authorizeB
195196

196197
// HandleGet registers a handler with middlewares for GET requests
197198
// The middlewares will be applied only for this handler
198-
func (m *MuxHandler) HandleGet(path string, handler router.Handler, middlewares ...middleware.Middleware) {
199+
func (m *MuxHandler) HandleGet(path string, handler handler.Handler, middlewares ...middleware.Middleware) {
199200
m.HandleRequest(http.MethodGet, path, handler, middlewares...)
200201
}
201202

202203
// HandlePost registers a handler with middlewares for POST requests
203204
// The middlewares will be applied only for this handler
204-
func (m *MuxHandler) HandlePost(path string, handler router.Handler, middlewares ...middleware.Middleware) {
205+
func (m *MuxHandler) HandlePost(path string, handler handler.Handler, middlewares ...middleware.Middleware) {
205206
m.HandleRequest(http.MethodPost, path, handler, middlewares...)
206207
}
207208

208209
// HandlePut registers a handler with middlewares for PUT requests
209210
// The middlewares will be applied only for this handler
210-
func (m *MuxHandler) HandlePut(path string, handler router.Handler, middlewares ...middleware.Middleware) {
211+
func (m *MuxHandler) HandlePut(path string, handler handler.Handler, middlewares ...middleware.Middleware) {
211212
m.HandleRequest(http.MethodPut, path, handler, middlewares...)
212213
}
213214

214215
// HandlePath registers a handler with middlewares for PATCH requests
215216
// The middlewares will be applied only for this handler
216-
func (m *MuxHandler) HandlePath(path string, handler router.Handler, middlewares ...middleware.Middleware) {
217+
func (m *MuxHandler) HandlePath(path string, handler handler.Handler, middlewares ...middleware.Middleware) {
217218
m.HandleRequest(http.MethodPatch, path, handler, middlewares...)
218219
}
219220

220221
// HandleDelete registers a handler with middlewares for DELETE requests
221222
// The middlewares will be applied only for this handler
222-
func (m *MuxHandler) HandleDelete(path string, handler router.Handler, middlewares ...middleware.Middleware) {
223+
func (m *MuxHandler) HandleDelete(path string, handler handler.Handler, middlewares ...middleware.Middleware) {
223224
m.HandleRequest(http.MethodDelete, path, handler, middlewares...)
224225
}
225226

226227
// HandleRequest registers a handler with middlewares for the specified HTTP method
227228
// The middlewares will be applied only for this handler
228-
func (m *MuxHandler) HandleRequest(httpMethod string, path string, handler router.Handler, middlewares ...middleware.Middleware) {
229-
handler = wrapMiddleware(wrapMiddleware(handler, middlewares...), m.commonMiddlewares...)
229+
func (m *MuxHandler) HandleRequest(httpMethod string, path string, h handler.Handler, middlewares ...middleware.Middleware) {
230+
h = wrapMiddleware(wrapMiddleware(h, middlewares...), m.commonMiddlewares...)
230231
var tmpl *template.Template
231232
if m.webConfig.ResourcesConfig != nil {
232233
tmpl = m.webConfig.ResourcesConfig.Template
233234
}
234235
//expose contextPath and template on request context
235-
handler = wrapMiddleware(handler, func(handler router.Handler) router.Handler {
236+
h = wrapMiddleware(h, func(h handler.Handler) handler.Handler {
236237
return func(ctx context.Context, r *request.HttpRequest) (response.HttpResponse, error) {
237238
ctx = context.WithValue(ctx, KeyValues, &CtxValues{
238239
ContextPath: m.webConfig.ContextPath,
239240
Template: tmpl,
240241
})
241-
return handler(ctx, r)
242+
return h(ctx, r)
242243
}
243244
})
244-
m.router.Handle(httpMethod, path, handler)
245+
m.router.Handle(httpMethod, path, h)
245246
}
246247

247248
// EnableDebugEndpoints enable debug endpoints
248249
func (m MuxHandler) EnableDebugEndpoints() {
249250
// Register all the standard library debug endpoints.
250-
m.router.Handle(http.MethodGet, "/debug/pprof/", router.HandlerFunc2Handler(pprof.Index))
251-
m.router.Handle(http.MethodGet, "/debug/pprof/allocs", router.HandlerFunc2Handler(pprof.Index))
252-
m.router.Handle(http.MethodGet, "/debug/pprof/block", router.HandlerFunc2Handler(pprof.Index))
253-
m.router.Handle(http.MethodGet, "/debug/pprof/goroutine", router.HandlerFunc2Handler(pprof.Index))
254-
m.router.Handle(http.MethodGet, "/debug/pprof/heap", router.HandlerFunc2Handler(pprof.Index))
255-
m.router.Handle(http.MethodGet, "/debug/pprof/mutex", router.HandlerFunc2Handler(pprof.Index))
256-
m.router.Handle(http.MethodGet, "/debug/pprof/threadcreate", router.HandlerFunc2Handler(pprof.Index))
257-
m.router.Handle(http.MethodGet, "/debug/pprof/cmdline", router.HandlerFunc2Handler(pprof.Cmdline))
258-
m.router.Handle(http.MethodGet, "/debug/pprof/profile", router.HandlerFunc2Handler(pprof.Profile))
259-
m.router.Handle(http.MethodGet, "/debug/pprof/symbol", router.HandlerFunc2Handler(pprof.Symbol))
260-
m.router.Handle(http.MethodGet, "/debug/pprof/trace", router.HandlerFunc2Handler(pprof.Trace))
261-
m.router.Handle(http.MethodGet, "/debug/vars", router.Handler2Handler(expvar.Handler()))
251+
m.router.Handle(http.MethodGet, "/debug/pprof/", handler.HandlerFunc2Handler(pprof.Index))
252+
m.router.Handle(http.MethodGet, "/debug/pprof/allocs", handler.HandlerFunc2Handler(pprof.Index))
253+
m.router.Handle(http.MethodGet, "/debug/pprof/block", handler.HandlerFunc2Handler(pprof.Index))
254+
m.router.Handle(http.MethodGet, "/debug/pprof/goroutine", handler.HandlerFunc2Handler(pprof.Index))
255+
m.router.Handle(http.MethodGet, "/debug/pprof/heap", handler.HandlerFunc2Handler(pprof.Index))
256+
m.router.Handle(http.MethodGet, "/debug/pprof/mutex", handler.HandlerFunc2Handler(pprof.Index))
257+
m.router.Handle(http.MethodGet, "/debug/pprof/threadcreate", handler.HandlerFunc2Handler(pprof.Index))
258+
m.router.Handle(http.MethodGet, "/debug/pprof/cmdline", handler.HandlerFunc2Handler(pprof.Cmdline))
259+
m.router.Handle(http.MethodGet, "/debug/pprof/profile", handler.HandlerFunc2Handler(pprof.Profile))
260+
m.router.Handle(http.MethodGet, "/debug/pprof/symbol", handler.HandlerFunc2Handler(pprof.Symbol))
261+
m.router.Handle(http.MethodGet, "/debug/pprof/trace", handler.HandlerFunc2Handler(pprof.Trace))
262+
m.router.Handle(http.MethodGet, "/debug/vars", handler.Handler2Handler(expvar.Handler()))
262263
}
263264

264-
func wrapMiddleware(handler router.Handler, middlewares ...middleware.Middleware) router.Handler {
265+
func wrapMiddleware(handler handler.Handler, middlewares ...middleware.Middleware) handler.Handler {
265266
wrappedHandlers := handler
266267
for i := len(middlewares) - 1; i >= 0; i-- {
267268
mid := middlewares[i]

Diff for: gow_test.go renamed to gofre_test.go

File renamed without changes.

Diff for: router/handler.go renamed to handler/handler.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
package router
1+
package handler
22

33
import (
44
"context"
5-
"github.com/ixtendio/gow/request"
6-
"github.com/ixtendio/gow/response"
5+
"github.com/ixtendio/gofre/request"
6+
"github.com/ixtendio/gofre/response"
77
"net/http"
88
)
99

0 commit comments

Comments
 (0)