├── examples ├── ego │ ├── public │ │ ├── css │ │ │ └── base.css │ │ ├── js │ │ │ └── base.js │ │ ├── src │ │ │ ├── icons │ │ │ │ ├── font.ttf │ │ │ │ └── icon.vgo │ │ │ └── toast │ │ │ │ ├── toast.css │ │ │ │ └── toast.js │ │ ├── head.vgo │ │ ├── head │ │ │ └── head.vgo │ │ ├── layout.html │ │ └── banner.vgo │ ├── views │ │ ├── css │ │ │ └── base.css │ │ ├── src │ │ │ ├── icons │ │ │ │ └── font.ttf │ │ │ └── toast │ │ │ │ ├── toast.css │ │ │ │ └── toast.js │ │ ├── html │ │ │ ├── head_head.html │ │ │ └── banner.html │ │ └── js │ │ │ └── ajax.js │ └── main.go ├── app-engine │ ├── app.yaml │ ├── hello.go │ └── README.md ├── grpc │ ├── README.md │ ├── grpc │ │ └── server.go │ ├── ego │ │ └── main.go │ └── pb │ │ ├── helloworld.proto │ │ └── helloworld.pb.go ├── auto-tls │ ├── example1.go │ └── example2 │ │ └── example2.go ├── basic │ ├── main_test.go │ └── main.go ├── upload-file │ ├── single │ │ ├── public │ │ │ └── index.html │ │ └── main.go │ └── multiple │ │ ├── public │ │ └── index.html │ │ └── main.go ├── realtime-advanced │ ├── rooms.go │ ├── main.go │ ├── stats.go │ ├── routes.go │ └── resources │ │ └── static │ │ ├── prismjs.min.css │ │ └── realtime.js ├── template │ └── main.go ├── realtime-chat │ ├── rooms.go │ ├── main.go │ └── template.go ├── graceful-shutdown │ ├── close │ │ └── server.go │ └── graceful-shutdown │ │ └── server.go ├── multiple-service │ └── main.go └── custom-validation │ └── server.go ├── codecov.yml ├── mid ├── rego │ ├── router.go │ ├── base.go │ ├── data.go │ ├── doc.go │ ├── fmt.go │ ├── html.go │ ├── lexer.go │ ├── uncss.go │ ├── mincss.go │ ├── min.go │ ├── compile.go │ ├── js.go │ ├── css.go │ ├── trim.go │ ├── find.go │ ├── file.go │ └── fn.go ├── binding │ ├── example │ │ ├── test.proto │ │ └── test.pb.go │ ├── query.go │ ├── xml.go │ ├── msgpack.go │ ├── protobuf.go │ ├── json.go │ ├── form.go │ ├── binding_body_test.go │ ├── default_validator.go │ └── binding.go ├── new_test.go ├── json │ ├── json.go │ └── jsoniter.go ├── render │ ├── data.go │ ├── xml.go │ ├── redirect.go │ ├── yaml.go │ ├── msgpack.go │ ├── text.go │ ├── reader.go │ ├── render.go │ ├── ffjson.go │ ├── jsoniter.go │ ├── html.go │ └── json.go ├── new.go ├── util │ ├── fs.go │ ├── path_test.go │ ├── path.go │ ├── errors_test.go │ └── errors.go ├── recovery_test.go ├── recovery.go └── logger.go ├── test ├── basic │ ├── hello.tmpl │ └── raw.tmpl ├── testdata │ ├── cert.pem │ └── key.pem └── benchmarks_test.go ├── .github ├── CONTRIBUTING.md ├── issue_template.md └── pull_request_template.md ├── img ├── head.png └── test.png ├── context_appengine.go ├── docs ├── doc.go ├── doc.md ├── CHANGELOG.md └── AUTHORS.md ├── egoS ├── README.md └── egos.go ├── circle.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── mode.go ├── recovery_test.go ├── Gopkg.toml ├── debug.go ├── auth.go ├── response_writer.go ├── recovery.go ├── debug_test.go ├── utils_test.go ├── response_writer_test.go ├── utils.go ├── logger.go ├── CONTRIBUTING.md └── routergroup_test.go /examples/ego/public/css/base.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/ego/public/js/base.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/ego/views/css/base.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # codecov: 2 | # token: -------------------------------------------------------------------------------- /mid/rego/router.go: -------------------------------------------------------------------------------- 1 | package rego 2 | -------------------------------------------------------------------------------- /test/basic/hello.tmpl: -------------------------------------------------------------------------------- 1 |

Hello {[{.name}]}

-------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Ego 2 | 3 | -------------------------------------------------------------------------------- /test/basic/raw.tmpl: -------------------------------------------------------------------------------- 1 | Date: {[{.now | formatAsDate}]} 2 | -------------------------------------------------------------------------------- /img/head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/go-ego/ego/HEAD/img/head.png -------------------------------------------------------------------------------- /img/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/go-ego/ego/HEAD/img/test.png -------------------------------------------------------------------------------- /context_appengine.go: -------------------------------------------------------------------------------- 1 | // +build appengine 2 | 3 | package ego 4 | 5 | func init() { 6 | defaultAppEngine = true 7 | } 8 | -------------------------------------------------------------------------------- /examples/ego/public/src/icons/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/go-ego/ego/HEAD/examples/ego/public/src/icons/font.ttf -------------------------------------------------------------------------------- /examples/ego/views/src/icons/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/go-ego/ego/HEAD/examples/ego/views/src/icons/font.ttf -------------------------------------------------------------------------------- /docs/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package ego Go open-source, high-performance, modular, full-stack web framework. 3 | 4 | Please make sure 5 | */ 6 | 7 | package ego 8 | -------------------------------------------------------------------------------- /examples/app-engine/app.yaml: -------------------------------------------------------------------------------- 1 | application: hello 2 | version: 1 3 | runtime: go 4 | api_version: go1 5 | 6 | handlers: 7 | - url: /.* 8 | script: _go_app -------------------------------------------------------------------------------- /examples/ego/public/head.vgo: -------------------------------------------------------------------------------- 1 | 2 | import ( 3 | "icons" 4 | icon "icons/icon.vgo" 5 | ) 6 | 7 |
8 |

{title}

9 |

{ahead}

10 | 11 | 12 | ... 13 | 14 | 15 | 16 |
17 | 18 | // 19 | 20 | 21 | ... 22 | -------------------------------------------------------------------------------- /mid/binding/example/test.proto: -------------------------------------------------------------------------------- 1 | package example; 2 | 3 | enum FOO {X=17;}; 4 | 5 | message Test { 6 | required string label = 1; 7 | optional int32 type = 2[default=77]; 8 | repeated int64 reps = 3; 9 | optional group OptionalGroup = 4{ 10 | required string RequiredField = 5; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /egoS/README.md: -------------------------------------------------------------------------------- 1 | # Ego Default Server 2 | 3 | This is API experiment for Ego. 4 | 5 | ```go 6 | package main 7 | 8 | import ( 9 | "github.com/go-ego/ego" 10 | "github.com/go-ego/ego/egoS" 11 | ) 12 | 13 | func main() { 14 | egoS.GET("/", func(c *ego.Context) { c.String(200, "Hello World") }) 15 | egoS.Run() 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /examples/grpc/README.md: -------------------------------------------------------------------------------- 1 | ## How to run this example 2 | 3 | 1. run grpc server 4 | 5 | ```sh 6 | $ go run grpc/server.go 7 | ``` 8 | 9 | 2. run ego server 10 | 11 | ```sh 12 | $ go run ego/main.go 13 | ``` 14 | 15 | 3. use curl command to test it 16 | 17 | ```sh 18 | $ curl -v 'http://localhost:8052/rest/n/thinkerou' 19 | ``` 20 | -------------------------------------------------------------------------------- /examples/ego/public/src/icons/icon.vgo: -------------------------------------------------------------------------------- 1 | // pkg icon 2 | 3 |
4 | 5 |

{prpo}

6 |
7 | 8 | 23 | -------------------------------------------------------------------------------- /examples/auto-tls/example1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/go-ego/autotls" 7 | "github.com/go-ego/ego" 8 | ) 9 | 10 | func main() { 11 | r := ego.Default() 12 | 13 | // Ping handler 14 | r.GET("/ping", func(c *ego.Context) { 15 | c.String(200, "pong") 16 | }) 17 | 18 | log.Fatal(autotls.Run(r, "example1.com", "example2.com")) 19 | } 20 | -------------------------------------------------------------------------------- /docs/doc.md: -------------------------------------------------------------------------------- 1 | ## import 2 | 3 | ```html 4 | 5 | import ( 6 | "icons" 7 | icon "icons/icon.vgo" 8 | ) 9 | 10 |
11 |
ego:{{.head}}
12 | 13 | 14 | vclass={icon-share-to} 15 | node={ id="slot1"} 16 | prpo={node---1} 17 | 18 | 19 |
20 | {{range .parArr}} 21 |

arr::: {{.}}

22 | {{end}} 23 |
24 | 25 |
26 | ``` -------------------------------------------------------------------------------- /examples/ego/public/head/head.vgo: -------------------------------------------------------------------------------- 1 | // pkg head 2 | 3 | import ( 4 | "icons" 5 | icon "icons/icon.vgo" 6 | ) 7 | 8 |
9 |
ego:{{.head}}
10 | 11 | 12 | vclass={icon-share-to} 13 | node={ id="slot1"} 14 | prpo={node---1} 15 | 16 | 17 |
18 | {{range .parArr}} 19 |

arr::: {{.}}

20 | {{end}} 21 |
22 | 23 |
24 | -------------------------------------------------------------------------------- /examples/ego/public/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | my-ego 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /mid/new_test.go: -------------------------------------------------------------------------------- 1 | package mid 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/go-ego/ego" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCreateDefaultRouter(t *testing.T) { 11 | router := ego.Default() 12 | assert.Len(t, router.Handlers, 2) 13 | } 14 | 15 | func TestCreateClassicRouter(t *testing.T) { 16 | router := ego.Classic() 17 | assert.Len(t, router.Handlers, 2) 18 | } 19 | -------------------------------------------------------------------------------- /mid/json/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Bo-Yi Wu. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !jsoniter 6 | 7 | package json 8 | 9 | import ( 10 | "encoding/json" 11 | ) 12 | 13 | var ( 14 | Marshal = json.Marshal 15 | MarshalIndent = json.MarshalIndent 16 | NewDecoder = json.NewDecoder 17 | ) 18 | -------------------------------------------------------------------------------- /examples/basic/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestPingRoute(t *testing.T) { 12 | router := setupRouter() 13 | 14 | w := httptest.NewRecorder() 15 | req, _ := http.NewRequest("GET", "/ping", nil) 16 | router.ServeHTTP(w, req) 17 | 18 | assert.Equal(t, 200, w.Code) 19 | assert.Equal(t, "pong", w.Body.String()) 20 | } 21 | -------------------------------------------------------------------------------- /mid/json/jsoniter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Bo-Yi Wu. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build jsoniter 6 | 7 | package json 8 | 9 | import ( 10 | "github.com/json-iterator/go" 11 | ) 12 | 13 | var ( 14 | json = jsoniter.ConfigCompatibleWithStandardLibrary 15 | Marshal = json.Marshal 16 | MarshalIndent = json.MarshalIndent 17 | NewDecoder = json.NewDecoder 18 | ) 19 | -------------------------------------------------------------------------------- /examples/upload-file/single/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Single file upload 6 | 7 | 8 |

Upload single file with fields

9 | 10 |
11 | Name:
12 | Email:
13 | Files:

14 | 15 |
16 | 17 | -------------------------------------------------------------------------------- /mid/rego/base.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | -------------------------------------------------------------------------------- /mid/rego/data.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | -------------------------------------------------------------------------------- /mid/rego/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | -------------------------------------------------------------------------------- /mid/rego/fmt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | -------------------------------------------------------------------------------- /mid/rego/html.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | -------------------------------------------------------------------------------- /mid/rego/lexer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | -------------------------------------------------------------------------------- /mid/rego/uncss.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | -------------------------------------------------------------------------------- /mid/rego/mincss.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | -------------------------------------------------------------------------------- /examples/upload-file/multiple/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Multiple file upload 6 | 7 | 8 |

Upload multiple files with fields

9 | 10 |
11 | Name:
12 | Email:
13 | Files:

14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /mid/binding/query.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import "net/http" 8 | 9 | type queryBinding struct{} 10 | 11 | func (queryBinding) Name() string { 12 | return "query" 13 | } 14 | 15 | func (queryBinding) Bind(req *http.Request, obj interface{}) error { 16 | values := req.URL.Query() 17 | if err := mapForm(obj, values); err != nil { 18 | return err 19 | } 20 | return validate(obj) 21 | } 22 | -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## Ego 0.10.0, Nile River 4 | 5 | ### Add 6 | 7 | - [NEW] Add TestFile 8 | - [NEW] Add \*context.ShouldBindWith() 9 | - [NEW] Add custom template delimiters 10 | - [NEW] Add more test: Add middleware_test and other 11 | - [NEW] Add circleci 12 | 13 | ### Update 14 | 15 | - Update vendor.json 16 | - Update name and refactoring 17 | - Update examples json.html 18 | - Update Build-tools name 19 | - Update and Cleanup README.md 20 | - Move changelog.md and authors.md 21 | 22 | ### Fix 23 | 24 | - Fix handlers registered 25 | - Fix time binding 26 | 27 | -------------------------------------------------------------------------------- /examples/app-engine/hello.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/go-ego/ego" 7 | ) 8 | 9 | // This function's name is a must. App engine uses it to drive the requests properly. 10 | func init() { 11 | // Starts a new ego instance with no middle-ware 12 | r := ego.New() 13 | 14 | // Define your handlers 15 | r.GET("/", func(c *ego.Context) { 16 | c.String(http.StatusOK, "Hello World!") 17 | }) 18 | r.GET("/ping", func(c *ego.Context) { 19 | c.String(http.StatusOK, "pong") 20 | }) 21 | 22 | // Handle all requests using net/http 23 | http.Handle("/", r) 24 | } 25 | -------------------------------------------------------------------------------- /docs/AUTHORS.md: -------------------------------------------------------------------------------- 1 | List of all the awesome people working to make Ego the best Web Framework in Go. 2 | 3 | @[vz](https://github.com/vcaesar) 4 | 5 | See [contributors page](https://github.com/go-ego/ego/graphs/contributors)for full list of contributors 6 | 7 | 8 | List of all the awesome people working to make Gin the best Web Framework in Go. 9 | 10 | ## gin 0.x series authors 11 | 12 | **Maintainer:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho) 13 | 14 | See [contributors page](https://github.com/gin-gonic/gin/graphs/contributors)for full list of contributors. 15 | 16 | -------------------------------------------------------------------------------- /examples/auto-tls/example2/example2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/go-ego/autotls" 7 | "github.com/go-ego/ego" 8 | "golang.org/x/crypto/acme/autocert" 9 | ) 10 | 11 | func main() { 12 | r := ego.Default() 13 | 14 | // Ping handler 15 | r.GET("/ping", func(c *ego.Context) { 16 | c.String(200, "pong") 17 | }) 18 | 19 | m := autocert.Manager{ 20 | Prompt: autocert.AcceptTOS, 21 | HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"), 22 | Cache: autocert.DirCache("/var/www/.cache"), 23 | } 24 | 25 | log.Fatal(autotls.RunWithManager(r, &m)) 26 | } 27 | -------------------------------------------------------------------------------- /mid/render/data.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import "net/http" 8 | 9 | type Data struct { 10 | ContentType string 11 | Data []byte 12 | } 13 | 14 | // Render (Data) writes data with custom ContentType. 15 | func (r Data) Render(w http.ResponseWriter) (err error) { 16 | r.WriteContentType(w) 17 | _, err = w.Write(r.Data) 18 | return 19 | } 20 | 21 | func (r Data) WriteContentType(w http.ResponseWriter) { 22 | writeContentType(w, []string{r.ContentType}) 23 | } 24 | -------------------------------------------------------------------------------- /examples/app-engine/README.md: -------------------------------------------------------------------------------- 1 | # Guide to run ego under App Engine LOCAL Development Server 2 | 3 | 1. Download, install and setup Go in your computer. (That includes setting your `$GOPATH`.) 4 | 2. Download SDK for your platform from [here](https://cloud.google.com/appengine/docs/standard/go/download): `https://cloud.google.com/appengine/docs/standard/go/download` 5 | 3. Download ego source code using: `$ go get github.com/go-ego/ego` 6 | 4. Navigate to examples folder: `$ cd $GOPATH/src/github.com/go-ego/ego/examples//app-engine` 7 | 8 | 5. Run it: `$ dev_appserver.py .` (notice that you have to run this script by Python2) -------------------------------------------------------------------------------- /mid/render/xml.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "encoding/xml" 9 | "net/http" 10 | ) 11 | 12 | type XML struct { 13 | Data interface{} 14 | } 15 | 16 | var xmlContentType = []string{"application/xml; charset=utf-8"} 17 | 18 | func (r XML) Render(w http.ResponseWriter) error { 19 | r.WriteContentType(w) 20 | return xml.NewEncoder(w).Encode(r.Data) 21 | } 22 | 23 | func (r XML) WriteContentType(w http.ResponseWriter) { 24 | writeContentType(w, xmlContentType) 25 | } 26 | -------------------------------------------------------------------------------- /examples/realtime-advanced/rooms.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/dustin/go-broadcast" 4 | 5 | var roomChannels = make(map[string]broadcast.Broadcaster) 6 | 7 | func openListener(roomid string) chan interface{} { 8 | listener := make(chan interface{}) 9 | room(roomid).Register(listener) 10 | return listener 11 | } 12 | 13 | func closeListener(roomid string, listener chan interface{}) { 14 | room(roomid).Unregister(listener) 15 | close(listener) 16 | } 17 | 18 | func room(roomid string) broadcast.Broadcaster { 19 | b, ok := roomChannels[roomid] 20 | if !ok { 21 | b = broadcast.NewBroadcaster(10) 22 | roomChannels[roomid] = b 23 | } 24 | return b 25 | } 26 | -------------------------------------------------------------------------------- /mid/new.go: -------------------------------------------------------------------------------- 1 | package mid 2 | 3 | import ( 4 | "github.com/go-ego/ego" 5 | // "github.com/go-ego/ego/mid/logger" 6 | ) 7 | 8 | // Classic returns an Engine instance with the Logger and Recovery middleware already attached. 9 | func Classic() *ego.Engine { 10 | engine := ego.New() 11 | engine.Use(Logger(), Recovery()) 12 | // engine.Use(logger.Logger(), logger.Recovery()) 13 | return engine 14 | } 15 | 16 | // Default returns an Engine instance with the Logger and Recovery middleware already attached. 17 | func Default() *ego.Engine { 18 | engine := ego.New() 19 | engine.Use(Logger(), Recovery()) 20 | // engine.Use(logger.Logger(), logger.Recovery()) 21 | return engine 22 | } 23 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | # circle.yml # 2 | # machine: 3 | # go: 4 | # version: 1.9.1 5 | 6 | version: 2 7 | 8 | jobs: 9 | build: 10 | docker: 11 | - image: govgo/go:1.10.3 12 | working_directory: /gopath/src/github.com/go-ego/ego 13 | steps: 14 | - checkout 15 | # specify any bash command here prefixed with `run: ` 16 | - run: go get -v -t -d ./... 17 | - run: go test -v ./... 18 | # codecov.io 19 | - run: go test -v -covermode=count -coverprofile=coverage.out 20 | - run: bash <(curl -s https://codecov.io/bash) 21 | 22 | # test: 23 | # post: 24 | # - go test -v -covermode=count -coverprofile=coverage.out 25 | # - bash <(curl -s https://codecov.io/bash) 26 | -------------------------------------------------------------------------------- /mid/render/redirect.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "fmt" 9 | "net/http" 10 | ) 11 | 12 | type Redirect struct { 13 | Code int 14 | Request *http.Request 15 | Location string 16 | } 17 | 18 | func (r Redirect) Render(w http.ResponseWriter) error { 19 | if (r.Code < 300 || r.Code > 308) && r.Code != 201 { 20 | panic(fmt.Sprintf("Cannot redirect with status code %d", r.Code)) 21 | } 22 | http.Redirect(w, r.Request, r.Location, r.Code) 23 | return nil 24 | } 25 | 26 | func (r Redirect) WriteContentType(http.ResponseWriter) {} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | .DS_Store 3 | .vscode 4 | .idea 5 | testf 6 | 7 | vendor 8 | 9 | # Debug files 10 | *.dSYM/ 11 | *.su 12 | debug 13 | 14 | # Architecture specific extensions/prefixes 15 | *.[568vq] 16 | [568vq].out 17 | 18 | *.cgo1.go 19 | *.cgo2.c 20 | _cgo_defun.c 21 | _cgo_gotypes.go 22 | _cgo_export.* 23 | 24 | _testmain.go 25 | 26 | *.o 27 | *.ko 28 | *.obj 29 | *.elf 30 | 31 | # Precompiled Headers 32 | *.gch 33 | *.pch 34 | 35 | # Libraries 36 | *.lib 37 | *.a 38 | *.la 39 | *.lo 40 | 41 | # Shared objects (inc. Windows DLLs) 42 | *.dll 43 | *.so 44 | *.so.* 45 | *.dylib 46 | 47 | # Executables 48 | *.exe 49 | *.out 50 | *.app 51 | *.i*86 52 | *.x86_64 53 | *.hex 54 | -------------------------------------------------------------------------------- /examples/ego/public/src/toast/toast.css: -------------------------------------------------------------------------------- 1 | .toast-container { 2 | position: fixed; 3 | z-index: 9999; 4 | /* bottom: 60px; */ 5 | top: 10rem; 6 | width: 100%; 7 | -webkit-transition: opacity .8s; 8 | transition: opacity .8s; 9 | opacity: 0; 10 | } 11 | 12 | .toast-container.active { 13 | opacity: 0.8; 14 | } 15 | 16 | .toast-msg { 17 | width: 5.5rem; 18 | height: 5.5rem; 19 | margin: 2px auto; 20 | padding: 10px; 21 | text-align: center; 22 | border-radius: 7px; 23 | background-color: #343434; 24 | color: #ffffff; 25 | font-size: 2.6rem; 26 | /* background-color: #d8d8d8; 27 | color: #555; */ 28 | } 29 | 30 | .toast-text { 31 | font-size: 1.8rem; 32 | margin-top: 8px; 33 | } 34 | -------------------------------------------------------------------------------- /examples/ego/views/src/toast/toast.css: -------------------------------------------------------------------------------- 1 | .toast-container { 2 | position: fixed; 3 | z-index: 9999; 4 | /* bottom: 60px; */ 5 | top: 10rem; 6 | width: 100%; 7 | -webkit-transition: opacity .8s; 8 | transition: opacity .8s; 9 | opacity: 0; 10 | } 11 | 12 | .toast-container.active { 13 | opacity: 0.8; 14 | } 15 | 16 | .toast-msg { 17 | width: 5.5rem; 18 | height: 5.5rem; 19 | margin: 2px auto; 20 | padding: 10px; 21 | text-align: center; 22 | border-radius: 7px; 23 | background-color: #343434; 24 | color: #ffffff; 25 | font-size: 2.6rem; 26 | /* background-color: #d8d8d8; 27 | color: #555; */ 28 | } 29 | 30 | .toast-text { 31 | font-size: 1.8rem; 32 | margin-top: 8px; 33 | } 34 | -------------------------------------------------------------------------------- /examples/template/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "html/template" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/go-ego/ego" 10 | ) 11 | 12 | func formatAsDate(t time.Time) string { 13 | year, month, day := t.Date() 14 | return fmt.Sprintf("%d%02d/%02d", year, month, day) 15 | } 16 | 17 | func main() { 18 | router := ego.Default() 19 | router.Delims("{[{", "}]}") 20 | router.SetFuncMap(template.FuncMap{ 21 | "formatAsDate": formatAsDate, 22 | }) 23 | router.LoadHTMLFiles("../../test/basic/raw.tmpl") 24 | 25 | router.GET("/raw", func(c *ego.Context) { 26 | c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{ 27 | "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), 28 | }) 29 | }) 30 | 31 | router.Run(":8080") 32 | } 33 | -------------------------------------------------------------------------------- /mid/render/yaml.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "net/http" 9 | 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | type YAML struct { 14 | Data interface{} 15 | } 16 | 17 | var yamlContentType = []string{"application/x-yaml; charset=utf-8"} 18 | 19 | func (r YAML) Render(w http.ResponseWriter) error { 20 | r.WriteContentType(w) 21 | 22 | bytes, err := yaml.Marshal(r.Data) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | w.Write(bytes) 28 | return nil 29 | } 30 | 31 | func (r YAML) WriteContentType(w http.ResponseWriter) { 32 | writeContentType(w, yamlContentType) 33 | } 34 | -------------------------------------------------------------------------------- /examples/realtime-chat/rooms.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/dustin/go-broadcast" 4 | 5 | var roomChannels = make(map[string]broadcast.Broadcaster) 6 | 7 | func openListener(roomid string) chan interface{} { 8 | listener := make(chan interface{}) 9 | room(roomid).Register(listener) 10 | return listener 11 | } 12 | 13 | func closeListener(roomid string, listener chan interface{}) { 14 | room(roomid).Unregister(listener) 15 | close(listener) 16 | } 17 | 18 | func deleteBroadcast(roomid string) { 19 | b, ok := roomChannels[roomid] 20 | if ok { 21 | b.Close() 22 | delete(roomChannels, roomid) 23 | } 24 | } 25 | 26 | func room(roomid string) broadcast.Broadcaster { 27 | b, ok := roomChannels[roomid] 28 | if !ok { 29 | b = broadcast.NewBroadcaster(10) 30 | roomChannels[roomid] = b 31 | } 32 | return b 33 | } 34 | -------------------------------------------------------------------------------- /mid/binding/xml.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import ( 8 | "bytes" 9 | "encoding/xml" 10 | "io" 11 | "net/http" 12 | ) 13 | 14 | type xmlBinding struct{} 15 | 16 | func (xmlBinding) Name() string { 17 | return "xml" 18 | } 19 | 20 | func (xmlBinding) Bind(req *http.Request, obj interface{}) error { 21 | return decodeXML(req.Body, obj) 22 | } 23 | 24 | func (xmlBinding) BindBody(body []byte, obj interface{}) error { 25 | return decodeXML(bytes.NewReader(body), obj) 26 | } 27 | func decodeXML(r io.Reader, obj interface{}) error { 28 | decoder := xml.NewDecoder(r) 29 | if err := decoder.Decode(obj); err != nil { 30 | return err 31 | } 32 | return validate(obj) 33 | } 34 | -------------------------------------------------------------------------------- /examples/realtime-advanced/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | "github.com/go-ego/ego" 8 | ) 9 | 10 | func main() { 11 | ConfigRuntime() 12 | StartWorkers() 13 | StartEgo() 14 | } 15 | 16 | func ConfigRuntime() { 17 | nuCPU := runtime.NumCPU() 18 | runtime.GOMAXPROCS(nuCPU) 19 | fmt.Printf("Running with %d CPUs\n", nuCPU) 20 | } 21 | 22 | func StartWorkers() { 23 | go statsWorker() 24 | } 25 | 26 | func StartEgo() { 27 | ego.SetMode(ego.ReleaseMode) 28 | 29 | router := ego.New() 30 | router.Use(rateLimit, ego.Recovery()) 31 | router.LoadHTMLGlob("resources/*.templ.html") 32 | router.Static("/static", "resources/static") 33 | router.GET("/", index) 34 | router.GET("/room/:roomid", roomGET) 35 | router.POST("/room-post/:roomid", roomPOST) 36 | router.GET("/stream/:roomid", streamRoom) 37 | 38 | router.Run(":80") 39 | } 40 | -------------------------------------------------------------------------------- /mid/render/msgpack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "net/http" 9 | 10 | "github.com/ugorji/go/codec" 11 | ) 12 | 13 | type MsgPack struct { 14 | Data interface{} 15 | } 16 | 17 | var msgpackContentType = []string{"application/msgpack; charset=utf-8"} 18 | 19 | func (r MsgPack) WriteContentType(w http.ResponseWriter) { 20 | writeContentType(w, msgpackContentType) 21 | } 22 | 23 | func (r MsgPack) Render(w http.ResponseWriter) error { 24 | return WriteMsgPack(w, r.Data) 25 | } 26 | 27 | func WriteMsgPack(w http.ResponseWriter, obj interface{}) error { 28 | writeContentType(w, msgpackContentType) 29 | var h codec.Handle = new(codec.MsgpackHandle) 30 | return codec.NewEncoder(w, h).Encode(obj) 31 | } 32 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 1. Please speak English, this is the language everybody of us can speak and write. 2 | 2. Please ask questions or config/deploy problems on our Gitter channel: https://gitter.im/go-ego/ego 3 | 3. Please take a moment to search that an issue doesn't already exist. 4 | 4. Please give all relevant information below for bug reports, incomplete details will be handled as an invalid report. 5 | 6 | **You MUST delete the content above including this line before posting, otherwise your issue will be invalid.** 7 | 8 | - Ego version (or commit ref): 9 | - Go version: 10 | - Gcc version: 11 | - Operating system and bit: 12 | - Can you reproduce the bug at [Examples](https://github.com/go-ego/ego/tree/master/examples): 13 | - [ ] Yes (provide example code) 14 | - [ ] No 15 | - [ ] Not relevant 16 | - Provide example code: 17 | ```Go 18 | 19 | ``` 20 | - Log gist: 21 | 22 | ## Description 23 | 24 | ... 25 | -------------------------------------------------------------------------------- /mid/render/text.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "net/http" 11 | ) 12 | 13 | type String struct { 14 | Format string 15 | Data []interface{} 16 | } 17 | 18 | var plainContentType = []string{"text/plain; charset=utf-8"} 19 | 20 | func (r String) Render(w http.ResponseWriter) error { 21 | WriteString(w, r.Format, r.Data) 22 | return nil 23 | } 24 | 25 | func (r String) WriteContentType(w http.ResponseWriter) { 26 | writeContentType(w, plainContentType) 27 | } 28 | 29 | func WriteString(w http.ResponseWriter, format string, data []interface{}) { 30 | writeContentType(w, plainContentType) 31 | if len(data) > 0 { 32 | fmt.Fprintf(w, format, data...) 33 | } else { 34 | io.WriteString(w, format) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/ego/public/banner.vgo: -------------------------------------------------------------------------------- 1 | import ( 2 | "toast" 3 | head "head.vgo" 4 | 5 | "icons" 6 | icon "icons/icon.vgo" 7 | ) 8 | 9 | 33 | 34 | 37 | 38 | 52 | 53 | -------------------------------------------------------------------------------- /mid/binding/msgpack.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "net/http" 11 | 12 | "github.com/ugorji/go/codec" 13 | ) 14 | 15 | type msgpackBinding struct{} 16 | 17 | func (msgpackBinding) Name() string { 18 | return "msgpack" 19 | } 20 | 21 | func (msgpackBinding) Bind(req *http.Request, obj interface{}) error { 22 | return decodeMsgPack(req.Body, obj) 23 | } 24 | 25 | func (msgpackBinding) BindBody(body []byte, obj interface{}) error { 26 | return decodeMsgPack(bytes.NewReader(body), obj) 27 | } 28 | 29 | func decodeMsgPack(r io.Reader, obj interface{}) error { 30 | cdc := new(codec.MsgpackHandle) 31 | if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil { 32 | return err 33 | } 34 | return validate(obj) 35 | } 36 | -------------------------------------------------------------------------------- /mid/render/reader.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "strconv" 7 | ) 8 | 9 | type Reader struct { 10 | ContentType string 11 | ContentLength int64 12 | Reader io.Reader 13 | Headers map[string]string 14 | } 15 | 16 | // Render (Reader) writes data with custom ContentType and headers. 17 | func (r Reader) Render(w http.ResponseWriter) (err error) { 18 | r.WriteContentType(w) 19 | r.Headers["Content-Length"] = strconv.FormatInt(r.ContentLength, 10) 20 | r.writeHeaders(w, r.Headers) 21 | _, err = io.Copy(w, r.Reader) 22 | return 23 | } 24 | 25 | func (r Reader) WriteContentType(w http.ResponseWriter) { 26 | writeContentType(w, []string{r.ContentType}) 27 | } 28 | 29 | func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) { 30 | header := w.Header() 31 | for k, v := range headers { 32 | if val := header[k]; len(val) == 0 { 33 | header[k] = []string{v} 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /examples/grpc/grpc/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net" 6 | 7 | pb "github.com/go-ego/ego/examples/grpc/pb" 8 | "golang.org/x/net/context" 9 | "google.golang.org/grpc" 10 | "google.golang.org/grpc/reflection" 11 | ) 12 | 13 | // server is used to implement helloworld.GreeterServer. 14 | type server struct{} 15 | 16 | // SayHello implements helloworld.GreeterServer 17 | func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { 18 | return &pb.HelloReply{Message: "Hello " + in.Name}, nil 19 | } 20 | 21 | func main() { 22 | lis, err := net.Listen("tcp", ":50051") 23 | if err != nil { 24 | log.Fatalf("failed to listen: %v", err) 25 | } 26 | s := grpc.NewServer() 27 | pb.RegisterGreeterServer(s, &server{}) 28 | 29 | // Register reflection service on gRPC server. 30 | reflection.Register(s) 31 | if err := s.Serve(lis); err != nil { 32 | log.Fatalf("failed to serve: %v", err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 4 | # - 1.7.x 5 | - 1.8.x 6 | - 1.9.x 7 | - 1.10.x 8 | - tip 9 | 10 | # env: 11 | # global: 12 | # - GO15VENDOREXPERIMENT=1 13 | # matrix: 14 | # - TARGET=amd64 15 | # - TARGET=arm64 16 | # - TARGET=arm 17 | # - TARGET=386 18 | # - TARGET=ppc64le 19 | go_import_path: github.com/go-ego/ego 20 | 21 | before_install: 22 | - export PATH=$PATH:$HOME/gopath/bin 23 | - go get -t -v ./... 24 | 25 | # script: 26 | # # - ./go.test.sh 27 | # - go test -v -covermode=count -coverprofile=coverage.out 28 | 29 | 30 | # after_success: 31 | # - bash <(curl -s https://codecov.io/bash) 32 | 33 | ### 34 | # notifications: 35 | # webhooks: 36 | # urls: 37 | # - https://webhooks.gitter.im/e/777 38 | # on_success: change # options: [always|never|change] default: always 39 | # on_failure: always # options: [always|never|change] default: always 40 | # on_start: false # default: false -------------------------------------------------------------------------------- /examples/graceful-shutdown/close/server.go: -------------------------------------------------------------------------------- 1 | // +build go1.8 2 | 3 | package main 4 | 5 | import ( 6 | "log" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | 11 | "github.com/go-ego/ego" 12 | ) 13 | 14 | func main() { 15 | router := ego.Default() 16 | router.GET("/", func(c *ego.Context) { 17 | c.String(http.StatusOK, "Welcome ego Server") 18 | }) 19 | 20 | server := &http.Server{ 21 | Addr: ":8080", 22 | Handler: router, 23 | } 24 | 25 | quit := make(chan os.Signal) 26 | signal.Notify(quit, os.Interrupt) 27 | 28 | go func() { 29 | <-quit 30 | log.Println("receive interrupt signal") 31 | if err := server.Close(); err != nil { 32 | log.Fatal("Server Close:", err) 33 | } 34 | }() 35 | 36 | if err := server.ListenAndServe(); err != nil { 37 | if err == http.ErrServerClosed { 38 | log.Println("Server closed under request") 39 | } else { 40 | log.Fatal("Server closed unexpect") 41 | } 42 | } 43 | 44 | log.Println("Server exiting") 45 | } 46 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | The pull request will be closed without any reasons if it does not satisfy any of following requirements: 2 | 3 | 1. Make sure you are targeting the `master` branch, pull requests on release branches are only allowed for bug fixes. 4 | 2. Please read contributing guidelines: [CONTRIBUTING](https://github.com/go-ego/ego/blob/master/CONTRIBUTING.md) 5 | 3. Describe what your pull request does and which issue you're targeting (if any and Please use English) 6 | 4. ... if it is not related to any particular issues, explain why we should not reject your pull request. 7 | 5. The Commits must use English, must be test and No useless submissions. 8 | 9 | **You MUST delete the content above including this line before posting, otherwise your pull request will be invalid.** 10 | 11 | **Please provide Issues links to:** 12 | 13 | - Issues: #1 14 | 15 | **Provide test code:** 16 | 17 | ```Go 18 | 19 | ``` 20 | 21 | ## Description 22 | 23 | ... 24 | -------------------------------------------------------------------------------- /examples/upload-file/single/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/go-ego/ego" 8 | ) 9 | 10 | func main() { 11 | router := ego.Default() 12 | // Set a lower memory limit for multipart forms (default is 32 MiB) 13 | router.MaxMultipartMemory = 8 << 20 // 8 MiB 14 | router.Static("/", "./public") 15 | router.POST("/upload", func(c *ego.Context) { 16 | name := c.PostForm("name") 17 | email := c.PostForm("email") 18 | 19 | // Source 20 | file, err := c.FormFile("file") 21 | if err != nil { 22 | c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error())) 23 | return 24 | } 25 | 26 | if err := c.SaveUploadedFile(file, file.Filename); err != nil { 27 | c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error())) 28 | return 29 | } 30 | 31 | c.String(http.StatusOK, fmt.Sprintf("File %s uploaded successfully with fields name=%s and email=%s.", file.Filename, name, email)) 32 | }) 33 | router.Run(":8080") 34 | } 35 | -------------------------------------------------------------------------------- /mid/binding/protobuf.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import ( 8 | "io/ioutil" 9 | "net/http" 10 | 11 | "github.com/golang/protobuf/proto" 12 | ) 13 | 14 | type protobufBinding struct{} 15 | 16 | func (protobufBinding) Name() string { 17 | return "protobuf" 18 | } 19 | 20 | func (b protobufBinding) Bind(req *http.Request, obj interface{}) error { 21 | buf, err := ioutil.ReadAll(req.Body) 22 | if err != nil { 23 | return err 24 | } 25 | return b.BindBody(buf, obj) 26 | } 27 | 28 | func (protobufBinding) BindBody(body []byte, obj interface{}) error { 29 | if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil { 30 | return err 31 | } 32 | // Here it's same to return validate(obj), but util now we cann't add 33 | // `binding:""` to the struct which automatically generate by gen-proto 34 | return nil 35 | // return validate(obj) 36 | } 37 | -------------------------------------------------------------------------------- /examples/upload-file/multiple/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | "github.com/go-ego/ego" 8 | ) 9 | 10 | func main() { 11 | router := ego.Default() 12 | // Set a lower memory limit for multipart forms (default is 32 MiB) 13 | router.MaxMultipartMemory = 8 << 20 // 8 MiB 14 | router.Static("/", "./public") 15 | router.POST("/upload", func(c *ego.Context) { 16 | name := c.PostForm("name") 17 | email := c.PostForm("email") 18 | 19 | // Multipart form 20 | form, err := c.MultipartForm() 21 | if err != nil { 22 | c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error())) 23 | return 24 | } 25 | files := form.File["files"] 26 | 27 | for _, file := range files { 28 | if err := c.SaveUploadedFile(file, file.Filename); err != nil { 29 | c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error())) 30 | return 31 | } 32 | } 33 | 34 | c.String(http.StatusOK, fmt.Sprintf("Uploaded successfully %d files with fields name=%s and email=%s.", len(files), name, email)) 35 | }) 36 | router.Run(":8080") 37 | } 38 | -------------------------------------------------------------------------------- /mid/render/render.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import "net/http" 8 | 9 | type Render interface { 10 | Render(http.ResponseWriter) error 11 | WriteContentType(w http.ResponseWriter) 12 | } 13 | 14 | var ( 15 | _ Render = JSON{} 16 | _ Render = IJSON{} 17 | _ Render = FFJSON{} 18 | _ Render = IndentedJSON{} 19 | _ Render = SecureJSON{} 20 | _ Render = JsonpJSON{} 21 | _ Render = XML{} 22 | _ Render = String{} 23 | _ Render = Redirect{} 24 | _ Render = Data{} 25 | _ Render = HTML{} 26 | _ HTMLRender = HTMLDebug{} 27 | _ HTMLRender = HTMLProduction{} 28 | _ Render = YAML{} 29 | _ Render = MsgPack{} 30 | _ Render = Reader{} 31 | _ Render = AsciiJSON{} 32 | ) 33 | 34 | func writeContentType(w http.ResponseWriter, value []string) { 35 | header := w.Header() 36 | if val := header["Content-Type"]; len(val) == 0 { 37 | header["Content-Type"] = value 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 go-ego Project Developers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/grpc/ego/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | 8 | "github.com/go-ego/ego" 9 | pb "github.com/go-ego/ego/examples/grpc/pb" 10 | "google.golang.org/grpc" 11 | ) 12 | 13 | func main() { 14 | // Set up a connection to the server. 15 | conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure()) 16 | if err != nil { 17 | log.Fatalf("did not connect: %v", err) 18 | } 19 | defer conn.Close() 20 | client := pb.NewGreeterClient(conn) 21 | 22 | // Set up a http setver. 23 | r := ego.Default() 24 | r.GET("/rest/n/:name", func(c *ego.Context) { 25 | name := c.Param("name") 26 | 27 | // Contact the server and print out its response. 28 | req := &pb.HelloRequest{Name: name} 29 | res, err := client.SayHello(c, req) 30 | if err != nil { 31 | c.JSON(http.StatusInternalServerError, ego.Map{ 32 | "error": err.Error(), 33 | }) 34 | return 35 | } 36 | 37 | c.JSON(http.StatusOK, ego.Map{ 38 | "result": fmt.Sprint(res.Message), 39 | }) 40 | }) 41 | 42 | // Run http server 43 | if err := r.Run(":8052"); err != nil { 44 | log.Fatalf("could not run server: %v", err) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/testdata/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC9DCCAdygAwIBAgIQUNSK+OxWHYYFxHVJV0IlpDANBgkqhkiG9w0BAQsFADAS 3 | MRAwDgYDVQQKEwdBY21lIENvMB4XDTE3MTExNjEyMDA0N1oXDTE4MTExNjEyMDA0 4 | N1owEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC 5 | AQoCggEBAKmyj/YZpD59Bpy4w3qf6VzMw9uUBsWp+IP4kl7z5cmGHYUHn/YopTLH 6 | vR23GAB12p6Km5QWzCBuJF4j61PJXHfg3/rjShZ77JcQ3kzxuy1iKDI+DNKN7Klz 7 | rdjJ49QD0lczZHeBvvCL7JsJFKFjGy62rnStuW8LaIEdtjXT+GUZTxJh6G7yPYfD 8 | MS1IsdMQGOdbGwNa+qogMuQPh0TzHw+C73myKrjY6pREijknMC/rnIMz9dLPt6Kl 9 | xXy4br443dpY6dYGIhDuKhROT+vZ05HKasuuQUFhY7v/KoUpEZMB9rfUSzjQ5fak 10 | eDUAMniXRcd+DmwvboG2TI6ixmuPK+ECAwEAAaNGMEQwDgYDVR0PAQH/BAQDAgWg 11 | MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDwYDVR0RBAgwBocE 12 | fwAAATANBgkqhkiG9w0BAQsFAAOCAQEAMXOLvj7BFsxdbcfRPBd0OFrH/8lI7vPV 13 | LRcJ6r5iv0cnNvZXXbIOQLbg4clJAWjoE08nRm1KvNXhKdns0ELEV86YN2S6jThq 14 | rIGrBqKtaJLB3M9BtDSiQ6SGPLYrWvmhj3Avi8PbSGy51bpGbqopd16j6LYU7Cp2 15 | TefMRlOAFtHojpCVon1CMpqcNxS0WNlQ3lUBSrw3HB0o12x++roja2ibF54tSHXB 16 | KUuadoEzN+mMBwenEBychmAGzdiG4GQHRmhigh85+mtW6UMGiqyCZHs0EgE9FCLL 17 | sRrsTI/VOzLz6lluygXkOsXrP+PP0SvmE3eylWjj9e2nj/u/Cy2YKg== 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /examples/ego/views/html/head_head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | my-ego 8 | 9 | 23 | 24 | 25 | 26 | 27 |
28 |
29 |
ego:{{.head}}
30 |
31 | 32 |

node---1

33 |
34 |
35 | {{range .parArr}} 36 |

arr:::: {{.}}

37 | {{end}} 38 |
39 |
40 |
41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /mid/util/fs.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "net/http" 5 | "os" 6 | ) 7 | 8 | type ( 9 | OnlyfilesFS struct { 10 | fs http.FileSystem 11 | } 12 | neuteredReaddirFile struct { 13 | http.File 14 | } 15 | ) 16 | 17 | // Dir returns a http.Filesystem that can be used by http.FileServer(). It is used internally 18 | // in router.Static(). 19 | // in router.Static(). 20 | // if listDirectory == true, then it works the same as http.Dir() otherwise it returns 21 | // a filesystem that prevents http.FileServer() to list the directory files. 22 | func Dir(root string, listDirectory bool) http.FileSystem { 23 | fs := http.Dir(root) 24 | if listDirectory { 25 | return fs 26 | } 27 | return &OnlyfilesFS{fs} 28 | } 29 | 30 | // Open conforms to http.Filesystem. 31 | func (fs OnlyfilesFS) Open(name string) (http.File, error) { 32 | f, err := fs.fs.Open(name) 33 | if err != nil { 34 | return nil, err 35 | } 36 | return neuteredReaddirFile{f}, nil 37 | } 38 | 39 | // Readdir overrides the http.File default implementation. 40 | func (f neuteredReaddirFile) Readdir(count int) ([]os.FileInfo, error) { 41 | // this disables directory listing 42 | return nil, nil 43 | } 44 | -------------------------------------------------------------------------------- /examples/graceful-shutdown/graceful-shutdown/server.go: -------------------------------------------------------------------------------- 1 | // +build go1.8 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "log" 8 | "net/http" 9 | "os" 10 | "os/signal" 11 | "time" 12 | 13 | "github.com/go-ego/ego" 14 | ) 15 | 16 | func main() { 17 | router := ego.Default() 18 | router.GET("/", func(c *ego.Context) { 19 | time.Sleep(5 * time.Second) 20 | c.String(http.StatusOK, "Welcome ego Server") 21 | }) 22 | 23 | srv := &http.Server{ 24 | Addr: ":8080", 25 | Handler: router, 26 | } 27 | 28 | go func() { 29 | // service connections 30 | if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { 31 | log.Fatalf("listen: %s\n", err) 32 | } 33 | }() 34 | 35 | // Wait for interrupt signal to gracefully shutdown the server with 36 | // a timeout of 5 seconds. 37 | quit := make(chan os.Signal) 38 | signal.Notify(quit, os.Interrupt) 39 | <-quit 40 | log.Println("Shutdown Server ...") 41 | 42 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 43 | defer cancel() 44 | if err := srv.Shutdown(ctx); err != nil { 45 | log.Fatal("Server Shutdown:", err) 46 | } 47 | log.Println("Server exiting") 48 | } 49 | -------------------------------------------------------------------------------- /mid/render/ffjson.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/pquerna/ffjson/ffjson" 8 | ) 9 | 10 | type ( 11 | FFJSON struct { 12 | Data interface{} 13 | } 14 | 15 | FFIndentedJSON struct { 16 | Data interface{} 17 | } 18 | ) 19 | 20 | func (r FFJSON) Render(w http.ResponseWriter) (err error) { 21 | if err = FFWriteJSON(w, r.Data); err != nil { 22 | panic(err) 23 | } 24 | return 25 | } 26 | 27 | func (r FFJSON) WriteContentType(w http.ResponseWriter) { 28 | writeContentType(w, jsonContentType) 29 | } 30 | 31 | func FFWriteJSON(w http.ResponseWriter, obj interface{}) error { 32 | writeContentType(w, jsonContentType) 33 | ffjsonBytes, err := ffjson.Marshal(obj) 34 | if err != nil { 35 | return err 36 | } 37 | w.Write(ffjsonBytes) 38 | return nil 39 | } 40 | 41 | func (r FFIndentedJSON) Render(w http.ResponseWriter) error { 42 | r.WriteContentType(w) 43 | jsonBytes, err := json.MarshalIndent(r.Data, "", " ") 44 | if err != nil { 45 | return err 46 | } 47 | w.Write(jsonBytes) 48 | return nil 49 | } 50 | 51 | func (r FFIndentedJSON) WriteContentType(w http.ResponseWriter) { 52 | writeContentType(w, jsonContentType) 53 | } 54 | -------------------------------------------------------------------------------- /mid/binding/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "net/http" 11 | 12 | "github.com/go-ego/ego/mid/json" 13 | ) 14 | 15 | // EnableDecoderUseNumber is used to call the UseNumber method on the JSON 16 | // Decoder instance. UseNumber causes the Decoder to unmarshal a number into an 17 | // interface{} as a Number instead of as a float64. 18 | var EnableDecoderUseNumber = false 19 | 20 | type jsonBinding struct{} 21 | 22 | func (jsonBinding) Name() string { 23 | return "json" 24 | } 25 | 26 | func (jsonBinding) Bind(req *http.Request, obj interface{}) error { 27 | return decodeJSON(req.Body, obj) 28 | } 29 | 30 | func (jsonBinding) BindBody(body []byte, obj interface{}) error { 31 | return decodeJSON(bytes.NewReader(body), obj) 32 | } 33 | 34 | func decodeJSON(r io.Reader, obj interface{}) error { 35 | decoder := json.NewDecoder(r) 36 | if EnableDecoderUseNumber { 37 | decoder.UseNumber() 38 | } 39 | if err := decoder.Decode(obj); err != nil { 40 | return err 41 | } 42 | return validate(obj) 43 | } 44 | -------------------------------------------------------------------------------- /examples/grpc/pb/helloworld.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2015 gRPC authors. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | option java_multiple_files = true; 18 | option java_package = "io.grpc.examples.helloworld"; 19 | option java_outer_classname = "HelloWorldProto"; 20 | 21 | package helloworld; 22 | 23 | // The greeting service definition. 24 | service Greeter { 25 | // Sends a greeting 26 | rpc SayHello (HelloRequest) returns (HelloReply) {} 27 | } 28 | 29 | // The request message containing the user's name. 30 | message HelloRequest { 31 | string name = 1; 32 | } 33 | 34 | // The response message containing the greetings 35 | message HelloReply { 36 | string message = 1; 37 | } 38 | -------------------------------------------------------------------------------- /examples/realtime-chat/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "math/rand" 7 | 8 | "github.com/go-ego/ego" 9 | ) 10 | 11 | func main() { 12 | router := ego.Default() 13 | router.SetHTMLTemplate(html) 14 | 15 | router.GET("/room/:roomid", roomGET) 16 | router.POST("/room/:roomid", roomPOST) 17 | router.DELETE("/room/:roomid", roomDELETE) 18 | router.GET("/stream/:roomid", stream) 19 | 20 | router.Run(":3000") 21 | } 22 | 23 | func stream(c *ego.Context) { 24 | roomid := c.Param("roomid") 25 | listener := openListener(roomid) 26 | defer closeListener(roomid, listener) 27 | 28 | c.Stream(func(w io.Writer) bool { 29 | c.SSEvent("message", <-listener) 30 | return true 31 | }) 32 | } 33 | 34 | func roomGET(c *ego.Context) { 35 | roomid := c.Param("roomid") 36 | userid := fmt.Sprint(rand.Int31()) 37 | c.HTML(200, "chat_room", ego.Map{ 38 | "roomid": roomid, 39 | "userid": userid, 40 | }) 41 | } 42 | 43 | func roomPOST(c *ego.Context) { 44 | roomid := c.Param("roomid") 45 | userid := c.PostForm("user") 46 | message := c.PostForm("message") 47 | room(roomid).Submit(userid + ": " + message) 48 | 49 | c.JSON(200, ego.Map{ 50 | "status": "success", 51 | "message": message, 52 | }) 53 | } 54 | 55 | func roomDELETE(c *ego.Context) { 56 | roomid := c.Param("roomid") 57 | deleteBroadcast(roomid) 58 | } 59 | -------------------------------------------------------------------------------- /examples/ego/public/src/toast/toast.js: -------------------------------------------------------------------------------- 1 | var classActive = 'active'; 2 | 3 | var toast = function(msg, type, time) { 4 | if (type == undefined) { 5 | type = "error"; 6 | }; 7 | if (time == undefined) { 8 | time = 2000; 9 | }; 10 | var toast = document.createElement('div'); 11 | toast.classList.add('toast-container'); 12 | var html = '
' + 13 | '' + 14 | '

' + msg + '

' + 15 | '
'; 16 | var docmsg = document.getElementById('toast-msg'); 17 | if (docmsg == undefined) { 18 | toast.innerHTML = html; 19 | } 20 | toast.addEventListener('webkitTransitionEnd', function() { 21 | if (!toast.classList.contains(classActive)) { 22 | toast.parentNode.removeChild(toast); 23 | } 24 | }); 25 | document.body.appendChild(toast); 26 | 27 | var mlength = msg.length; 28 | if (mlength > 5) { 29 | var mlen = (mlength - 5) / 1.5; 30 | var mrem = 5.5 + mlen; 31 | var docmsg = document.getElementById('toast-msg'); 32 | docmsg.style.width = mrem + "rem"; 33 | } 34 | toast.offsetHeight; 35 | toast.classList.add(classActive); 36 | setTimeout(function() { 37 | toast.classList.remove(classActive); 38 | }, time); 39 | }; 40 | -------------------------------------------------------------------------------- /examples/ego/views/src/toast/toast.js: -------------------------------------------------------------------------------- 1 | var classActive = 'active'; 2 | 3 | var toast = function(msg, type, time) { 4 | if (type == undefined) { 5 | type = "error"; 6 | }; 7 | if (time == undefined) { 8 | time = 2000; 9 | }; 10 | var toast = document.createElement('div'); 11 | toast.classList.add('toast-container'); 12 | var html = '
' + 13 | '' + 14 | '

' + msg + '

' + 15 | '
'; 16 | var docmsg = document.getElementById('toast-msg'); 17 | if (docmsg == undefined) { 18 | toast.innerHTML = html; 19 | } 20 | toast.addEventListener('webkitTransitionEnd', function() { 21 | if (!toast.classList.contains(classActive)) { 22 | toast.parentNode.removeChild(toast); 23 | } 24 | }); 25 | document.body.appendChild(toast); 26 | 27 | var mlength = msg.length; 28 | if (mlength > 5) { 29 | var mlen = (mlength - 5) / 1.5; 30 | var mrem = 5.5 + mlen; 31 | var docmsg = document.getElementById('toast-msg'); 32 | docmsg.style.width = mrem + "rem"; 33 | } 34 | toast.offsetHeight; 35 | toast.classList.add(classActive); 36 | setTimeout(function() { 37 | toast.classList.remove(classActive); 38 | }, time); 39 | }; 40 | -------------------------------------------------------------------------------- /examples/realtime-advanced/stats.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "time" 7 | 8 | "github.com/manucorporat/stats" 9 | ) 10 | 11 | var ( 12 | ips = stats.New() 13 | messages = stats.New() 14 | users = stats.New() 15 | mutexStats sync.RWMutex 16 | savedStats map[string]uint64 17 | ) 18 | 19 | func statsWorker() { 20 | c := time.Tick(1 * time.Second) 21 | var lastMallocs uint64 22 | var lastFrees uint64 23 | for range c { 24 | var stats runtime.MemStats 25 | runtime.ReadMemStats(&stats) 26 | 27 | mutexStats.Lock() 28 | savedStats = map[string]uint64{ 29 | "timestamp": uint64(time.Now().Unix()), 30 | "HeapInuse": stats.HeapInuse, 31 | "StackInuse": stats.StackInuse, 32 | "Mallocs": stats.Mallocs - lastMallocs, 33 | "Frees": stats.Frees - lastFrees, 34 | "Inbound": uint64(messages.Get("inbound")), 35 | "Outbound": uint64(messages.Get("outbound")), 36 | "Connected": connectedUsers(), 37 | } 38 | lastMallocs = stats.Mallocs 39 | lastFrees = stats.Frees 40 | messages.Reset() 41 | mutexStats.Unlock() 42 | } 43 | } 44 | 45 | func connectedUsers() uint64 { 46 | connected := users.Get("connected") - users.Get("disconnected") 47 | if connected < 0 { 48 | return 0 49 | } 50 | return uint64(connected) 51 | } 52 | 53 | func Stats() map[string]uint64 { 54 | mutexStats.RLock() 55 | defer mutexStats.RUnlock() 56 | 57 | return savedStats 58 | } 59 | -------------------------------------------------------------------------------- /mid/recovery_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mid 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | 11 | "github.com/go-ego/ego" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | // TestPanicInHandler assert that panic has been recovered. 16 | func TestPanicInHandler(t *testing.T) { 17 | buffer := new(bytes.Buffer) 18 | router := ego.New() 19 | router.Use(RecoveryWithWriter(buffer)) 20 | router.GET("/recovery", func(_ *ego.Context) { 21 | panic("Oupps, Houston, we have a problem") 22 | }) 23 | // RUN 24 | w := performRequest(router, "GET", "/recovery") 25 | // TEST 26 | assert.Equal(t, 500, w.Code) 27 | assert.Contains(t, buffer.String(), "GET /recovery") 28 | assert.Contains(t, buffer.String(), "Oupps, Houston, we have a problem") 29 | assert.Contains(t, buffer.String(), "TestPanicInHandler") 30 | } 31 | 32 | // TestPanicWithAbort assert that panic has been recovered even if context.Abort was used. 33 | func TestPanicWithAbort(t *testing.T) { 34 | router := ego.New() 35 | router.Use(RecoveryWithWriter(nil)) 36 | router.GET("/recovery", func(c *ego.Context) { 37 | c.AbortWithStatus(400) 38 | panic("Oupps, Houston, we have a problem") 39 | }) 40 | // RUN 41 | w := performRequest(router, "GET", "/recovery") 42 | // TEST 43 | assert.Equal(t, 400, w.Code) 44 | } 45 | -------------------------------------------------------------------------------- /examples/multiple-service/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "time" 7 | 8 | "github.com/go-ego/ego" 9 | "golang.org/x/sync/errgroup" 10 | ) 11 | 12 | var ( 13 | g errgroup.Group 14 | ) 15 | 16 | func router01() http.Handler { 17 | e := ego.New() 18 | e.Use(ego.Recovery()) 19 | e.GET("/", func(c *ego.Context) { 20 | c.JSON( 21 | http.StatusOK, 22 | ego.Map{ 23 | "code": http.StatusOK, 24 | "error": "Welcome server 01", 25 | }, 26 | ) 27 | }) 28 | 29 | return e 30 | } 31 | 32 | func router02() http.Handler { 33 | e := ego.New() 34 | e.Use(ego.Recovery()) 35 | e.GET("/", func(c *ego.Context) { 36 | c.JSON( 37 | http.StatusOK, 38 | ego.Map{ 39 | "code": http.StatusOK, 40 | "error": "Welcome server 02", 41 | }, 42 | ) 43 | }) 44 | 45 | return e 46 | } 47 | 48 | func main() { 49 | server01 := &http.Server{ 50 | Addr: ":8080", 51 | Handler: router01(), 52 | ReadTimeout: 5 * time.Second, 53 | WriteTimeout: 10 * time.Second, 54 | } 55 | 56 | server02 := &http.Server{ 57 | Addr: ":8081", 58 | Handler: router02(), 59 | ReadTimeout: 5 * time.Second, 60 | WriteTimeout: 10 * time.Second, 61 | } 62 | 63 | g.Go(func() error { 64 | return server01.ListenAndServe() 65 | }) 66 | 67 | g.Go(func() error { 68 | return server02.ListenAndServe() 69 | }) 70 | 71 | if err := g.Wait(); err != nil { 72 | log.Fatal(err) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /mid/rego/min.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | 13 | import ( 14 | "regexp" 15 | 16 | "github.com/tdewolff/minify" 17 | "github.com/tdewolff/minify/css" 18 | "github.com/tdewolff/minify/html" 19 | "github.com/tdewolff/minify/js" 20 | "github.com/tdewolff/minify/json" 21 | "github.com/tdewolff/minify/svg" 22 | "github.com/tdewolff/minify/xml" 23 | ) 24 | 25 | func minStr(data, mediatype string) (string, error) { 26 | m := minify.New() 27 | m.AddFunc("text/css", css.Minify) 28 | m.AddFunc("text/javascript", js.Minify) 29 | m.AddFunc("text/html", html.Minify) 30 | m.AddFunc("image/svg+xml", svg.Minify) 31 | m.AddFuncRegexp(regexp.MustCompile("[/+]json$"), json.Minify) 32 | m.AddFuncRegexp(regexp.MustCompile("[/+]xml$"), xml.Minify) 33 | 34 | var err error 35 | // b, err = m.Bytes(mediatype, b) 36 | data, err = m.String(mediatype, data) 37 | if err != nil { 38 | return "", err 39 | } 40 | // fmt.Println(data) 41 | 42 | return data, nil 43 | } 44 | -------------------------------------------------------------------------------- /examples/custom-validation/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "reflect" 6 | "time" 7 | 8 | "github.com/go-ego/ego" 9 | "github.com/go-ego/ego/mid/binding" 10 | validator "gopkg.in/go-playground/validator.v8" 11 | ) 12 | 13 | type Booking struct { 14 | CheckIn time.Time `form:"check_in" binding:"required,bookabledate" time_format:"2006-01-02"` 15 | CheckOut time.Time `form:"check_out" binding:"required,gtfield=CheckIn" time_format:"2006-01-02"` 16 | } 17 | 18 | func bookableDate( 19 | v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, 20 | field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string, 21 | ) bool { 22 | if date, ok := field.Interface().(time.Time); ok { 23 | today := time.Now() 24 | if today.Year() > date.Year() || today.YearDay() > date.YearDay() { 25 | return false 26 | } 27 | } 28 | return true 29 | } 30 | 31 | func main() { 32 | route := ego.Default() 33 | // binding.Validator.RegisterValidation("bookabledate", bookableDate) 34 | if v, ok := binding.Validator.Engine().(*validator.Validate); ok { 35 | v.RegisterValidation("bookabledate", bookableDate) 36 | } 37 | route.GET("/bookable", getBookable) 38 | route.Run(":8085") 39 | } 40 | 41 | func getBookable(c *ego.Context) { 42 | var b Booking 43 | if err := c.ShouldBindWith(&b, binding.Query); err == nil { 44 | c.JSON(http.StatusOK, ego.Map{"message": "Booking dates are valid!"}) 45 | } else { 46 | c.JSON(http.StatusBadRequest, ego.Map{"error": err.Error()}) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /mid/binding/form.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import "net/http" 8 | 9 | // 32 MB 10 | const defaultMemory = 32 * 1024 * 1024 11 | 12 | type ( 13 | formBinding struct{} 14 | formPostBinding struct{} 15 | formMultipartBinding struct{} 16 | ) 17 | 18 | func (formBinding) Name() string { 19 | return "form" 20 | } 21 | 22 | func (formBinding) Bind(req *http.Request, obj interface{}) error { 23 | if err := req.ParseForm(); err != nil { 24 | return err 25 | } 26 | req.ParseMultipartForm(defaultMemory) 27 | if err := mapForm(obj, req.Form); err != nil { 28 | return err 29 | } 30 | return validate(obj) 31 | } 32 | 33 | func (formPostBinding) Name() string { 34 | return "form-urlencoded" 35 | } 36 | 37 | func (formPostBinding) Bind(req *http.Request, obj interface{}) error { 38 | if err := req.ParseForm(); err != nil { 39 | return err 40 | } 41 | if err := mapForm(obj, req.PostForm); err != nil { 42 | return err 43 | } 44 | return validate(obj) 45 | } 46 | 47 | func (formMultipartBinding) Name() string { 48 | return "multipart/form-data" 49 | } 50 | 51 | func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error { 52 | if err := req.ParseMultipartForm(defaultMemory); err != nil { 53 | return err 54 | } 55 | if err := mapForm(obj, req.MultipartForm.Value); err != nil { 56 | return err 57 | } 58 | return validate(obj) 59 | } 60 | -------------------------------------------------------------------------------- /examples/basic/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-ego/ego" 5 | ) 6 | 7 | var DB = make(map[string]string) 8 | 9 | func setupRouter() *ego.Engine { 10 | // Disable Console Color 11 | // ego.DisableConsoleColor() 12 | r := ego.Default() 13 | 14 | // Ping test 15 | r.GET("/ping", func(c *ego.Context) { 16 | c.String(200, "pong") 17 | }) 18 | 19 | // Get user value 20 | r.GET("/user/:name", func(c *ego.Context) { 21 | user := c.Params.ByName("name") 22 | value, ok := DB[user] 23 | if ok { 24 | c.JSON(200, ego.Map{"user": user, "value": value}) 25 | } else { 26 | c.JSON(200, ego.Map{"user": user, "status": "no value"}) 27 | } 28 | }) 29 | 30 | // Authorized group (uses ego.BasicAuth() middleware) 31 | // Same than: 32 | // authorized := r.Group("/") 33 | // authorized.Use(ego.BasicAuth(ego.Credentials{ 34 | // "foo": "bar", 35 | // "manu": "123", 36 | //})) 37 | authorized := r.Group("/", ego.BasicAuth(ego.Accounts{ 38 | "foo": "bar", // user:foo password:bar 39 | "manu": "123", // user:manu password:123 40 | })) 41 | 42 | authorized.POST("admin", func(c *ego.Context) { 43 | user := c.MustGet(ego.AuthUserKey).(string) 44 | 45 | // Parse JSON 46 | var json struct { 47 | Value string `json:"value" binding:"required"` 48 | } 49 | 50 | if c.Bind(&json) == nil { 51 | DB[user] = json.Value 52 | c.JSON(200, ego.Map{"status": "ok"}) 53 | } 54 | }) 55 | 56 | return r 57 | } 58 | 59 | func main() { 60 | r := setupRouter() 61 | // Listen and Server in 0.0.0.0:8080 62 | r.Run(":8080") 63 | } 64 | -------------------------------------------------------------------------------- /examples/ego/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package main 12 | 13 | import ( 14 | // "fmt" 15 | "github.com/go-ego/ego" 16 | ) 17 | 18 | // const httpUrl string = "http://127.0.0.1:3000" 19 | 20 | func main() { 21 | 22 | router := ego.Classic() 23 | ego.UseRenders() 24 | // router := ego.Default() 25 | 26 | router.Static("/js", "./views/js") 27 | router.Static("/src", "./views/src") 28 | router.GlobHTML("views/html/*") 29 | 30 | // strUrl := httpUrl + "/test/hlist" 31 | // paramMap := ego.Map{ 32 | // "lon": "10.1010101", 33 | // "lat": "20.202020", 34 | // "type": "1", 35 | // } 36 | // router.TestHtml(strUrl, paramMap) 37 | 38 | router.Ego("/banner/", "banner.html", ego.Map{ 39 | "head": "Test to load the HTML template", 40 | }) 41 | 42 | parArr := [5]int{1, 2, 3, 4, 5} 43 | router.Ego("/head/", "head/head.html", ego.Map{ 44 | "head": "Test to load the HTML template", 45 | "parArr": parArr, 46 | }) 47 | 48 | // rMap := ego.Map{ 49 | // "/b1/": "banner.html", 50 | // "/b2/": "banner.html", 51 | // "/he/": "head/head.html", 52 | // } 53 | // router.EgoGroup(rMap) 54 | 55 | router.Run(":3100") 56 | } 57 | -------------------------------------------------------------------------------- /examples/realtime-chat/template.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "html/template" 4 | 5 | var html = template.Must(template.New("chat_room").Parse(` 6 | 7 | 8 | {{.roomid}} 9 | 10 | 11 | 12 | 33 | 34 | 35 |

Welcome to {{.roomid}} room

36 |
37 |
38 | User: 39 | Message: 40 | 41 |
42 | 43 | 44 | `)) 45 | -------------------------------------------------------------------------------- /mid/binding/binding_body_test.go: -------------------------------------------------------------------------------- 1 | package binding 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "testing" 7 | 8 | "github.com/go-ego/ego/mid/binding/example" 9 | "github.com/golang/protobuf/proto" 10 | "github.com/stretchr/testify/assert" 11 | "github.com/ugorji/go/codec" 12 | ) 13 | 14 | func TestBindingBody(t *testing.T) { 15 | for _, tt := range []struct { 16 | name string 17 | binding BindingBody 18 | body string 19 | want string 20 | }{ 21 | { 22 | name: "JSON bidning", 23 | binding: JSON, 24 | body: `{"foo":"FOO"}`, 25 | }, 26 | { 27 | name: "XML bidning", 28 | binding: XML, 29 | body: ` 30 | 31 | FOO 32 | `, 33 | }, 34 | { 35 | name: "MsgPack binding", 36 | binding: MsgPack, 37 | body: msgPackBody(t), 38 | }, 39 | } { 40 | t.Logf("testing: %s", tt.name) 41 | req := requestWithBody("POST", "/", tt.body) 42 | form := FooStruct{} 43 | body, _ := ioutil.ReadAll(req.Body) 44 | assert.NoError(t, tt.binding.BindBody(body, &form)) 45 | assert.Equal(t, FooStruct{"FOO"}, form) 46 | } 47 | } 48 | 49 | func msgPackBody(t *testing.T) string { 50 | test := FooStruct{"FOO"} 51 | h := new(codec.MsgpackHandle) 52 | buf := bytes.NewBuffer(nil) 53 | assert.NoError(t, codec.NewEncoder(buf, h).Encode(test)) 54 | return buf.String() 55 | } 56 | 57 | func TestBindingBodyProto(t *testing.T) { 58 | test := example.Test{ 59 | Label: proto.String("FOO"), 60 | } 61 | data, _ := proto.Marshal(&test) 62 | req := requestWithBody("POST", "/", string(data)) 63 | form := example.Test{} 64 | body, _ := ioutil.ReadAll(req.Body) 65 | assert.NoError(t, ProtoBuf.BindBody(body, &form)) 66 | assert.Equal(t, test, form) 67 | } 68 | -------------------------------------------------------------------------------- /test/testdata/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAqbKP9hmkPn0GnLjDep/pXMzD25QGxan4g/iSXvPlyYYdhQef 3 | 9iilMse9HbcYAHXanoqblBbMIG4kXiPrU8lcd+Df+uNKFnvslxDeTPG7LWIoMj4M 4 | 0o3sqXOt2Mnj1APSVzNkd4G+8IvsmwkUoWMbLraudK25bwtogR22NdP4ZRlPEmHo 5 | bvI9h8MxLUix0xAY51sbA1r6qiAy5A+HRPMfD4LvebIquNjqlESKOScwL+ucgzP1 6 | 0s+3oqXFfLhuvjjd2ljp1gYiEO4qFE5P69nTkcpqy65BQWFju/8qhSkRkwH2t9RL 7 | ONDl9qR4NQAyeJdFx34ObC9ugbZMjqLGa48r4QIDAQABAoIBAD5mhd+GMEo2KU9J 8 | 9b/Ku8I/HapJtW/L/7Fvn0tBPncrVQGM+zpGWfDhV95sbGwG6lwwNeNvuqIWPlNL 9 | vAY0XkdKrrIQEDdSXH50WnpKzXxzwrou7QIj5Cmvevbjzl4xBZDBOilj0XWczmV4 10 | IljyG5XC4UXQeAaoWEZaSZ1jk8yAt2Zq1Hgg7HqhHsK/arWXBgax+4K5nV/s9gZx 11 | yjKU9mXTIs7k/aNnZqwQKqcZF+l3mvbZttOaFwsP14H0I8OFWhnM9hie54Dejqxi 12 | f4/llNxDqUs6lqJfP3qNxtORLcFe75M+Yl8v7g2hkjtLdZBakPzSTEx3TAK/UHgi 13 | aM8DdxECgYEA3fmg/PI4EgUEj0C3SCmQXR/CnQLMUQgb54s0asp4akvp+M7YCcr1 14 | pQd3HFUpBwhBcJg5LeSe87vLupY7pHCKk56cl9WY6hse0b9sP/7DWJuGiO62m0E0 15 | vNjQ2jpG99oR2ROIHHeWsGCpGLmrRT/kY+vR3M+AOLZniXlOCw8k0aUCgYEAw7WL 16 | XFWLxgZYQYilywqrQmfv1MBfaUCvykO6oWB+f6mmnihSFjecI+nDw/b3yXVYGEgy 17 | 0ebkuw0jP8suC8wBqX9WuXj+9nZNomJRssJyOMiEhDEqUiTztFPSp9pdruoakLTh 18 | Wk1p9NralOqGPUmxpXlFKVmYRTUbluikVxDypI0CgYBn6sqEQH0hann0+o4TWWn9 19 | PrYkPUAbm1k8771tVTZERR/W3Dbldr/DL5iCihe39BR2urziEEqdvkglJNntJMar 20 | TzDuIBADYQjvltb9qq4XGFBGYMLaMg+XbUVxNKEuvUdnwa4R7aZ9EfN34MwekkfA 21 | w5Cu9/GGG1ajVEfGA6PwBQKBgA3o71jGs8KFXOx7e90sivOTU5Z5fc6LTHNB0Rf7 22 | NcJ5GmCPWRY/KZfb25AoE4B8GKDRMNt+X69zxZeZJ1KrU0rqxA02rlhyHB54gnoE 23 | G/4xMkn6/JkOC0w70PMhMBtohC7YzFOQwQEoNPT0nkno3Pl33xSLS6lPlwBo1JVj 24 | nPtZAoGACXNLXYkR5vexE+w6FGl59r4RQhu1XU8Mr5DIHeB7kXPN3RKbS201M+Tb 25 | SB5jbu0iDV477XkzSNmhaksFf2wM9MT6CaE+8n3UU5tMa+MmBGgwYTp/i9HkqVh5 26 | jjpJifn1VWBINd4cpNzwCg9LXoo0tbtUPWwGzqVeyo/YE5GIHGo= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /mid/binding/default_validator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import ( 8 | "reflect" 9 | "sync" 10 | 11 | "gopkg.in/go-playground/validator.v8" 12 | ) 13 | 14 | type defaultValidator struct { 15 | once sync.Once 16 | validate *validator.Validate 17 | } 18 | 19 | var _ StructValidator = &defaultValidator{} 20 | 21 | // ValidateStruct receives any kind of type, but only performed 22 | // struct or pointer to struct type. 23 | func (v *defaultValidator) ValidateStruct(obj interface{}) error { 24 | value := reflect.ValueOf(obj) 25 | valueType := value.Kind() 26 | if valueType == reflect.Ptr { 27 | valueType = value.Elem().Kind() 28 | } 29 | 30 | if valueType == reflect.Struct { 31 | v.lazyinit() 32 | if err := v.validate.Struct(obj); err != nil { 33 | return err 34 | } 35 | } 36 | return nil 37 | } 38 | 39 | // Engine returns the underlying validator engine which powers the default 40 | // Validator instance. This is useful if you want to register custom validations 41 | // or struct level validations. See validator GoDoc for more info - 42 | // https://godoc.org/gopkg.in/go-playground/validator.v8 43 | func (v *defaultValidator) Engine() interface{} { 44 | v.lazyinit() 45 | return v.validate 46 | } 47 | 48 | func (v *defaultValidator) lazyinit() { 49 | v.once.Do(func() { 50 | config := &validator.Config{TagName: "binding"} 51 | v.validate = validator.New(config) 52 | }) 53 | } 54 | 55 | func kindOfData(data interface{}) reflect.Kind { 56 | value := reflect.ValueOf(data) 57 | valueType := value.Kind() 58 | if valueType == reflect.Ptr { 59 | valueType = value.Elem().Kind() 60 | } 61 | return valueType 62 | } 63 | -------------------------------------------------------------------------------- /mode.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "io" 9 | "os" 10 | 11 | "github.com/go-ego/ego/mid/binding" 12 | ) 13 | 14 | const ( 15 | // EnvEgoMode set the mode env 16 | EnvEgoMode = "EGO_MODE" 17 | 18 | DebugMode = "debug" 19 | ReleaseMode = "release" 20 | TestMode = "test" 21 | ) 22 | 23 | const ( 24 | debugCode = iota 25 | releaseCode 26 | testCode 27 | ) 28 | 29 | var ( 30 | // DefaultWriter is the default io.Writer used the Ego for debug output and 31 | // middleware output like Logger() or Recovery(). 32 | // Note that both Logger and Recovery provides custom ways to configure their 33 | // output io.Writer. 34 | // To support coloring in Windows use: 35 | // import "github.com/mattn/go-colorable" 36 | // ego.DefaultWriter = colorable.NewColorableStdout() 37 | DefaultWriter io.Writer = os.Stdout 38 | DefaultErrorWriter io.Writer = os.Stderr 39 | 40 | egoMode = debugCode 41 | modeName = DebugMode 42 | ) 43 | 44 | func init() { 45 | mode := os.Getenv(EnvEgoMode) 46 | SetMode(mode) 47 | } 48 | 49 | // SetMode set ego mode 50 | func SetMode(value string) { 51 | switch value { 52 | case DebugMode, "": 53 | egoMode = debugCode 54 | case ReleaseMode: 55 | egoMode = releaseCode 56 | case TestMode: 57 | egoMode = testCode 58 | default: 59 | panic("ego mode unknown: " + value) 60 | } 61 | if value == "" { 62 | value = DebugMode 63 | } 64 | modeName = value 65 | } 66 | 67 | func DisableBindValidation() { 68 | binding.Validator = nil 69 | } 70 | 71 | func EnableJsonDecoderUseNumber() { 72 | binding.EnableDecoderUseNumber = true 73 | } 74 | 75 | func Mode() string { 76 | return modeName 77 | } 78 | -------------------------------------------------------------------------------- /recovery_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | // TestPanicInHandler assert that panic has been recovered. 15 | func TestPanicInHandler(t *testing.T) { 16 | buffer := new(bytes.Buffer) 17 | router := New() 18 | router.Use(RecoveryWithWriter(buffer)) 19 | router.GET("/recovery", func(_ *Context) { 20 | panic("Oupps, Houston, we have a problem") 21 | }) 22 | // RUN 23 | w := performRequest(router, "GET", "/recovery") 24 | // TEST 25 | assert.Equal(t, 500, w.Code) 26 | assert.Contains(t, buffer.String(), "GET /recovery") 27 | assert.Contains(t, buffer.String(), "Oupps, Houston, we have a problem") 28 | assert.Contains(t, buffer.String(), "TestPanicInHandler") 29 | } 30 | 31 | // TestPanicWithAbort assert that panic has been recovered even if context.Abort was used. 32 | func TestPanicWithAbort(t *testing.T) { 33 | router := New() 34 | router.Use(RecoveryWithWriter(nil)) 35 | router.GET("/recovery", func(c *Context) { 36 | c.AbortWithStatus(400) 37 | panic("Oupps, Houston, we have a problem") 38 | }) 39 | // RUN 40 | w := performRequest(router, "GET", "/recovery") 41 | // TEST 42 | assert.Equal(t, 400, w.Code) 43 | } 44 | 45 | func TestSource(t *testing.T) { 46 | bs := source(nil, 0) 47 | assert.Equal(t, []byte("???"), bs) 48 | 49 | in := [][]byte{ 50 | []byte("Hello world."), 51 | []byte("Hi, ego.."), 52 | } 53 | bs = source(in, 10) 54 | assert.Equal(t, []byte("???"), bs) 55 | 56 | bs = source(in, 1) 57 | assert.Equal(t, []byte("Hello world."), bs) 58 | } 59 | 60 | func TestFunction(t *testing.T) { 61 | bs := function(1) 62 | assert.Equal(t, []byte("???"), bs) 63 | } 64 | -------------------------------------------------------------------------------- /mid/render/jsoniter.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | 7 | "github.com/json-iterator/go" 8 | ) 9 | 10 | var ijson = jsoniter.ConfigCompatibleWithStandardLibrary 11 | 12 | type IJSON struct { 13 | Data interface{} 14 | } 15 | 16 | type IIndentedJSON struct { 17 | Data interface{} 18 | } 19 | 20 | type ISecureJSON struct { 21 | Prefix string 22 | Data interface{} 23 | } 24 | 25 | type ISecureJSONPrefix string 26 | 27 | // var jsonContentType = []string{"application/json; charset=utf-8"} 28 | 29 | func (r IJSON) Render(w http.ResponseWriter) (err error) { 30 | if err = IWriteJSON(w, r.Data); err != nil { 31 | panic(err) 32 | } 33 | return 34 | } 35 | 36 | func (r IJSON) WriteContentType(w http.ResponseWriter) { 37 | writeContentType(w, jsonContentType) 38 | } 39 | 40 | func IWriteJSON(w http.ResponseWriter, obj interface{}) error { 41 | writeContentType(w, jsonContentType) 42 | jsonBytes, err := ijson.Marshal(obj) 43 | if err != nil { 44 | return err 45 | } 46 | w.Write(jsonBytes) 47 | return nil 48 | } 49 | 50 | func (r IIndentedJSON) Render(w http.ResponseWriter) error { 51 | r.WriteContentType(w) 52 | jsonBytes, err := ijson.MarshalIndent(r.Data, "", " ") 53 | if err != nil { 54 | return err 55 | } 56 | w.Write(jsonBytes) 57 | return nil 58 | } 59 | 60 | func (r IIndentedJSON) WriteContentType(w http.ResponseWriter) { 61 | writeContentType(w, jsonContentType) 62 | } 63 | 64 | func (r ISecureJSON) Render(w http.ResponseWriter) error { 65 | r.WriteContentType(w) 66 | jsonBytes, err := ijson.Marshal(r.Data) 67 | if err != nil { 68 | return err 69 | } 70 | // if the jsonBytes is array values 71 | if bytes.HasPrefix(jsonBytes, []byte("[")) && bytes.HasSuffix(jsonBytes, []byte("]")) { 72 | w.Write([]byte(r.Prefix)) 73 | } 74 | w.Write(jsonBytes) 75 | return nil 76 | } 77 | 78 | func (r ISecureJSON) WriteContentType(w http.ResponseWriter) { 79 | writeContentType(w, jsonContentType) 80 | } 81 | -------------------------------------------------------------------------------- /mid/rego/compile.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | // Package rego renders vgo 12 | package rego 13 | 14 | import ( 15 | "strings" 16 | ) 17 | 18 | func RendersVgo(name string) { 19 | var ( 20 | rname string 21 | racss string 22 | rajs string 23 | ) 24 | 25 | layout, err := Readfile("public/layout.html") 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | if len(name) > 0 { 31 | rname = "public/" + name + ".vgo" 32 | } else { 33 | rname = "public/banner.vgo" 34 | } 35 | 36 | abanner, err := Readfile(rname) 37 | if err != nil { 38 | panic(err) 39 | } 40 | 41 | notes := TrimNotes(abanner) 42 | _, retext, _, class, recss, rejs := ImpStr(notes) 43 | 44 | aRecss, aRejs := styScr(notes) 45 | 46 | for i := 0; i < len(aRecss); i++ { 47 | racss += aRecss[i] 48 | } 49 | 50 | for i := 0; i < len(aRejs); i++ { 51 | rajs += aRejs[i] 52 | } 53 | 54 | for h := 0; h < len(recss); h++ { 55 | for i := 0; i < len(recss[h]); i++ { 56 | if strings.Contains(racss, recss[h][i]) { 57 | recss[h][i] = "" 58 | } 59 | racss += recss[h][i] 60 | } 61 | } 62 | 63 | for h := 0; h < len(rejs); h++ { 64 | for i := 0; i < len(rejs[h]); i++ { 65 | if strings.Contains(rajs, rejs[h][i]) { 66 | rejs[h][i] = "" 67 | } 68 | rajs += rejs[h][i] 69 | } 70 | } 71 | 72 | trimtext := TrimIs(retext) 73 | 74 | var wname string 75 | if strings.Contains(name, "/") { 76 | sname := strings.Split(name, "/") 77 | wname = sname[0] + "_" + sname[1] 78 | } else { 79 | wname = name 80 | } 81 | 82 | WHtml(class, layout, racss, trimtext, rajs, wname) 83 | 84 | } 85 | -------------------------------------------------------------------------------- /mid/render/html.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "html/template" 9 | "net/http" 10 | ) 11 | 12 | type Delims struct { 13 | Left string 14 | Right string 15 | } 16 | 17 | type HTMLRender interface { 18 | Instance(string, interface{}) Render 19 | } 20 | 21 | type HTMLProduction struct { 22 | Template *template.Template 23 | Delims Delims 24 | } 25 | 26 | type HTMLDebug struct { 27 | Files []string 28 | Glob string 29 | Delims Delims 30 | FuncMap template.FuncMap 31 | } 32 | 33 | type HTML struct { 34 | Template *template.Template 35 | Name string 36 | Data interface{} 37 | } 38 | 39 | var htmlContentType = []string{"text/html; charset=utf-8"} 40 | 41 | func (r HTMLProduction) Instance(name string, data interface{}) Render { 42 | return HTML{ 43 | Template: r.Template, 44 | Name: name, 45 | Data: data, 46 | } 47 | } 48 | 49 | func (r HTMLDebug) Instance(name string, data interface{}) Render { 50 | return HTML{ 51 | Template: r.loadTemplate(), 52 | Name: name, 53 | Data: data, 54 | } 55 | } 56 | func (r HTMLDebug) loadTemplate() *template.Template { 57 | if r.FuncMap == nil { 58 | r.FuncMap = template.FuncMap{} 59 | } 60 | if len(r.Files) > 0 { 61 | return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFiles(r.Files...)) 62 | } 63 | if r.Glob != "" { 64 | return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob)) 65 | } 66 | panic("the HTML debug render was created without files or glob pattern") 67 | } 68 | 69 | func (r HTML) Render(w http.ResponseWriter) error { 70 | r.WriteContentType(w) 71 | 72 | if r.Name == "" { 73 | return r.Template.Execute(w, r.Data) 74 | } 75 | return r.Template.ExecuteTemplate(w, r.Name, r.Data) 76 | } 77 | 78 | func (r HTML) WriteContentType(w http.ResponseWriter) { 79 | writeContentType(w, htmlContentType) 80 | } 81 | -------------------------------------------------------------------------------- /examples/ego/views/html/banner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | my-ego 8 | 9 | 10 | 24 | 38 | 39 | 40 | 41 | 42 |
43 | 62 | 64 |
65 | 66 | 67 | 68 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [[constraint]] 29 | branch = "master" 30 | name = "github.com/dustin/go-broadcast" 31 | 32 | [[constraint]] 33 | branch = "master" 34 | name = "github.com/gin-contrib/sse" 35 | 36 | [[constraint]] 37 | branch = "master" 38 | name = "github.com/go-ego/autotls" 39 | 40 | [[constraint]] 41 | name = "github.com/golang/protobuf" 42 | version = "1.0.0" 43 | 44 | [[constraint]] 45 | name = "github.com/json-iterator/go" 46 | version = "1.1.3" 47 | 48 | [[constraint]] 49 | branch = "master" 50 | name = "github.com/manucorporat/stats" 51 | 52 | [[constraint]] 53 | name = "github.com/mattn/go-isatty" 54 | version = "0.0.3" 55 | 56 | [[constraint]] 57 | branch = "master" 58 | name = "github.com/pquerna/ffjson" 59 | 60 | [[constraint]] 61 | name = "github.com/stretchr/testify" 62 | version = "1.2.1" 63 | 64 | [[constraint]] 65 | name = "github.com/tdewolff/minify" 66 | version = "2.3.4" 67 | 68 | [[constraint]] 69 | name = "github.com/ugorji/go" 70 | version = "1.1.1" 71 | 72 | [[constraint]] 73 | branch = "master" 74 | name = "golang.org/x/crypto" 75 | 76 | [[constraint]] 77 | branch = "master" 78 | name = "golang.org/x/net" 79 | 80 | [[constraint]] 81 | branch = "master" 82 | name = "golang.org/x/sync" 83 | 84 | [[constraint]] 85 | name = "gopkg.in/go-playground/validator.v8" 86 | version = "8.18.2" 87 | 88 | [[constraint]] 89 | name = "gopkg.in/yaml.v2" 90 | version = "2.2.1" 91 | 92 | [prune] 93 | go-tests = true 94 | unused-packages = true 95 | -------------------------------------------------------------------------------- /examples/realtime-advanced/routes.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "html" 6 | "io" 7 | "strings" 8 | "time" 9 | 10 | "github.com/go-ego/ego" 11 | ) 12 | 13 | func rateLimit(c *ego.Context) { 14 | ip := c.ClientIP() 15 | value := int(ips.Add(ip, 1)) 16 | if value%50 == 0 { 17 | fmt.Printf("ip: %s, count: %d\n", ip, value) 18 | } 19 | if value >= 200 { 20 | if value%200 == 0 { 21 | fmt.Println("ip blocked") 22 | } 23 | c.Abort() 24 | c.String(503, "you were automatically banned :)") 25 | } 26 | } 27 | 28 | func index(c *ego.Context) { 29 | c.Redirect(301, "/room/hn") 30 | } 31 | 32 | func roomGET(c *ego.Context) { 33 | roomid := c.Param("roomid") 34 | nick := c.Query("nick") 35 | if len(nick) < 2 { 36 | nick = "" 37 | } 38 | if len(nick) > 13 { 39 | nick = nick[0:12] + "..." 40 | } 41 | c.HTML(200, "room_loego.templ.html", ego.Map{ 42 | "roomid": roomid, 43 | "nick": nick, 44 | "timestamp": time.Now().Unix(), 45 | }) 46 | 47 | } 48 | 49 | func roomPOST(c *ego.Context) { 50 | roomid := c.Param("roomid") 51 | nick := c.Query("nick") 52 | message := c.PostForm("message") 53 | message = strings.TrimSpace(message) 54 | 55 | validMessage := len(message) > 1 && len(message) < 200 56 | validNick := len(nick) > 1 && len(nick) < 14 57 | if !validMessage || !validNick { 58 | c.JSON(400, ego.Map{ 59 | "status": "failed", 60 | "error": "the message or nickname is too long", 61 | }) 62 | return 63 | } 64 | 65 | post := ego.Map{ 66 | "nick": html.EscapeString(nick), 67 | "message": html.EscapeString(message), 68 | } 69 | messages.Add("inbound", 1) 70 | room(roomid).Submit(post) 71 | c.JSON(200, post) 72 | } 73 | 74 | func streamRoom(c *ego.Context) { 75 | roomid := c.Param("roomid") 76 | listener := openListener(roomid) 77 | ticker := time.NewTicker(1 * time.Second) 78 | users.Add("connected", 1) 79 | defer func() { 80 | closeListener(roomid, listener) 81 | ticker.Stop() 82 | users.Add("disconnected", 1) 83 | }() 84 | 85 | c.Stream(func(w io.Writer) bool { 86 | select { 87 | case msg := <-listener: 88 | messages.Add("outbound", 1) 89 | c.SSEvent("message", msg) 90 | case <-ticker.C: 91 | c.SSEvent("stats", Stats()) 92 | } 93 | return true 94 | }) 95 | } 96 | -------------------------------------------------------------------------------- /debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "bytes" 9 | "html/template" 10 | "log" 11 | ) 12 | 13 | func init() { 14 | log.SetFlags(0) 15 | } 16 | 17 | // IsDebugging returns true if the framework is running in debug mode. 18 | // Use SetMode(ego.ReleaseMode) to disable debug mode. 19 | func IsDebugging() bool { 20 | return egoMode == debugCode 21 | } 22 | 23 | func debugPrintRoute(httpMethod, absolutePath string, handlers HandlersChain) { 24 | if IsDebugging() { 25 | nuHandlers := len(handlers) 26 | handlerName := nameOfFunction(handlers.Last()) 27 | debugPrint("%-6s %-25s --> %s (%d handlers)\n", httpMethod, absolutePath, handlerName, nuHandlers) 28 | } 29 | } 30 | 31 | func debugPrintLoadTemplate(tmpl *template.Template) { 32 | if IsDebugging() { 33 | var buf bytes.Buffer 34 | for _, tmpl := range tmpl.Templates() { 35 | buf.WriteString("\t- ") 36 | buf.WriteString(tmpl.Name()) 37 | buf.WriteString("\n") 38 | } 39 | debugPrint("Loaded HTML Templates (%d): \n%s\n", len(tmpl.Templates()), buf.String()) 40 | } 41 | } 42 | 43 | func debugPrint(format string, values ...interface{}) { 44 | if IsDebugging() { 45 | log.Printf("[EGO-debug] "+format, values...) 46 | } 47 | } 48 | 49 | func debugPrintWARNINGDefault() { 50 | debugPrint(`[WARNING] Now Ego requires Go 1.8 or later and Go 1.x will be required soon.`) 51 | 52 | debugPrint(`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. 53 | `) 54 | } 55 | 56 | func debugPrintWARNINGNew() { 57 | debugPrint(`[WARNING] Running in "debug" mode. Switch to "release" mode in production. 58 | - using env: export EGO_MODE=release 59 | - using code: ego.SetMode(ego.ReleaseMode) 60 | 61 | `) 62 | } 63 | 64 | func debugPrintWARNINGSetHTMLTemplate() { 65 | debugPrint(`[WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called 66 | at initialization. ie. before any route is registered or the router is listening in a socket: 67 | 68 | router := ego.Default() 69 | router.SetHTMLTemplate(template) // << good place 70 | 71 | `) 72 | } 73 | 74 | func debugPrintError(err error) { 75 | if err != nil { 76 | debugPrint("[ERROR] %v\n", err) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /mid/util/path_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Julien Schmidt. All rights reserved. 2 | // Based on the path package, Copyright 2009 The Go Authors. 3 | // Use of this source code is governed by a BSD-style license that can be found 4 | // at https://github.com/julienschmidt/httprouter/blob/master/LICENSE 5 | 6 | package util 7 | 8 | import ( 9 | "runtime" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | var cleanTests = []struct { 16 | path, result string 17 | }{ 18 | // Already clean 19 | {"/", "/"}, 20 | {"/abc", "/abc"}, 21 | {"/a/b/c", "/a/b/c"}, 22 | {"/abc/", "/abc/"}, 23 | {"/a/b/c/", "/a/b/c/"}, 24 | 25 | // missing root 26 | {"", "/"}, 27 | {"a/", "/a/"}, 28 | {"abc", "/abc"}, 29 | {"abc/def", "/abc/def"}, 30 | {"a/b/c", "/a/b/c"}, 31 | 32 | // Remove doubled slash 33 | {"//", "/"}, 34 | {"/abc//", "/abc/"}, 35 | {"/abc/def//", "/abc/def/"}, 36 | {"/a/b/c//", "/a/b/c/"}, 37 | {"/abc//def//ghi", "/abc/def/ghi"}, 38 | {"//abc", "/abc"}, 39 | {"///abc", "/abc"}, 40 | {"//abc//", "/abc/"}, 41 | 42 | // Remove . elements 43 | {".", "/"}, 44 | {"./", "/"}, 45 | {"/abc/./def", "/abc/def"}, 46 | {"/./abc/def", "/abc/def"}, 47 | {"/abc/.", "/abc/"}, 48 | 49 | // Remove .. elements 50 | {"..", "/"}, 51 | {"../", "/"}, 52 | {"../../", "/"}, 53 | {"../..", "/"}, 54 | {"../../abc", "/abc"}, 55 | {"/abc/def/ghi/../jkl", "/abc/def/jkl"}, 56 | {"/abc/def/../ghi/../jkl", "/abc/jkl"}, 57 | {"/abc/def/..", "/abc"}, 58 | {"/abc/def/../..", "/"}, 59 | {"/abc/def/../../..", "/"}, 60 | {"/abc/def/../../..", "/"}, 61 | {"/abc/def/../../../ghi/jkl/../../../mno", "/mno"}, 62 | 63 | // Combinations 64 | {"abc/./../def", "/def"}, 65 | {"abc//./../def", "/def"}, 66 | {"abc/../../././../def", "/def"}, 67 | } 68 | 69 | func TestPathClean(t *testing.T) { 70 | for _, test := range cleanTests { 71 | assert.Equal(t, test.result, CleanPath(test.path)) 72 | assert.Equal(t, test.result, CleanPath(test.result)) 73 | } 74 | } 75 | 76 | func TestPathCleanMallocs(t *testing.T) { 77 | if testing.Short() { 78 | t.Skip("skipping malloc count in short mode") 79 | } 80 | if runtime.GOMAXPROCS(0) > 1 { 81 | t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1") 82 | return 83 | } 84 | 85 | for _, test := range cleanTests { 86 | allocs := testing.AllocsPerRun(100, func() { CleanPath(test.result) }) 87 | assert.EqualValues(t, allocs, 0) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /examples/ego/views/js/ajax.js: -------------------------------------------------------------------------------- 1 | function ajax(options) { 2 | options = options || {}; 3 | options.type = (options.type || "GET").toUpperCase(); 4 | options.dataType = options.dataType || "json"; 5 | var params = formatParams(options.data); 6 | 7 | if (window.XMLHttpRequest) { 8 | var xhr = new XMLHttpRequest(); 9 | } else { 10 | var xhr = new ActiveXObject('Microsoft.XMLHTTP'); 11 | } 12 | 13 | xhr.onreadystatechange = function() { 14 | if (xhr.readyState == 4) { 15 | var status = xhr.status; 16 | if (status >= 200 && status < 300) { 17 | options.success && options.success(xhr.responseText, xhr.responseXML); 18 | } else { 19 | options.fail && options.fail(status); 20 | } 21 | } 22 | } 23 | 24 | if (options.type == "GET") { 25 | xhr.open("GET", options.url + "?" + params, true); 26 | xhr.send(null); 27 | } else if (options.type == "POST") { 28 | xhr.open("POST", options.url, true); 29 | 30 | xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 31 | xhr.send(params); 32 | } 33 | } 34 | 35 | function formatParams(data) { 36 | var arr = []; 37 | for (var name in data) { 38 | arr.push(encodeURIComponent(name) + "=" + encodeURIComponent(data[name])); 39 | } 40 | arr.push(("v=" + Math.random()).replace(".", "")); 41 | return arr.join("&"); 42 | } 43 | 44 | function jsonp(options) { 45 | options = options || {}; 46 | if (!options.url || !options.callback) { 47 | throw new Error("Parameter is not legal !"); 48 | } 49 | 50 | var callbackName = ('jsonp_' + Math.random()).replace(".", ""); 51 | var oHead = document.getElementsByTagName('head')[0]; 52 | options.data[options.callback] = callbackName; 53 | var params = formatParams(options.data); 54 | var oscr = document.createElement('script'); 55 | oHead.appendChild(oscr); 56 | 57 | window[callbackName] = function(json) { 58 | oHead.removeChild(oscr); 59 | clearTimeout(oscr.timer); 60 | window[callbackName] = null; 61 | options.success && options.success(json); 62 | }; 63 | 64 | oscr.src = options.url + '?' + params; 65 | 66 | if (options.time) { 67 | oscr.timer = setTimeout(function() { 68 | window[callbackName] = null; 69 | oHead.removeChild(oscr); 70 | options.fail && options.fail({ message: "Time out !" }); 71 | }, time); 72 | } 73 | }; -------------------------------------------------------------------------------- /mid/rego/js.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | 13 | import ( 14 | // "fmt" 15 | "strings" 16 | ) 17 | 18 | func reJs(str string) string { 19 | amap := Map{ 20 | //fn 21 | "func ": "function ", 22 | "fn ": "function ", 23 | "log(": "console.log(", 24 | "doc.": "document.", 25 | "win.": "window.", 26 | "nav.": "navigator.", 27 | //doc 28 | "doc(#": "document.getElementById(", 29 | "docid(": "document.getElementById(", 30 | "doctag(": "document.getElementsByTagName(", 31 | "doclass(": "document.getElementsByClassName(", 32 | "docname(": "document.getElementsByName(", 33 | "docquery(": "document.querySelector(", 34 | "docall(": "document.querySelectorAll(", 35 | //??? 36 | "docId(": "document.getElementById(", 37 | "docTag(": "document.getElementsByTagName(", 38 | "docClass(": "document.getElementsByClassName(", 39 | "docName(": "document.getElementsByName(", 40 | "docQuery(": "document.querySelector(", 41 | "docAll(": "document.querySelectorAll(", 42 | "tag(": "getElementsByTagName(", 43 | "class(": "getElementsByClassName(", 44 | "name(": "getElementsByName(", 45 | "query(": "querySelector(", 46 | "all(": "querySelectorAll(", 47 | //val 48 | ".html": ".innerHTML", 49 | ".val": ".value", 50 | ".text": ".innerText", 51 | //style 52 | ".color": ".style.color", 53 | "fonts": ".style.fontSize", 54 | "fontf": "style.fontFamily", 55 | // 56 | ".addEvent(": ".addEventListener(", 57 | } 58 | 59 | rejs := re(str, amap) 60 | return rejs 61 | } 62 | 63 | func reIf(str string) string { 64 | iftext := strings.Replace(str, "if ", "if (", -1) 65 | // fmt.Println("str--------", iftext) 66 | strtext := strings.Replace(iftext, " {", "){", -1) 67 | return strtext 68 | } 69 | 70 | func reWhile(str string) string { 71 | fortext := strings.Replace(str, "while ", "while (", -1) 72 | strtext := strings.Replace(fortext, " {", "){", -1) 73 | return strtext 74 | } 75 | 76 | func reFor(str string) string { 77 | fortext := strings.Replace(str, "for ", "for (", -1) 78 | strtext := strings.Replace(fortext, " {", "){", -1) 79 | return strtext 80 | } 81 | 82 | func reVar(str string) string { 83 | vartext := strings.Replace(str, ":=", "var ", -1) 84 | strtext := strings.Replace(vartext, " {", "){", -1) 85 | return strtext 86 | } 87 | 88 | func reLen(str string) string { 89 | lentext := strings.Replace(str, "len(", " ", -1) 90 | strtext := strings.Replace(lentext, ")", ".length", -1) 91 | return strtext 92 | } 93 | -------------------------------------------------------------------------------- /examples/realtime-advanced/resources/static/prismjs.min.css: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=clike+javascript+go */ 2 | /** 3 | * prism.js default theme for JavaScript, CSS and HTML 4 | * Based on dabblet (http://dabblet.com) 5 | * @author Lea Verou 6 | */ 7 | 8 | code[class*="language-"], 9 | pre[class*="language-"] { 10 | color: black; 11 | text-shadow: 0 1px white; 12 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 13 | direction: ltr; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | line-height: 1.5; 19 | 20 | -moz-tab-size: 4; 21 | -o-tab-size: 4; 22 | tab-size: 4; 23 | 24 | -webkit-hyphens: none; 25 | -moz-hyphens: none; 26 | -ms-hyphens: none; 27 | hyphens: none; 28 | } 29 | 30 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 31 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 32 | text-shadow: none; 33 | background: #b3d4fc; 34 | } 35 | 36 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 37 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 38 | text-shadow: none; 39 | background: #b3d4fc; 40 | } 41 | 42 | @media print { 43 | code[class*="language-"], 44 | pre[class*="language-"] { 45 | text-shadow: none; 46 | } 47 | } 48 | 49 | /* Code blocks */ 50 | pre[class*="language-"] { 51 | padding: 1em; 52 | margin: .5em 0; 53 | overflow: auto; 54 | } 55 | 56 | :not(pre) > code[class*="language-"], 57 | pre[class*="language-"] { 58 | background: #f5f2f0; 59 | } 60 | 61 | /* Inline code */ 62 | :not(pre) > code[class*="language-"] { 63 | padding: .1em; 64 | border-radius: .3em; 65 | } 66 | 67 | .token.comment, 68 | .token.prolog, 69 | .token.doctype, 70 | .token.cdata { 71 | color: slategray; 72 | } 73 | 74 | .token.punctuation { 75 | color: #999; 76 | } 77 | 78 | .namespace { 79 | opacity: .7; 80 | } 81 | 82 | .token.property, 83 | .token.tag, 84 | .token.boolean, 85 | .token.number, 86 | .token.constant, 87 | .token.symbol, 88 | .token.deleted { 89 | color: #905; 90 | } 91 | 92 | .token.selector, 93 | .token.attr-name, 94 | .token.string, 95 | .token.char, 96 | .token.builtin, 97 | .token.inserted { 98 | color: #690; 99 | } 100 | 101 | .token.operator, 102 | .token.entity, 103 | .token.url, 104 | .language-css .token.string, 105 | .style .token.string { 106 | color: #a67f59; 107 | background: hsla(0, 0%, 100%, .5); 108 | } 109 | 110 | .token.atrule, 111 | .token.attr-value, 112 | .token.keyword { 113 | color: #07a; 114 | } 115 | 116 | .token.function { 117 | color: #DD4A68; 118 | } 119 | 120 | .token.regex, 121 | .token.important, 122 | .token.variable { 123 | color: #e90; 124 | } 125 | 126 | .token.important, 127 | .token.bold { 128 | font-weight: bold; 129 | } 130 | .token.italic { 131 | font-style: italic; 132 | } 133 | 134 | .token.entity { 135 | cursor: help; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /mid/rego/css.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | 13 | // "fmt" 14 | 15 | func reCss(str string) string { 16 | amap := Map{ 17 | //text 18 | "lh:": "line-height:", 19 | "ta:": "text-align:", 20 | "va:": "vertical-align:", 21 | "td:": "text-decoration:", 22 | "tt:": "text-transform:", 23 | "ti:": "text-indent:", 24 | "ts:": "text-shadow:", 25 | "ws:": "white-space:", 26 | "wb:": "word-break:", 27 | "to:": "text-overflow:", 28 | //font 29 | // "f:": "font", 30 | "fs:": "font-size:", 31 | "ff:": "font-family:", 32 | "fst:": "font-style:", 33 | "fw:": "font-weight:", 34 | "fv:": "font-variant:", 35 | //bg 36 | "bg:": "background:", 37 | // "bgc:": "background-color:", 38 | "bc:": "background-color:", 39 | "bi:": "background-image:", 40 | "bp:": "background-position:", 41 | "bgr:": "background-repeat:", 42 | "ba:": "background-attachment:", 43 | "bgs": "background-size:", 44 | "bo:": "background-origin:", 45 | "bgc:": "background-clip:", 46 | //margin 47 | "mg:": "margin:", 48 | "ml:": "margin-left:", 49 | "mr:": "margin-right:", 50 | "mt:": "margin-top:", 51 | "mb:": "margin-bottom:", 52 | // 53 | "mh:": "max-height:", 54 | "mw": "max-width:", 55 | "mih": "min-height:", 56 | "miw": "min-width:", 57 | "vb:": "visibility:", 58 | "dp:": "display:", 59 | "pt:": "position:", 60 | //padding 61 | "pd:": "padding:", 62 | "pdt:": "padding-top:", 63 | "pb:": "padding-bottom:", 64 | "pr:": "padding-right:", 65 | "pl:": "padding-left:", 66 | //list 67 | "ls:": "list-style:", 68 | "lst:": "list-style-type:", 69 | "lsi:": "list-style-image:", 70 | "lsp:": "list-style-position:", 71 | //border 72 | "bd:": "border:", 73 | "bw:": "border-width:", 74 | "bt:": "border-top:", 75 | "bb:": "border-bottom:", 76 | "bl:": "border-left:", 77 | "bri:": "border-right:", 78 | "bdi:": "border-image:", 79 | // border-style 80 | "bs:": "border-style:", 81 | "bts:": "border-top-style:", 82 | "brs:": "border-right-style:", 83 | "bbs:": "border-bottom-style:", 84 | "bls:": "border-left-style:", 85 | "bdc:": "border-color:", 86 | "br:": "border-radius:", 87 | "bca:": "border-collapse:", 88 | // "bdca:": "border-collapse:", 89 | "boxs:": "box-shadow", 90 | //outline 91 | "oc:": "outline-color:", 92 | "os:": "outline-style:", 93 | "ow:": "outline-width:", 94 | // 95 | "tf:": "transform", 96 | } 97 | 98 | recss := re(str, amap) 99 | return recss 100 | } 101 | -------------------------------------------------------------------------------- /mid/binding/example/test.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: test.proto 3 | // DO NOT EDIT! 4 | 5 | /* 6 | Package example is a generated protocol buffer package. 7 | 8 | It is generated from these files: 9 | test.proto 10 | 11 | It has these top-level messages: 12 | Test 13 | */ 14 | package example 15 | 16 | import proto "github.com/golang/protobuf/proto" 17 | import math "math" 18 | 19 | // Reference imports to suppress errors if they are not otherwise used. 20 | var _ = proto.Marshal 21 | var _ = math.Inf 22 | 23 | type FOO int32 24 | 25 | const ( 26 | FOO_X FOO = 17 27 | ) 28 | 29 | var FOO_name = map[int32]string{ 30 | 17: "X", 31 | } 32 | var FOO_value = map[string]int32{ 33 | "X": 17, 34 | } 35 | 36 | func (x FOO) Enum() *FOO { 37 | p := new(FOO) 38 | *p = x 39 | return p 40 | } 41 | func (x FOO) String() string { 42 | return proto.EnumName(FOO_name, int32(x)) 43 | } 44 | func (x *FOO) UnmarshalJSON(data []byte) error { 45 | value, err := proto.UnmarshalJSONEnum(FOO_value, data, "FOO") 46 | if err != nil { 47 | return err 48 | } 49 | *x = FOO(value) 50 | return nil 51 | } 52 | 53 | type Test struct { 54 | Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"` 55 | Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"` 56 | Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"` 57 | Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"` 58 | XXX_unrecognized []byte `json:"-"` 59 | } 60 | 61 | func (m *Test) Reset() { *m = Test{} } 62 | func (m *Test) String() string { return proto.CompactTextString(m) } 63 | func (*Test) ProtoMessage() {} 64 | 65 | const Default_Test_Type int32 = 77 66 | 67 | func (m *Test) GetLabel() string { 68 | if m != nil && m.Label != nil { 69 | return *m.Label 70 | } 71 | return "" 72 | } 73 | 74 | func (m *Test) GetType() int32 { 75 | if m != nil && m.Type != nil { 76 | return *m.Type 77 | } 78 | return Default_Test_Type 79 | } 80 | 81 | func (m *Test) GetReps() []int64 { 82 | if m != nil { 83 | return m.Reps 84 | } 85 | return nil 86 | } 87 | 88 | func (m *Test) GetOptionalgroup() *Test_OptionalGroup { 89 | if m != nil { 90 | return m.Optionalgroup 91 | } 92 | return nil 93 | } 94 | 95 | type Test_OptionalGroup struct { 96 | RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"` 97 | XXX_unrecognized []byte `json:"-"` 98 | } 99 | 100 | func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} } 101 | func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) } 102 | func (*Test_OptionalGroup) ProtoMessage() {} 103 | 104 | func (m *Test_OptionalGroup) GetRequiredField() string { 105 | if m != nil && m.RequiredField != nil { 106 | return *m.RequiredField 107 | } 108 | return "" 109 | } 110 | 111 | func init() { 112 | proto.RegisterEnum("example.FOO", FOO_name, FOO_value) 113 | } 114 | -------------------------------------------------------------------------------- /mid/util/path.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Julien Schmidt. All rights reserved. 2 | // Based on the path package, Copyright 2009 The Go Authors. 3 | // Use of this source code is governed by a BSD-style license that can be found 4 | // in the LICENSE file. 5 | 6 | package util 7 | 8 | // CleanPath is the URL version of path.Clean, it returns a canonical URL path 9 | // for p, eliminating . and .. elements. 10 | // 11 | // The following rules are applied iteratively until no further processing can 12 | // be done: 13 | // 1. Replace multiple slashes with a single slash. 14 | // 2. Eliminate each . path name element (the current directory). 15 | // 3. Eliminate each inner .. path name element (the parent directory) 16 | // along with the non-.. element that precedes it. 17 | // 4. Eliminate .. elements that begin a rooted path: 18 | // that is, replace "/.." by "/" at the beginning of a path. 19 | // 20 | // If the result of this process is an empty string, "/" is returned. 21 | func CleanPath(p string) string { 22 | // Turn empty string into "/" 23 | if p == "" { 24 | return "/" 25 | } 26 | 27 | n := len(p) 28 | var buf []byte 29 | 30 | // Invariants: 31 | // reading from path; r is index of next byte to process. 32 | // writing to buf; w is index of next byte to write. 33 | 34 | // path must start with '/' 35 | r := 1 36 | w := 1 37 | 38 | if p[0] != '/' { 39 | r = 0 40 | buf = make([]byte, n+1) 41 | buf[0] = '/' 42 | } 43 | 44 | trailing := n > 1 && p[n-1] == '/' 45 | 46 | // A bit more clunky without a 'lazybuf' like the path package, but the loop 47 | // gets completely inlined (bufApp). So in contrast to the path package this 48 | // loop has no expensive function calls (except 1x make) 49 | 50 | for r < n { 51 | switch { 52 | case p[r] == '/': 53 | // empty path element, trailing slash is added after the end 54 | r++ 55 | 56 | case p[r] == '.' && r+1 == n: 57 | trailing = true 58 | r++ 59 | 60 | case p[r] == '.' && p[r+1] == '/': 61 | // . element 62 | r += 2 63 | 64 | case p[r] == '.' && p[r+1] == '.' && (r+2 == n || p[r+2] == '/'): 65 | // .. element: remove to last / 66 | r += 3 67 | 68 | if w > 1 { 69 | // can backtrack 70 | w-- 71 | 72 | if buf == nil { 73 | for w > 1 && p[w] != '/' { 74 | w-- 75 | } 76 | } else { 77 | for w > 1 && buf[w] != '/' { 78 | w-- 79 | } 80 | } 81 | } 82 | 83 | default: 84 | // real path element. 85 | // add slash if needed 86 | if w > 1 { 87 | bufApp(&buf, p, w, '/') 88 | w++ 89 | } 90 | 91 | // copy element 92 | for r < n && p[r] != '/' { 93 | bufApp(&buf, p, w, p[r]) 94 | w++ 95 | r++ 96 | } 97 | } 98 | } 99 | 100 | // re-append trailing slash 101 | if trailing && w > 1 { 102 | bufApp(&buf, p, w, '/') 103 | w++ 104 | } 105 | 106 | if buf == nil { 107 | return p[:w] 108 | } 109 | return string(buf[:w]) 110 | } 111 | 112 | // bufApp internal helper to lazily create a buffer if necessary. 113 | func bufApp(buf *[]byte, s string, w int, c byte) { 114 | if *buf == nil { 115 | if s[w] == c { 116 | return 117 | } 118 | 119 | *buf = make([]byte, len(s)) 120 | copy(*buf, s[:w]) 121 | } 122 | (*buf)[w] = c 123 | } 124 | -------------------------------------------------------------------------------- /auth.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "crypto/subtle" 9 | "encoding/base64" 10 | "net/http" 11 | "strconv" 12 | ) 13 | 14 | // AuthUserKey is the cookie name for user credential in basic auth. 15 | const AuthUserKey = "user" 16 | 17 | type ( 18 | // Accounts defines a key/value for user/pass list of authorized logins. 19 | Accounts map[string]string 20 | authPairs []authPair 21 | ) 22 | 23 | type authPair struct { 24 | value string 25 | user string 26 | } 27 | 28 | func (a authPairs) searchCredential(authValue string) (string, bool) { 29 | if authValue == "" { 30 | return "", false 31 | } 32 | for _, pair := range a { 33 | if pair.value == authValue { 34 | return pair.user, true 35 | } 36 | } 37 | return "", false 38 | } 39 | 40 | // BasicAuthForRealm returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where 41 | // the key is the user name and the value is the password, as well as the name of the Realm. 42 | // If the realm is empty, "Authorization Required" will be used by default. 43 | // (see http://tools.ietf.org/html/rfc2617#section-1.2) 44 | func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc { 45 | if realm == "" { 46 | realm = "Authorization Required" 47 | } 48 | realm = "Basic realm=" + strconv.Quote(realm) 49 | pairs := processAccounts(accounts) 50 | return func(c *Context) { 51 | // Search user in the slice of allowed credentials. 52 | user, found := pairs.searchCredential(c.requestHeader("Authorization")) 53 | if !found { 54 | // Credentials doesn't match, we return 401 and abort handlers chain. 55 | c.Header("WWW-Authenticate", realm) 56 | c.AbortWithStatus(http.StatusUnauthorized) 57 | return 58 | } 59 | 60 | // The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using 61 | // c.MustGet(ego.AuthUserKey). 62 | c.Set(AuthUserKey, user) 63 | } 64 | } 65 | 66 | // BasicAuth returns a Basic HTTP Authorization middleware. It takes as argument a map[string]string where 67 | // the key is the user name and the value is the password. 68 | func BasicAuth(accounts Accounts) HandlerFunc { 69 | return BasicAuthForRealm(accounts, "") 70 | } 71 | 72 | func processAccounts(accounts Accounts) authPairs { 73 | assert1(len(accounts) > 0, "Empty list of authorized credentials") 74 | pairs := make(authPairs, 0, len(accounts)) 75 | for user, password := range accounts { 76 | assert1(user != "", "User can not be empty") 77 | value := authorizationHeader(user, password) 78 | pairs = append(pairs, authPair{ 79 | value: value, 80 | user: user, 81 | }) 82 | } 83 | return pairs 84 | } 85 | 86 | func authorizationHeader(user, password string) string { 87 | base := user + ":" + password 88 | return "Basic " + base64.StdEncoding.EncodeToString([]byte(base)) 89 | } 90 | 91 | func secureCompare(given, actual string) bool { 92 | if subtle.ConstantTimeEq(int32(len(given)), int32(len(actual))) == 1 { 93 | return subtle.ConstantTimeCompare([]byte(given), []byte(actual)) == 1 94 | } 95 | // Securely compare actual to itself to keep constant time, but always return false. 96 | return subtle.ConstantTimeCompare([]byte(actual), []byte(actual)) == 1 && false 97 | } 98 | -------------------------------------------------------------------------------- /response_writer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "bufio" 9 | "io" 10 | "net" 11 | "net/http" 12 | ) 13 | 14 | const ( 15 | noWritten = -1 16 | defaultStatus = http.StatusOK 17 | ) 18 | 19 | // ResponseWriter ... 20 | type ResponseWriter interface { 21 | responseWriterBase 22 | // get the http.Pusher for server push 23 | Pusher() http.Pusher 24 | } 25 | 26 | type ( 27 | responseWriterBase interface { 28 | http.ResponseWriter 29 | http.Hijacker 30 | http.Flusher 31 | http.CloseNotifier 32 | 33 | // Returns the HTTP response status code of the current request. 34 | Status() int 35 | 36 | // Returns the number of bytes already written into the response http body. 37 | // See Written() 38 | Size() int 39 | 40 | // Writes the string into the response body. 41 | WriteString(string) (int, error) 42 | 43 | // Returns true if the response body was already written. 44 | Written() bool 45 | 46 | // Forces to write the http header (status code + headers). 47 | WriteHeaderNow() 48 | } 49 | 50 | responseWriter struct { 51 | http.ResponseWriter 52 | size int 53 | status int 54 | } 55 | ) 56 | 57 | var _ ResponseWriter = &responseWriter{} 58 | 59 | func (w *responseWriter) reset(writer http.ResponseWriter) { 60 | w.ResponseWriter = writer 61 | w.size = noWritten 62 | w.status = defaultStatus 63 | } 64 | 65 | func (w *responseWriter) Pusher() (pusher http.Pusher) { 66 | if pusher, ok := w.ResponseWriter.(http.Pusher); ok { 67 | return pusher 68 | } 69 | return nil 70 | } 71 | 72 | func (w *responseWriter) WriteHeader(code int) { 73 | if code > 0 && w.status != code { 74 | if w.Written() { 75 | debugPrint("[WARNING] Headers were already written. Wanted to override status code %d with %d", w.status, code) 76 | } 77 | w.status = code 78 | } 79 | } 80 | 81 | func (w *responseWriter) WriteHeaderNow() { 82 | if !w.Written() { 83 | w.size = 0 84 | w.ResponseWriter.WriteHeader(w.status) 85 | } 86 | } 87 | 88 | func (w *responseWriter) Write(data []byte) (n int, err error) { 89 | w.WriteHeaderNow() 90 | n, err = w.ResponseWriter.Write(data) 91 | w.size += n 92 | return 93 | } 94 | 95 | func (w *responseWriter) WriteString(s string) (n int, err error) { 96 | w.WriteHeaderNow() 97 | n, err = io.WriteString(w.ResponseWriter, s) 98 | w.size += n 99 | return 100 | } 101 | 102 | func (w *responseWriter) Status() int { 103 | return w.status 104 | } 105 | 106 | func (w *responseWriter) Size() int { 107 | return w.size 108 | } 109 | 110 | func (w *responseWriter) Written() bool { 111 | return w.size != noWritten 112 | } 113 | 114 | // Hijack implements the http.Hijacker interface. 115 | func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 116 | if w.size < 0 { 117 | w.size = 0 118 | } 119 | return w.ResponseWriter.(http.Hijacker).Hijack() 120 | } 121 | 122 | // CloseNotify implements the http.CloseNotify interface. 123 | func (w *responseWriter) CloseNotify() <-chan bool { 124 | return w.ResponseWriter.(http.CloseNotifier).CloseNotify() 125 | } 126 | 127 | // Flush implements the http.Flush interface. 128 | func (w *responseWriter) Flush() { 129 | w.WriteHeaderNow() 130 | w.ResponseWriter.(http.Flusher).Flush() 131 | } 132 | -------------------------------------------------------------------------------- /mid/rego/trim.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | 13 | import ( 14 | // "fmt" 15 | "regexp" 16 | "strings" 17 | ) 18 | 19 | func TrimNotes(rfile string) string { 20 | regc := regexp.MustCompile("(?U)\\/\\*[\\s\\S]*\\*\\/|\\/\\/.*(\n|\r)") 21 | 22 | rname := rfile + "\n" 23 | restr := regc.ReplaceAllString(rname, "") 24 | return restr 25 | } 26 | 27 | func TrimSlot(text string) string { 28 | slot := strings.Replace(text, "", "", -1) 29 | // fmt.Println(slot) 30 | return slot 31 | } 32 | 33 | func TrimQ(tkey string, restr []string) []string { 34 | var reval []string 35 | for i := 0; i < len(restr); i++ { 36 | tval := strings.Trim(restr[i], tkey+"\\=\\") 37 | bval := strings.Trim(tval, `\"`) 38 | 39 | reval = append(reval, bval) 40 | } 41 | return reval 42 | } 43 | 44 | func TrimTVal(tkey string, restr []string) []string { 45 | var reval []string 46 | for i := 0; i < len(restr); i++ { 47 | tval := strings.Trim(restr[i], tkey+"\\=\\") 48 | var bval string 49 | 50 | if strings.HasSuffix(tval, "}") { 51 | bval = strings.Trim(tval, `\{|\}`) 52 | } else { 53 | bval = strings.Trim(tval, `\"`) 54 | } 55 | 56 | reval = append(reval, bval) 57 | } 58 | return reval 59 | } 60 | 61 | func TrimVal(fval []string, keyarr, header, tvgostr string) string { 62 | tkey := TrimB(keyarr) 63 | tfval := TrimTVal(tkey, fval) 64 | 65 | if len(header) == 0 { 66 | header = strings.Replace(tvgostr, keyarr, tfval[0], -1) 67 | } else { 68 | header = strings.Replace(header, keyarr, tfval[0], -1) 69 | } 70 | 71 | return header 72 | } 73 | 74 | func Quotation(rfile string) []string { 75 | regc := regexp.MustCompile(`(?U)\".*\"`) 76 | restr := regc.FindAllString(rfile, -1) 77 | return restr 78 | } 79 | 80 | func TrimQt(str string) string { 81 | restr := strings.Trim(str, `\"`) 82 | return restr 83 | } 84 | 85 | func TrimB(str string) string { 86 | restr := strings.Trim(str, `\{|\}`) 87 | return restr 88 | } 89 | 90 | //TrimBlank 91 | func TrimBlank(str string) string { 92 | regt := regexp.MustCompile(`\t|\r|\n|\"|\s*`) 93 | restr := regt.ReplaceAllString(str, "") 94 | return restr 95 | } 96 | 97 | func TrimIs(str string) string { 98 | regt := regexp.MustCompile("(?U)\\[\\s\\S]*\\<\\/style\\>|\\[\\s\\S]*\\<\\/script\\>") 99 | restr := regt.ReplaceAllString(str, "") 100 | 101 | regi := regexp.MustCompile(`(?U)import \([\s\S]*\)|import \"[\s\S]*\"`) 102 | reistr := regi.ReplaceAllString(restr, "") 103 | 104 | return reistr 105 | } 106 | 107 | func TrimS(str string) string { 108 | regt := regexp.MustCompile("(?U)\\[\\s\\S]*\\<\\/style\\>|\\[\\s\\S]*\\<\\/script\\>") 109 | restr := regt.ReplaceAllString(str, "") 110 | return restr 111 | } 112 | 113 | func TrimBrace(rfile string) string { 114 | regc := regexp.MustCompile("(?U)\\{[^\\{|^\\}|^\\.|^\\<|^\\>]*\\}") 115 | restr := regc.ReplaceAllString(rfile, "") 116 | return restr 117 | } 118 | 119 | func TBrace(rfile string) string { 120 | restr := strings.Trim(rfile, `\{\}`) 121 | return restr 122 | } 123 | -------------------------------------------------------------------------------- /mid/render/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package render 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "html/template" 11 | "net/http" 12 | 13 | "github.com/go-ego/ego/mid/json" 14 | ) 15 | 16 | type JSON struct { 17 | Data interface{} 18 | } 19 | 20 | type IndentedJSON struct { 21 | Data interface{} 22 | } 23 | 24 | type SecureJSON struct { 25 | Prefix string 26 | Data interface{} 27 | } 28 | 29 | type JsonpJSON struct { 30 | Callback string 31 | Data interface{} 32 | } 33 | 34 | type AsciiJSON struct { 35 | Data interface{} 36 | } 37 | 38 | type SecureJSONPrefix string 39 | 40 | var ( 41 | jsonContentType = []string{"application/json; charset=utf-8"} 42 | jsonpContentType = []string{"application/javascript; charset=utf-8"} 43 | jsonAsciiContentType = []string{"application/json"} 44 | ) 45 | 46 | func (r JSON) Render(w http.ResponseWriter) (err error) { 47 | if err = WriteJSON(w, r.Data); err != nil { 48 | panic(err) 49 | } 50 | return 51 | } 52 | 53 | func (r JSON) WriteContentType(w http.ResponseWriter) { 54 | writeContentType(w, jsonContentType) 55 | } 56 | 57 | func WriteJSON(w http.ResponseWriter, obj interface{}) error { 58 | writeContentType(w, jsonContentType) 59 | jsonBytes, err := json.Marshal(obj) 60 | if err != nil { 61 | return err 62 | } 63 | w.Write(jsonBytes) 64 | return nil 65 | } 66 | 67 | func (r IndentedJSON) Render(w http.ResponseWriter) error { 68 | r.WriteContentType(w) 69 | jsonBytes, err := json.MarshalIndent(r.Data, "", " ") 70 | if err != nil { 71 | return err 72 | } 73 | w.Write(jsonBytes) 74 | return nil 75 | } 76 | 77 | func (r IndentedJSON) WriteContentType(w http.ResponseWriter) { 78 | writeContentType(w, jsonContentType) 79 | } 80 | 81 | func (r SecureJSON) Render(w http.ResponseWriter) error { 82 | r.WriteContentType(w) 83 | jsonBytes, err := json.Marshal(r.Data) 84 | if err != nil { 85 | return err 86 | } 87 | // if the jsonBytes is array values 88 | if bytes.HasPrefix(jsonBytes, []byte("[")) && bytes.HasSuffix(jsonBytes, []byte("]")) { 89 | w.Write([]byte(r.Prefix)) 90 | } 91 | w.Write(jsonBytes) 92 | return nil 93 | } 94 | 95 | func (r SecureJSON) WriteContentType(w http.ResponseWriter) { 96 | writeContentType(w, jsonContentType) 97 | } 98 | 99 | func (r JsonpJSON) Render(w http.ResponseWriter) (err error) { 100 | r.WriteContentType(w) 101 | ret, err := json.Marshal(r.Data) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | if r.Callback == "" { 107 | w.Write(ret) 108 | return nil 109 | } 110 | 111 | callback := template.JSEscapeString(r.Callback) 112 | w.Write([]byte(callback)) 113 | w.Write([]byte("(")) 114 | w.Write(ret) 115 | w.Write([]byte(")")) 116 | 117 | return nil 118 | } 119 | 120 | func (r JsonpJSON) WriteContentType(w http.ResponseWriter) { 121 | writeContentType(w, jsonpContentType) 122 | } 123 | 124 | func (r AsciiJSON) Render(w http.ResponseWriter) (err error) { 125 | r.WriteContentType(w) 126 | ret, err := json.Marshal(r.Data) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | var buffer bytes.Buffer 132 | for _, r := range string(ret) { 133 | cvt := "" 134 | if r < 128 { 135 | cvt = string(r) 136 | } else { 137 | cvt = fmt.Sprintf("\\u%04x", int64(r)) 138 | } 139 | buffer.WriteString(cvt) 140 | } 141 | 142 | w.Write(buffer.Bytes()) 143 | return nil 144 | } 145 | 146 | func (r AsciiJSON) WriteContentType(w http.ResponseWriter) { 147 | writeContentType(w, jsonAsciiContentType) 148 | } 149 | -------------------------------------------------------------------------------- /mid/recovery.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mid 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | "log" 13 | "net/http/httputil" 14 | "runtime" 15 | "time" 16 | 17 | "github.com/go-ego/ego" 18 | ) 19 | 20 | var ( 21 | dunno = []byte("???") 22 | centerDot = []byte("·") 23 | dot = []byte(".") 24 | slash = []byte("/") 25 | ) 26 | 27 | // Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. 28 | func Recovery() ego.HandlerFunc { 29 | return RecoveryWithWriter(ego.DefaultErrorWriter) 30 | } 31 | 32 | func RecoveryWithWriter(out io.Writer) ego.HandlerFunc { 33 | var logger *log.Logger 34 | if out != nil { 35 | logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags) 36 | } 37 | return func(c *ego.Context) { 38 | defer func() { 39 | if err := recover(); err != nil { 40 | if logger != nil { 41 | stack := stack(3) 42 | httprequest, _ := httputil.DumpRequest(c.Request, false) 43 | logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", 44 | timeFormat(time.Now()), string(httprequest), err, stack, reset) 45 | } 46 | c.AbortWithStatus(500) 47 | } 48 | }() 49 | c.Next() 50 | } 51 | } 52 | 53 | // stack returns a nicely formated stack frame, skipping skip frames 54 | func stack(skip int) []byte { 55 | buf := new(bytes.Buffer) // the returned data 56 | // As we loop, we open files and read them. These variables record the currently 57 | // loaded file. 58 | var ( 59 | lines [][]byte 60 | lastFile string 61 | ) 62 | 63 | for i := skip; ; i++ { // Skip the expected number of frames 64 | pc, file, line, ok := runtime.Caller(i) 65 | if !ok { 66 | break 67 | } 68 | // Print this much at least. If we can't find the source, it won't show. 69 | fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) 70 | if file != lastFile { 71 | data, err := ioutil.ReadFile(file) 72 | if err != nil { 73 | continue 74 | } 75 | lines = bytes.Split(data, []byte{'\n'}) 76 | lastFile = file 77 | } 78 | fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) 79 | } 80 | return buf.Bytes() 81 | } 82 | 83 | // source returns a space-trimmed slice of the n'th line. 84 | func source(lines [][]byte, n int) []byte { 85 | n-- // in stack trace, lines are 1-indexed but our array is 0-indexed 86 | if n < 0 || n >= len(lines) { 87 | return dunno 88 | } 89 | return bytes.TrimSpace(lines[n]) 90 | } 91 | 92 | // function returns, if possible, the name of the function containing the PC. 93 | func function(pc uintptr) []byte { 94 | fn := runtime.FuncForPC(pc) 95 | if fn == nil { 96 | return dunno 97 | } 98 | name := []byte(fn.Name()) 99 | // The name includes the path name to the package, which is unnecessary 100 | // since the file name is already included. Plus, it has center dots. 101 | // That is, we see 102 | // runtime/debug.*T·ptrmethod 103 | // and want 104 | // *T.ptrmethod 105 | // Also the package path might contains dot (e.g. code.google.com/...), 106 | // so first eliminate the path prefix 107 | if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { 108 | name = name[lastslash+1:] 109 | } 110 | if period := bytes.Index(name, dot); period >= 0 { 111 | name = name[period+1:] 112 | } 113 | name = bytes.Replace(name, centerDot, dot, -1) 114 | return name 115 | } 116 | 117 | func timeFormat(t time.Time) string { 118 | var timeString = t.Format("2006/01/02 - 15:04:05") 119 | return timeString 120 | } 121 | -------------------------------------------------------------------------------- /mid/util/errors_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package util 6 | 7 | import ( 8 | // "encoding/json" 9 | "errors" 10 | "testing" 11 | 12 | "github.com/go-ego/ego/mid/json" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestError(t *testing.T) { 17 | baseError := errors.New("test error") 18 | err := &Error{ 19 | Err: baseError, 20 | Type: ErrorTypePrivate, 21 | } 22 | assert.Equal(t, baseError.Error(), err.Error()) 23 | assert.Equal(t, Map{"error": baseError.Error()}, err.JSON()) 24 | 25 | assert.Equal(t, err.SetType(ErrorTypePublic), err) 26 | assert.Equal(t, ErrorTypePublic, err.Type) 27 | 28 | assert.Equal(t, err.SetMeta("some data"), err) 29 | assert.Equal(t, "some data", err.Meta) 30 | assert.Equal(t, Map{ 31 | "error": baseError.Error(), 32 | "meta": "some data", 33 | }, err.JSON()) 34 | 35 | jsonBytes, _ := json.Marshal(err) 36 | assert.Equal(t, "{\"error\":\"test error\",\"meta\":\"some data\"}", string(jsonBytes)) 37 | 38 | err.SetMeta(Map{ 39 | "status": "200", 40 | "data": "some data", 41 | }) 42 | assert.Equal(t, Map{ 43 | "error": baseError.Error(), 44 | "status": "200", 45 | "data": "some data", 46 | }, err.JSON()) 47 | 48 | err.SetMeta(Map{ 49 | "error": "custom error", 50 | "status": "200", 51 | "data": "some data", 52 | }) 53 | assert.Equal(t, Map{ 54 | "error": "custom error", 55 | "status": "200", 56 | "data": "some data", 57 | }, err.JSON()) 58 | 59 | type customError struct { 60 | status string 61 | data string 62 | } 63 | err.SetMeta(customError{status: "200", data: "other data"}) 64 | assert.Equal(t, customError{status: "200", data: "other data"}, err.JSON()) 65 | } 66 | 67 | func TestErrorSlice(t *testing.T) { 68 | errs := ErrorMsgs{ 69 | {Err: errors.New("first"), Type: ErrorTypePrivate}, 70 | {Err: errors.New("second"), Type: ErrorTypePrivate, Meta: "some data"}, 71 | {Err: errors.New("third"), Type: ErrorTypePublic, Meta: Map{"status": "400"}}, 72 | } 73 | 74 | assert.Equal(t, errs, errs.ByType(ErrorTypeAny)) 75 | assert.Equal(t, "third", errs.Last().Error()) 76 | assert.Equal(t, []string{"first", "second", "third"}, errs.Errors()) 77 | assert.Equal(t, []string{"third"}, errs.ByType(ErrorTypePublic).Errors()) 78 | assert.Equal(t, []string{"first", "second"}, errs.ByType(ErrorTypePrivate).Errors()) 79 | assert.Equal(t, []string{"first", "second", "third"}, errs.ByType(ErrorTypePublic|ErrorTypePrivate).Errors()) 80 | assert.Empty(t, errs.ByType(ErrorTypeBind)) 81 | assert.Empty(t, errs.ByType(ErrorTypeBind).String()) 82 | 83 | assert.Equal(t, `Error #01: first 84 | Error #02: second 85 | Meta: some data 86 | Error #03: third 87 | Meta: map[status:400] 88 | `, errs.String()) 89 | assert.Equal(t, []interface{}{ 90 | Map{"error": "first"}, 91 | Map{"error": "second", "meta": "some data"}, 92 | Map{"error": "third", "status": "400"}, 93 | }, errs.JSON()) 94 | jsonBytes, _ := json.Marshal(errs) 95 | assert.Equal(t, "[{\"error\":\"first\"},{\"error\":\"second\",\"meta\":\"some data\"},{\"error\":\"third\",\"status\":\"400\"}]", 96 | string(jsonBytes)) 97 | errs = ErrorMsgs{ 98 | {Err: errors.New("first"), Type: ErrorTypePrivate}, 99 | } 100 | assert.Equal(t, Map{"error": "first"}, errs.JSON()) 101 | jsonBytes, _ = json.Marshal(errs) 102 | assert.Equal(t, "{\"error\":\"first\"}", string(jsonBytes)) 103 | 104 | errs = ErrorMsgs{} 105 | assert.Nil(t, errs.Last()) 106 | assert.Nil(t, errs.JSON()) 107 | assert.Empty(t, errs.String()) 108 | } 109 | -------------------------------------------------------------------------------- /mid/binding/binding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package binding 6 | 7 | import "net/http" 8 | 9 | // Content-Type MIME of the most common data formats. 10 | const ( 11 | MIMEJSON = "application/json" 12 | MIMEHTML = "text/html" 13 | MIMEXML = "application/xml" 14 | MIMEXML2 = "text/xml" 15 | MIMEPlain = "text/plain" 16 | MIMEPOSTForm = "application/x-www-form-urlencoded" 17 | MIMEMultipartPOSTForm = "multipart/form-data" 18 | MIMEPROTOBUF = "application/x-protobuf" 19 | MIMEMSGPACK = "application/x-msgpack" 20 | MIMEMSGPACK2 = "application/msgpack" 21 | ) 22 | 23 | // Binding describes the interface which needs to be implemented for binding the 24 | // data present in the request such as JSON request body, query parameters or 25 | // the form POST. 26 | type Binding interface { 27 | Name() string 28 | Bind(*http.Request, interface{}) error 29 | } 30 | 31 | // BindingBody adds BindBody method to Binding. BindBody is similar with Bind, 32 | // but it reads the body from supplied bytes instead of req.Body. 33 | type BindingBody interface { 34 | Binding 35 | BindBody([]byte, interface{}) error 36 | } 37 | 38 | // StructValidator is the minimal interface which needs to be implemented in 39 | // order for it to be used as the validator engine for ensuring the correctness 40 | // of the reqest. Gin provides a default implementation for this using 41 | // https://github.com/go-playground/validator/tree/v8.18.2. 42 | type StructValidator interface { 43 | // ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right. 44 | // If the received type is not a struct, any validation should be skipped and nil must be returned. 45 | // If the received type is a struct or pointer to a struct, the validation should be performed. 46 | // If the struct is not valid or the validation itself fails, a descriptive error should be returned. 47 | // Otherwise nil must be returned. 48 | ValidateStruct(interface{}) error 49 | 50 | // Engine returns the underlying validator engine which powers the 51 | // StructValidator implementation. 52 | Engine() interface{} 53 | } 54 | 55 | // Validator is the default validator which implements the StructValidator 56 | // interface. It uses https://github.com/go-playground/validator/tree/v8.18.2 57 | // under the hood. 58 | var Validator StructValidator = &defaultValidator{} 59 | 60 | // These implement the Binding interface and can be used to bind the data 61 | // present in the request to struct instances. 62 | var ( 63 | JSON = jsonBinding{} 64 | XML = xmlBinding{} 65 | Form = formBinding{} 66 | Query = queryBinding{} 67 | FormPost = formPostBinding{} 68 | FormMultipart = formMultipartBinding{} 69 | ProtoBuf = protobufBinding{} 70 | MsgPack = msgpackBinding{} 71 | ) 72 | 73 | // Default returns the appropriate Binding instance based on the HTTP method 74 | // and the content type. 75 | func Default(method, contentType string) Binding { 76 | if method == "GET" { 77 | return Form 78 | } 79 | 80 | switch contentType { 81 | case MIMEJSON: 82 | return JSON 83 | case MIMEXML, MIMEXML2: 84 | return XML 85 | case MIMEPROTOBUF: 86 | return ProtoBuf 87 | case MIMEMSGPACK, MIMEMSGPACK2: 88 | return MsgPack 89 | default: //case MIMEPOSTForm, MIMEMultipartPOSTForm: 90 | return Form 91 | } 92 | } 93 | 94 | func validate(obj interface{}) error { 95 | if Validator == nil { 96 | return nil 97 | } 98 | return Validator.ValidateStruct(obj) 99 | } 100 | -------------------------------------------------------------------------------- /recovery.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | "log" 13 | "net/http" 14 | "net/http/httputil" 15 | "runtime" 16 | "time" 17 | ) 18 | 19 | var ( 20 | dunno = []byte("???") 21 | centerDot = []byte("·") 22 | dot = []byte(".") 23 | slash = []byte("/") 24 | ) 25 | 26 | // Recovery returns a middleware that recovers from any panics and 27 | // writes a 500 if there was one. 28 | func Recovery() HandlerFunc { 29 | return RecoveryWithWriter(DefaultErrorWriter) 30 | } 31 | 32 | // RecoveryWithWriter returns a middleware for a given writer that recovers 33 | // from any panics and writes a 500 if there was one. 34 | func RecoveryWithWriter(out io.Writer) HandlerFunc { 35 | var logger *log.Logger 36 | if out != nil { 37 | logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags) 38 | } 39 | return func(c *Context) { 40 | defer func() { 41 | if err := recover(); err != nil { 42 | if logger != nil { 43 | stack := stack(3) 44 | httprequest, _ := httputil.DumpRequest(c.Request, false) 45 | logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s", 46 | timeFormat(time.Now()), string(httprequest), err, stack, reset) 47 | } 48 | c.AbortWithStatus(http.StatusInternalServerError) 49 | } 50 | }() 51 | c.Next() 52 | } 53 | } 54 | 55 | // stack returns a nicely formatted stack frame, skipping skip frames. 56 | func stack(skip int) []byte { 57 | buf := new(bytes.Buffer) // the returned data 58 | // As we loop, we open files and read them. These variables record the currently 59 | // loaded file. 60 | var lines [][]byte 61 | var lastFile string 62 | for i := skip; ; i++ { // Skip the expected number of frames 63 | pc, file, line, ok := runtime.Caller(i) 64 | if !ok { 65 | break 66 | } 67 | // Print this much at least. If we can't find the source, it won't show. 68 | fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) 69 | if file != lastFile { 70 | data, err := ioutil.ReadFile(file) 71 | if err != nil { 72 | continue 73 | } 74 | lines = bytes.Split(data, []byte{'\n'}) 75 | lastFile = file 76 | } 77 | fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) 78 | } 79 | return buf.Bytes() 80 | } 81 | 82 | // source returns a space-trimmed slice of the n'th line. 83 | func source(lines [][]byte, n int) []byte { 84 | n-- // in stack trace, lines are 1-indexed but our array is 0-indexed 85 | if n < 0 || n >= len(lines) { 86 | return dunno 87 | } 88 | return bytes.TrimSpace(lines[n]) 89 | } 90 | 91 | // function returns, if possible, the name of the function containing the PC. 92 | func function(pc uintptr) []byte { 93 | fn := runtime.FuncForPC(pc) 94 | if fn == nil { 95 | return dunno 96 | } 97 | name := []byte(fn.Name()) 98 | // The name includes the path name to the package, which is unnecessary 99 | // since the file name is already included. Plus, it has center dots. 100 | // That is, we see 101 | // runtime/debug.*T·ptrmethod 102 | // and want 103 | // *T.ptrmethod 104 | // Also the package path might contains dot (e.g. code.google.com/...), 105 | // so first eliminate the path prefix 106 | if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { 107 | name = name[lastslash+1:] 108 | } 109 | if period := bytes.Index(name, dot); period >= 0 { 110 | name = name[period+1:] 111 | } 112 | name = bytes.Replace(name, centerDot, dot, -1) 113 | return name 114 | } 115 | 116 | func timeFormat(t time.Time) string { 117 | var timeString = t.Format("2006/01/02 - 15:04:05") 118 | return timeString 119 | } 120 | -------------------------------------------------------------------------------- /debug_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "html/template" 11 | "io" 12 | "log" 13 | "os" 14 | "testing" 15 | 16 | "github.com/stretchr/testify/assert" 17 | ) 18 | 19 | // TODO 20 | // func debugRoute(httpMethod, absolutePath string, handlers HandlersChain) { 21 | // func debugPrint(format string, values ...interface{}) { 22 | 23 | func TestIsDebugging(t *testing.T) { 24 | SetMode(DebugMode) 25 | assert.True(t, IsDebugging()) 26 | SetMode(ReleaseMode) 27 | assert.False(t, IsDebugging()) 28 | SetMode(TestMode) 29 | assert.False(t, IsDebugging()) 30 | } 31 | 32 | func TestDebugPrint(t *testing.T) { 33 | var w bytes.Buffer 34 | setup(&w) 35 | defer teardown() 36 | 37 | SetMode(ReleaseMode) 38 | debugPrint("DEBUG this!") 39 | SetMode(TestMode) 40 | debugPrint("DEBUG this!") 41 | assert.Empty(t, w.String()) 42 | 43 | SetMode(DebugMode) 44 | debugPrint("these are %d %s\n", 2, "error messages") 45 | assert.Equal(t, "[EGO-debug] these are 2 error messages\n", w.String()) 46 | } 47 | 48 | func TestDebugPrintError(t *testing.T) { 49 | var w bytes.Buffer 50 | setup(&w) 51 | defer teardown() 52 | 53 | SetMode(DebugMode) 54 | debugPrintError(nil) 55 | assert.Empty(t, w.String()) 56 | 57 | debugPrintError(errors.New("this is an error")) 58 | assert.Equal(t, "[EGO-debug] [ERROR] this is an error\n", w.String()) 59 | } 60 | 61 | func TestDebugPrintRoutes(t *testing.T) { 62 | var w bytes.Buffer 63 | setup(&w) 64 | defer teardown() 65 | 66 | debugPrintRoute("GET", "/path/to/route/:param", HandlersChain{func(c *Context) {}, handlerNameTest}) 67 | assert.Regexp(t, `^\[EGO-debug\] GET /path/to/route/:param --> (.*/vendor/)?github.com/go-ego/ego.handlerNameTest \(2 handlers\)\n$`, w.String()) 68 | } 69 | 70 | func TestDebugPrintLoadTemplate(t *testing.T) { 71 | var w bytes.Buffer 72 | setup(&w) 73 | defer teardown() 74 | 75 | templ := template.Must(template.New("").Delims("{[{", "}]}").ParseGlob("./test/basic/hello.tmpl")) 76 | debugPrintLoadTemplate(templ) 77 | assert.Regexp(t, `^\[EGO-debug\] Loaded HTML Templates \(2\): \n(\t- \n|\t- hello\.tmpl\n){2}\n`, w.String()) 78 | } 79 | 80 | func TestDebugPrintWARNINGSetHTMLTemplate(t *testing.T) { 81 | var w bytes.Buffer 82 | setup(&w) 83 | defer teardown() 84 | 85 | debugPrintWARNINGSetHTMLTemplate() 86 | assert.Equal(t, "[EGO-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := ego.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", w.String()) 87 | } 88 | 89 | func TestDebugPrintWARNINGDefault(t *testing.T) { 90 | var w bytes.Buffer 91 | setup(&w) 92 | defer teardown() 93 | 94 | debugPrintWARNINGDefault() 95 | assert.Equal(t, "[EGO-debug] [WARNING] Now Ego requires Go 1.8 or later and Go 1.x will be required soon.\n[EGO-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\t\n", w.String()) 96 | } 97 | 98 | func TestDebugPrintWARNINGNew(t *testing.T) { 99 | var w bytes.Buffer 100 | setup(&w) 101 | defer teardown() 102 | 103 | debugPrintWARNINGNew() 104 | assert.Equal(t, "[EGO-debug] [WARNING] Running in \"debug\" mode. Switch to \"release\" mode in production.\n - using env:\texport EGO_MODE=release\n - using code:\tego.SetMode(ego.ReleaseMode)\n\n", w.String()) 105 | } 106 | 107 | func setup(w io.Writer) { 108 | SetMode(DebugMode) 109 | log.SetOutput(w) 110 | } 111 | 112 | func teardown() { 113 | SetMode(TestMode) 114 | log.SetOutput(os.Stdout) 115 | } 116 | -------------------------------------------------------------------------------- /utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "fmt" 9 | "net/http" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func init() { 16 | SetMode(TestMode) 17 | } 18 | 19 | type testStruct struct { 20 | T *testing.T 21 | } 22 | 23 | func (t *testStruct) ServeHTTP(w http.ResponseWriter, req *http.Request) { 24 | assert.Equal(t.T, "POST", req.Method) 25 | assert.Equal(t.T, "/path", req.URL.Path) 26 | w.WriteHeader(500) 27 | fmt.Fprint(w, "hello") 28 | } 29 | 30 | func TestWrap(t *testing.T) { 31 | router := New() 32 | router.POST("/path", WrapH(&testStruct{t})) 33 | router.GET("/path2", WrapF(func(w http.ResponseWriter, req *http.Request) { 34 | assert.Equal(t, "GET", req.Method) 35 | assert.Equal(t, "/path2", req.URL.Path) 36 | w.WriteHeader(400) 37 | fmt.Fprint(w, "hola!") 38 | })) 39 | 40 | w := performRequest(router, "POST", "/path") 41 | assert.Equal(t, 500, w.Code) 42 | assert.Equal(t, "hello", w.Body.String()) 43 | 44 | w = performRequest(router, "GET", "/path2") 45 | assert.Equal(t, 400, w.Code) 46 | assert.Equal(t, "hola!", w.Body.String()) 47 | } 48 | 49 | func TestLastChar(t *testing.T) { 50 | assert.Equal(t, uint8('a'), lastChar("hola")) 51 | assert.Equal(t, uint8('s'), lastChar("adios")) 52 | assert.Panics(t, func() { lastChar("") }) 53 | } 54 | 55 | func TestParseAccept(t *testing.T) { 56 | parts := parseAccept("text/html , application/xhtml+xml,application/xml;q=0.9, */* ;q=0.8") 57 | assert.Len(t, parts, 4) 58 | assert.Equal(t, "text/html", parts[0]) 59 | assert.Equal(t, "application/xhtml+xml", parts[1]) 60 | assert.Equal(t, "application/xml", parts[2]) 61 | assert.Equal(t, "*/*", parts[3]) 62 | } 63 | 64 | func TestChooseData(t *testing.T) { 65 | A := "a" 66 | B := "b" 67 | assert.Equal(t, A, chooseData(A, B)) 68 | assert.Equal(t, B, chooseData(nil, B)) 69 | assert.Panics(t, func() { chooseData(nil, nil) }) 70 | } 71 | 72 | func TestFilterFlags(t *testing.T) { 73 | result := filterFlags("text/html ") 74 | assert.Equal(t, "text/html", result) 75 | 76 | result = filterFlags("text/html;") 77 | assert.Equal(t, "text/html", result) 78 | } 79 | 80 | func TestFunctionName(t *testing.T) { 81 | assert.Regexp(t, `^(.*/vendor/)?github.com/go-ego/ego.somefunction$`, nameOfFunction(somefunction)) 82 | } 83 | 84 | func somefunction() { 85 | // this empty function is used by TestFunctionName() 86 | } 87 | 88 | func TestJoinPaths(t *testing.T) { 89 | assert.Equal(t, "", joinPaths("", "")) 90 | assert.Equal(t, "/", joinPaths("", "/")) 91 | assert.Equal(t, "/a", joinPaths("/a", "")) 92 | assert.Equal(t, "/a/", joinPaths("/a/", "")) 93 | assert.Equal(t, "/a/", joinPaths("/a/", "/")) 94 | assert.Equal(t, "/a/", joinPaths("/a", "/")) 95 | assert.Equal(t, "/a/hola", joinPaths("/a", "/hola")) 96 | assert.Equal(t, "/a/hola", joinPaths("/a/", "/hola")) 97 | assert.Equal(t, "/a/hola/", joinPaths("/a/", "/hola/")) 98 | assert.Equal(t, "/a/hola/", joinPaths("/a/", "/hola//")) 99 | } 100 | 101 | type bindTestStruct struct { 102 | Foo string `form:"foo" binding:"required"` 103 | Bar int `form:"bar" binding:"min=4"` 104 | } 105 | 106 | func TestBindMiddleware(t *testing.T) { 107 | var value *bindTestStruct 108 | var called bool 109 | router := New() 110 | router.GET("/", Bind(bindTestStruct{}), func(c *Context) { 111 | called = true 112 | value = c.MustGet(BindKey).(*bindTestStruct) 113 | }) 114 | performRequest(router, "GET", "/?foo=hola&bar=10") 115 | assert.True(t, called) 116 | assert.Equal(t, "hola", value.Foo) 117 | assert.Equal(t, 10, value.Bar) 118 | 119 | called = false 120 | performRequest(router, "GET", "/?foo=hola&bar=1") 121 | assert.False(t, called) 122 | 123 | assert.Panics(t, func() { 124 | Bind(&bindTestStruct{}) 125 | }) 126 | } 127 | -------------------------------------------------------------------------------- /response_writer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "net/http" 9 | "net/http/httptest" 10 | "testing" 11 | 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | // TODO 16 | // func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 17 | // func (w *responseWriter) CloseNotify() <-chan bool { 18 | // func (w *responseWriter) Flush() { 19 | 20 | var ( 21 | _ ResponseWriter = &responseWriter{} 22 | _ http.ResponseWriter = &responseWriter{} 23 | _ http.ResponseWriter = ResponseWriter(&responseWriter{}) 24 | _ http.Hijacker = ResponseWriter(&responseWriter{}) 25 | _ http.Flusher = ResponseWriter(&responseWriter{}) 26 | _ http.CloseNotifier = ResponseWriter(&responseWriter{}) 27 | ) 28 | 29 | func init() { 30 | SetMode(TestMode) 31 | } 32 | 33 | func TestResponseWriterReset(t *testing.T) { 34 | testWritter := httptest.NewRecorder() 35 | writer := &responseWriter{} 36 | var w ResponseWriter = writer 37 | 38 | writer.reset(testWritter) 39 | assert.Equal(t, -1, writer.size) 40 | assert.Equal(t, 200, writer.status) 41 | assert.Equal(t, testWritter, writer.ResponseWriter) 42 | assert.Equal(t, -1, w.Size()) 43 | assert.Equal(t, 200, w.Status()) 44 | assert.False(t, w.Written()) 45 | } 46 | 47 | func TestResponseWriterWriteHeader(t *testing.T) { 48 | testWritter := httptest.NewRecorder() 49 | writer := &responseWriter{} 50 | writer.reset(testWritter) 51 | w := ResponseWriter(writer) 52 | 53 | w.WriteHeader(300) 54 | assert.False(t, w.Written()) 55 | assert.Equal(t, 300, w.Status()) 56 | assert.NotEqual(t, 300, testWritter.Code) 57 | 58 | w.WriteHeader(-1) 59 | assert.Equal(t, 300, w.Status()) 60 | } 61 | 62 | func TestResponseWriterWriteHeadersNow(t *testing.T) { 63 | testWritter := httptest.NewRecorder() 64 | writer := &responseWriter{} 65 | writer.reset(testWritter) 66 | w := ResponseWriter(writer) 67 | 68 | w.WriteHeader(300) 69 | w.WriteHeaderNow() 70 | 71 | assert.True(t, w.Written()) 72 | assert.Equal(t, 0, w.Size()) 73 | assert.Equal(t, 300, testWritter.Code) 74 | 75 | writer.size = 10 76 | w.WriteHeaderNow() 77 | assert.Equal(t, 10, w.Size()) 78 | } 79 | 80 | func TestResponseWriterWrite(t *testing.T) { 81 | testWritter := httptest.NewRecorder() 82 | writer := &responseWriter{} 83 | writer.reset(testWritter) 84 | w := ResponseWriter(writer) 85 | 86 | n, err := w.Write([]byte("hola")) 87 | assert.Equal(t, 4, n) 88 | assert.Equal(t, 4, w.Size()) 89 | assert.Equal(t, 200, w.Status()) 90 | assert.Equal(t, 200, testWritter.Code) 91 | assert.Equal(t, "hola", testWritter.Body.String()) 92 | assert.NoError(t, err) 93 | 94 | n, err = w.Write([]byte(" adios")) 95 | assert.Equal(t, 6, n) 96 | assert.Equal(t, 10, w.Size()) 97 | assert.Equal(t, "hola adios", testWritter.Body.String()) 98 | assert.NoError(t, err) 99 | } 100 | 101 | func TestResponseWriterHijack(t *testing.T) { 102 | testWritter := httptest.NewRecorder() 103 | writer := &responseWriter{} 104 | writer.reset(testWritter) 105 | w := ResponseWriter(writer) 106 | 107 | assert.Panics(t, func() { 108 | w.Hijack() 109 | }) 110 | assert.True(t, w.Written()) 111 | 112 | assert.Panics(t, func() { 113 | w.CloseNotify() 114 | }) 115 | 116 | w.Flush() 117 | } 118 | 119 | func TestResponseWriterFlush(t *testing.T) { 120 | testServer := httptest.NewServer(http.HandlerFunc( 121 | func(w http.ResponseWriter, r *http.Request) { 122 | writer := &responseWriter{} 123 | writer.reset(w) 124 | 125 | writer.WriteHeader(http.StatusInternalServerError) 126 | writer.Flush() 127 | })) 128 | defer testServer.Close() 129 | 130 | // should return 500 131 | resp, err := http.Get(testServer.URL) 132 | assert.NoError(t, err) 133 | assert.Equal(t, http.StatusInternalServerError, resp.StatusCode) 134 | } 135 | -------------------------------------------------------------------------------- /mid/rego/find.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | 13 | import ( 14 | // "fmt" 15 | "regexp" 16 | "strings" 17 | ) 18 | 19 | func FindIf(str string) []string { 20 | regt := regexp.MustCompile("(?U)if [\\s\\S]*\\{") 21 | restr := regt.FindAllString(str, -1) 22 | return restr 23 | } 24 | 25 | func FindS(str string) []string { 26 | regt := regexp.MustCompile("(?U)\\[\\s\\S]*\\<\\/style\\>|\\[\\s\\S]*\\<\\/script\\>") 27 | restr := regt.FindAllString(str, -1) 28 | return restr 29 | } 30 | 31 | func FindSty(str string) []string { 32 | regt := regexp.MustCompile("(?U)\\[\\s\\S]*\\<\\/style\\>") 33 | restr := regt.FindAllString(str, -1) 34 | return restr 35 | } 36 | 37 | func FindScr(str string) []string { 38 | regt := regexp.MustCompile("(?U)\\[\\s\\S]*\\<\\/script\\>") 39 | restr := regt.FindAllString(str, -1) 40 | return restr 41 | } 42 | 43 | func Brace(text string) []string { 44 | regc := regexp.MustCompile("(?U)\\{[^\\{|^\\}|^\\.|^\\<|^\\>]*\\}") 45 | restr := regc.FindAllString(text, -1) 46 | return restr 47 | } 48 | 49 | func KeyBrace(key, text string) []string { 50 | regc := regexp.MustCompile("(?U)\\<" + key + "\\>\\{[^\\{|^\\}|^\\.|^\\<|^\\>]*\\}\\<\\/" + key + "\\>") 51 | restr := regc.FindAllString(text, -1) 52 | return restr 53 | } 54 | 55 | func ImpBrace(imp, key, text string) []string { 56 | regc := regexp.MustCompile("(?U)\\<" + imp + "\\>[\\s\\S]*" + key + "[\\s\\S]*\\<\\/" + imp + "\\>") 57 | restr := regc.FindAllString(text, -1) 58 | return restr 59 | } 60 | 61 | func FindaKey(key []string) []string { 62 | var rekey []string 63 | 64 | for i := 0; i < len(key); i++ { 65 | tkey := TrimB(key[i]) 66 | rekey = append(rekey, tkey) 67 | } 68 | 69 | return rekey 70 | } 71 | 72 | func FindKey(key []string) []string { 73 | var rekey []string 74 | for h := 0; h < len(key); h++ { 75 | bkey := Brace(key[h]) 76 | 77 | for i := 0; i < len(bkey); i++ { 78 | tkey := TrimB(bkey[i]) 79 | rekey = append(rekey, tkey) 80 | } 81 | } 82 | 83 | return rekey 84 | } 85 | 86 | func FindNoVal(text string) []string { 87 | regc := regexp.MustCompile(`(?U)\"[\s\S]*\"`) 88 | restr := regc.FindAllString(text, -1) 89 | return restr 90 | } 91 | 92 | func FindBVal(key, rstr string) []string { 93 | var streg string 94 | 95 | tkey := TrimB(key) 96 | 97 | if strings.Contains(rstr, "{") { 98 | streg = `(?U)` + tkey + "\\=\\{[\\s\\S]*\\}" 99 | } else { 100 | streg = `(?U)` + tkey + `\=\"[\s\S]*\"` 101 | } 102 | 103 | regc := regexp.MustCompile(streg) 104 | restr := regc.FindAllString(rstr, -1) 105 | 106 | return restr 107 | } 108 | 109 | func FindQVal(key, text string) []string { 110 | 111 | tkey := TrimB(key) 112 | 113 | streg := `(?U)` + tkey + `\=\"[\s\S]*\"` 114 | 115 | regc := regexp.MustCompile(streg) 116 | restr := regc.FindAllString(text, -1) 117 | 118 | return restr 119 | } 120 | 121 | func FindVal(key, text string) []string { 122 | var val []string 123 | bval := FindBVal(key, text) 124 | qval := FindQVal(key, text) 125 | for h := 0; h < len(bval); h++ { 126 | val = append(val, bval[h]) 127 | } 128 | 129 | for i := 0; i < len(qval); i++ { 130 | val = append(val, qval[i]) 131 | } 132 | 133 | return val 134 | } 135 | 136 | func FindArrVal(key []string) []string { 137 | var reval []string 138 | for h := 0; h < len(key); h++ { 139 | bval := Brace(key[h]) 140 | 141 | for i := 0; i < len(bval); i++ { 142 | tval := TrimB(bval[i]) 143 | reval = append(reval, tval) 144 | } 145 | } 146 | 147 | return reval 148 | } 149 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "encoding/xml" 9 | "net/http" 10 | "os" 11 | "path" 12 | "reflect" 13 | "runtime" 14 | "strings" 15 | ) 16 | 17 | const ( 18 | BindKey = "_go-ego/ego/bindkey" 19 | BodyBytesKey = "_go-ego/ego/bodybyteskey" 20 | ) 21 | 22 | func Bind(val interface{}) HandlerFunc { 23 | value := reflect.ValueOf(val) 24 | if value.Kind() == reflect.Ptr { 25 | panic(`Bind struct can not be a pointer. Example: 26 | Use: ego.Bind(Struct{}) instead of ego.Bind(&Struct{}) 27 | `) 28 | } 29 | typ := value.Type() 30 | 31 | return func(c *Context) { 32 | obj := reflect.New(typ).Interface() 33 | if c.Bind(obj) == nil { 34 | c.Set(BindKey, obj) 35 | } 36 | } 37 | } 38 | 39 | // WrapF is a helper function for wrapping http.HandlerFunc 40 | // Returns a Gin middleware 41 | func WrapF(f http.HandlerFunc) HandlerFunc { 42 | return func(c *Context) { 43 | f(c.Writer, c.Request) 44 | } 45 | } 46 | 47 | // WrapH is a helper function for wrapping http.Handler 48 | // Returns a Gin middleware 49 | func WrapH(h http.Handler) HandlerFunc { 50 | return func(c *Context) { 51 | h.ServeHTTP(c.Writer, c.Request) 52 | } 53 | } 54 | 55 | // Map is a shortcut for map[string]interface{} 56 | // type H map[string]interface{} 57 | type Map map[string]interface{} 58 | 59 | // MarshalXML allows type H to be used with xml.Marshal. 60 | func (m Map) MarshalXML(e *xml.Encoder, start xml.StartElement) error { 61 | start.Name = xml.Name{ 62 | Space: "", 63 | Local: "map", 64 | } 65 | if err := e.EncodeToken(start); err != nil { 66 | return err 67 | } 68 | for key, value := range m { 69 | elem := xml.StartElement{ 70 | Name: xml.Name{Space: "", Local: key}, 71 | Attr: []xml.Attr{}, 72 | } 73 | if err := e.EncodeElement(value, elem); err != nil { 74 | return err 75 | } 76 | } 77 | 78 | return e.EncodeToken(xml.EndElement{Name: start.Name}) 79 | } 80 | 81 | func assert1(guard bool, text string) { 82 | if !guard { 83 | panic(text) 84 | } 85 | } 86 | 87 | func filterFlags(content string) string { 88 | for i, char := range content { 89 | if char == ' ' || char == ';' { 90 | return content[:i] 91 | } 92 | } 93 | return content 94 | } 95 | 96 | func chooseData(custom, wildcard interface{}) interface{} { 97 | if custom == nil { 98 | if wildcard == nil { 99 | panic("negotiation config is invalid") 100 | } 101 | return wildcard 102 | } 103 | return custom 104 | } 105 | 106 | func parseAccept(acceptHeader string) []string { 107 | parts := strings.Split(acceptHeader, ",") 108 | out := make([]string, 0, len(parts)) 109 | for _, part := range parts { 110 | part = strings.TrimSpace(strings.Split(part, ";")[0]) 111 | if part != "" { 112 | out = append(out, part) 113 | } 114 | } 115 | return out 116 | } 117 | 118 | func lastChar(str string) uint8 { 119 | if str == "" { 120 | panic("The length of the string can't be 0") 121 | } 122 | return str[len(str)-1] 123 | } 124 | 125 | func nameOfFunction(f interface{}) string { 126 | return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() 127 | } 128 | 129 | func joinPaths(absolutePath, relativePath string) string { 130 | if relativePath == "" { 131 | return absolutePath 132 | } 133 | 134 | finalPath := path.Join(absolutePath, relativePath) 135 | appendSlash := lastChar(relativePath) == '/' && lastChar(finalPath) != '/' 136 | if appendSlash { 137 | return finalPath + "/" 138 | } 139 | return finalPath 140 | } 141 | 142 | func resolveAddress(addr []string) string { 143 | switch len(addr) { 144 | case 0: 145 | if port := os.Getenv("PORT"); port != "" { 146 | debugPrint("Environment variable PORT=\"%s\"", port) 147 | return ":" + port 148 | } 149 | debugPrint("Environment variable PORT is undefined. Using port :3000 by default") 150 | return ":3000" 151 | case 1: 152 | return addr[0] 153 | default: 154 | panic("too much parameters") 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /mid/util/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package util 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "reflect" 11 | 12 | "github.com/go-ego/ego/mid/json" 13 | ) 14 | 15 | type Map map[string]interface{} 16 | 17 | type ErrorType uint64 18 | 19 | const ( 20 | ErrorTypeBind ErrorType = 1 << 63 // used when c.Bind() fails 21 | ErrorTypeRender ErrorType = 1 << 62 // used when c.Render() fails 22 | ErrorTypePrivate ErrorType = 1 << 0 23 | ErrorTypePublic ErrorType = 1 << 1 24 | 25 | ErrorTypeAny ErrorType = 1<<64 - 1 26 | ErrorTypeNu = 2 27 | ) 28 | 29 | type ( 30 | Error struct { 31 | Err error 32 | Type ErrorType 33 | Meta interface{} 34 | } 35 | 36 | ErrorMsgs []*Error 37 | ) 38 | 39 | var _ error = &Error{} 40 | 41 | func (msg *Error) SetType(flags ErrorType) *Error { 42 | msg.Type = flags 43 | return msg 44 | } 45 | 46 | func (msg *Error) SetMeta(data interface{}) *Error { 47 | msg.Meta = data 48 | return msg 49 | } 50 | 51 | func (msg *Error) JSON() interface{} { 52 | // json := H{} 53 | json := Map{} 54 | if msg.Meta != nil { 55 | value := reflect.ValueOf(msg.Meta) 56 | switch value.Kind() { 57 | case reflect.Struct: 58 | return msg.Meta 59 | case reflect.Map: 60 | for _, key := range value.MapKeys() { 61 | json[key.String()] = value.MapIndex(key).Interface() 62 | } 63 | default: 64 | json["meta"] = msg.Meta 65 | } 66 | } 67 | if _, ok := json["error"]; !ok { 68 | json["error"] = msg.Error() 69 | } 70 | return json 71 | } 72 | 73 | // MarshalJSON implements the json.Marshaller interface. 74 | func (msg *Error) MarshalJSON() ([]byte, error) { 75 | return json.Marshal(msg.JSON()) 76 | } 77 | 78 | // Error implements the error interface. 79 | func (msg *Error) Error() string { 80 | return msg.Err.Error() 81 | } 82 | 83 | func (msg *Error) IsType(flags ErrorType) bool { 84 | return (msg.Type & flags) > 0 85 | } 86 | 87 | // ByType returns a readonly copy filtered the byte. 88 | // ie ByType(util.ErrorTypePublic) returns a slice of errors 89 | // with type = ErrorTypePublic. 90 | func (a ErrorMsgs) ByType(typ ErrorType) ErrorMsgs { 91 | if len(a) == 0 { 92 | return nil 93 | } 94 | if typ == ErrorTypeAny { 95 | return a 96 | } 97 | var result ErrorMsgs 98 | for _, msg := range a { 99 | if msg.IsType(typ) { 100 | result = append(result, msg) 101 | } 102 | } 103 | return result 104 | } 105 | 106 | // Last returns the last error in the slice. It returns nil if the array is empty. 107 | // Shortcut for errors[len(errors)-1]. 108 | func (a ErrorMsgs) Last() *Error { 109 | length := len(a) 110 | if length > 0 { 111 | return a[length-1] 112 | } 113 | return nil 114 | } 115 | 116 | // Errors returns an array will all the error messages. 117 | // Example: 118 | // c.Error(errors.New("first")) 119 | // c.Error(errors.New("second")) 120 | // c.Error(errors.New("third")) 121 | // c.Errors.Errors() // == []string{"first", "second", "third"} 122 | func (a ErrorMsgs) Errors() []string { 123 | if len(a) == 0 { 124 | return nil 125 | } 126 | errorStrings := make([]string, len(a)) 127 | for i, err := range a { 128 | errorStrings[i] = err.Error() 129 | } 130 | return errorStrings 131 | } 132 | 133 | func (a ErrorMsgs) JSON() interface{} { 134 | switch len(a) { 135 | case 0: 136 | return nil 137 | case 1: 138 | return a.Last().JSON() 139 | default: 140 | json := make([]interface{}, len(a)) 141 | for i, err := range a { 142 | json[i] = err.JSON() 143 | } 144 | return json 145 | } 146 | } 147 | 148 | func (a ErrorMsgs) MarshalJSON() ([]byte, error) { 149 | return json.Marshal(a.JSON()) 150 | } 151 | 152 | func (a ErrorMsgs) String() string { 153 | if len(a) == 0 { 154 | return "" 155 | } 156 | var buffer bytes.Buffer 157 | for i, msg := range a { 158 | fmt.Fprintf(&buffer, "Error #%02d: %s\n", i+1, msg.Err) 159 | if msg.Meta != nil { 160 | fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta) 161 | } 162 | } 163 | return buffer.String() 164 | } 165 | -------------------------------------------------------------------------------- /examples/realtime-advanced/resources/static/realtime.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | function StartRealtime(roomid, timestamp) { 4 | StartEpoch(timestamp); 5 | StartSSE(roomid); 6 | StartForm(); 7 | } 8 | 9 | function StartForm() { 10 | $('#chat-message').focus(); 11 | $('#chat-form').ajaxForm(function() { 12 | $('#chat-message').val(''); 13 | $('#chat-message').focus(); 14 | }); 15 | } 16 | 17 | function StartEpoch(timestamp) { 18 | var windowSize = 60; 19 | var height = 200; 20 | var defaultData = histogram(windowSize, timestamp); 21 | 22 | window.heapChart = $('#heapChart').epoch({ 23 | type: 'time.area', 24 | axes: ['bottom', 'left'], 25 | height: height, 26 | historySize: 10, 27 | data: [ 28 | {values: defaultData}, 29 | {values: defaultData} 30 | ] 31 | }); 32 | 33 | window.mallocsChart = $('#mallocsChart').epoch({ 34 | type: 'time.area', 35 | axes: ['bottom', 'left'], 36 | height: height, 37 | historySize: 10, 38 | data: [ 39 | {values: defaultData}, 40 | {values: defaultData} 41 | ] 42 | }); 43 | 44 | window.messagesChart = $('#messagesChart').epoch({ 45 | type: 'time.line', 46 | axes: ['bottom', 'left'], 47 | height: 240, 48 | historySize: 10, 49 | data: [ 50 | {values: defaultData}, 51 | {values: defaultData}, 52 | {values: defaultData} 53 | ] 54 | }); 55 | } 56 | 57 | function StartSSE(roomid) { 58 | if (!window.EventSource) { 59 | alert("EventSource is not enabled in this browser"); 60 | return; 61 | } 62 | var source = new EventSource('/stream/'+roomid); 63 | source.addEventListener('message', newChatMessage, false); 64 | source.addEventListener('stats', stats, false); 65 | } 66 | 67 | function stats(e) { 68 | var data = parseJSONStats(e.data); 69 | heapChart.push(data.heap); 70 | mallocsChart.push(data.mallocs); 71 | messagesChart.push(data.messages); 72 | } 73 | 74 | function parseJSONStats(e) { 75 | var data = jQuery.parseJSON(e); 76 | var timestamp = data.timestamp; 77 | 78 | var heap = [ 79 | {time: timestamp, y: data.HeapInuse}, 80 | {time: timestamp, y: data.StackInuse} 81 | ]; 82 | 83 | var mallocs = [ 84 | {time: timestamp, y: data.Mallocs}, 85 | {time: timestamp, y: data.Frees} 86 | ]; 87 | var messages = [ 88 | {time: timestamp, y: data.Connected}, 89 | {time: timestamp, y: data.Inbound}, 90 | {time: timestamp, y: data.Outbound} 91 | ]; 92 | 93 | return { 94 | heap: heap, 95 | mallocs: mallocs, 96 | messages: messages 97 | } 98 | } 99 | 100 | function newChatMessage(e) { 101 | var data = jQuery.parseJSON(e.data); 102 | var nick = data.nick; 103 | var message = data.message; 104 | var style = rowStyle(nick); 105 | var html = ""+nick+""+message+""; 106 | $('#chat').append(html); 107 | 108 | $("#chat-scroll").scrollTop($("#chat-scroll")[0].scrollHeight); 109 | } 110 | 111 | function histogram(windowSize, timestamp) { 112 | var entries = new Array(windowSize); 113 | for(var i = 0; i < windowSize; i++) { 114 | entries[i] = {time: (timestamp-windowSize+i-1), y:0}; 115 | } 116 | return entries; 117 | } 118 | 119 | var entityMap = { 120 | "&": "&", 121 | "<": "<", 122 | ">": ">", 123 | '"': '"', 124 | "'": ''', 125 | "/": '/' 126 | }; 127 | 128 | function rowStyle(nick) { 129 | var classes = ['active', 'success', 'info', 'warning', 'danger']; 130 | var index = hashCode(nick)%5; 131 | return classes[index]; 132 | } 133 | 134 | function hashCode(s){ 135 | return Math.abs(s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0)); 136 | } 137 | 138 | function escapeHtml(string) { 139 | return String(string).replace(/[&<>"'\/]/g, function (s) { 140 | return entityMap[s]; 141 | }); 142 | } 143 | 144 | window.StartRealtime = StartRealtime 145 | -------------------------------------------------------------------------------- /logger.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "os" 11 | "time" 12 | 13 | "github.com/go-ego/ego/mid/util" 14 | "github.com/mattn/go-isatty" 15 | ) 16 | 17 | var ( 18 | green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109}) 19 | white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109}) 20 | yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109}) 21 | red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109}) 22 | blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109}) 23 | magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109}) 24 | cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109}) 25 | reset = string([]byte{27, 91, 48, 109}) 26 | disableColor = false 27 | ) 28 | 29 | // DisableConsoleColor disables color output in the console. 30 | func DisableConsoleColor() { 31 | disableColor = true 32 | } 33 | 34 | // ErrorLogger returns a handlerfunc for any error type. 35 | func ErrorLogger() HandlerFunc { 36 | return ErrorLoggerT(util.ErrorTypeAny) 37 | } 38 | 39 | // ErrorLoggerT returns a handlerfunc for a given error type. 40 | func ErrorLoggerT(typ util.ErrorType) HandlerFunc { 41 | return func(c *Context) { 42 | c.Next() 43 | errors := c.Errors.ByType(typ) 44 | if len(errors) > 0 { 45 | c.JSON(-1, errors) 46 | } 47 | } 48 | } 49 | 50 | // Logger instances a Logger middleware that will write the logs to ego.DefaultWriter. 51 | // By default ego.DefaultWriter = os.Stdout. 52 | func Logger() HandlerFunc { 53 | return LoggerWithWriter(DefaultWriter) 54 | } 55 | 56 | // LoggerWithWriter instance a Logger middleware with the specified writter buffer. 57 | // Example: os.Stdout, a file opened in write mode, a socket... 58 | func LoggerWithWriter(out io.Writer, notlogged ...string) HandlerFunc { 59 | isTerm := true 60 | 61 | // if w, ok := out.(*os.File); !ok || !isatty.IsTerminal(w.Fd()) { 62 | if w, ok := out.(*os.File); !ok || 63 | (os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()))) || 64 | disableColor { 65 | isTerm = false 66 | } 67 | 68 | var skip map[string]struct{} 69 | 70 | if length := len(notlogged); length > 0 { 71 | skip = make(map[string]struct{}, length) 72 | 73 | for _, path := range notlogged { 74 | skip[path] = struct{}{} 75 | } 76 | } 77 | 78 | return func(c *Context) { 79 | // Start timer 80 | start := time.Now() 81 | path := c.Request.URL.Path 82 | raw := c.Request.URL.RawQuery 83 | 84 | // Process request 85 | c.Next() 86 | 87 | // Log only when path is not being skipped 88 | if _, ok := skip[path]; !ok { 89 | // Stop timer 90 | end := time.Now() 91 | latency := end.Sub(start) 92 | 93 | clientIP := c.ClientIP() 94 | // method := Colorize(c.Request.Method, "note") 95 | method := c.Request.Method 96 | statusCode := c.Writer.Status() 97 | var statusColor, methodColor, resetColor string 98 | if isTerm { 99 | statusColor = colorForStatus(statusCode) 100 | methodColor = colorForMethod(method) 101 | resetColor = reset 102 | } 103 | comment := c.Errors.ByType(util.ErrorTypePrivate).String() 104 | 105 | if raw != "" { 106 | path = path + "?" + raw 107 | } 108 | // fmt.Fprintf(out, "[EGO] %v |%s %3d %s| %13v | %15s |%s %s %-7s %s\n%s", 109 | fmt.Fprintf(out, "[EGO] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s", 110 | end.Format("2006/01/02 - 15:04:05"), 111 | statusColor, statusCode, resetColor, 112 | latency, 113 | clientIP, 114 | methodColor, method, resetColor, 115 | path, 116 | comment, 117 | ) 118 | } 119 | } 120 | } 121 | 122 | func colorForStatus(code int) string { 123 | switch { 124 | case code >= 200 && code < 300: 125 | return green 126 | case code >= 300 && code < 400: 127 | return white 128 | case code >= 400 && code < 500: 129 | return yellow 130 | default: 131 | return red 132 | } 133 | } 134 | 135 | func colorForMethod(method string) string { 136 | switch method { 137 | case "GET": 138 | return blue 139 | case "POST": 140 | return cyan 141 | case "PUT": 142 | return yellow 143 | case "DELETE": 144 | return red 145 | case "PATCH": 146 | return green 147 | case "HEAD": 148 | return magenta 149 | case "OPTIONS": 150 | return white 151 | default: 152 | return reset 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | ## Introduction 4 | 5 | This document explains how to contribute changes to the Ego project. It assumes you have followed the README.md and [API Document](https://github.com/go-ego/ego/blob/master/docs/doc.md). 6 | 7 | ## Bug reports 8 | 9 | Please search the issues on the issue tracker with a variety of keywords to ensure your bug is not already reported. 10 | 11 | If unique, [open an issue](https://github.com/go-ego/ego/issues/new) and answer the questions so we can understand and reproduce the problematic behavior. 12 | 13 | The burden is on you to convince us that it is actually a bug in Ego. This is easiest to do when you write clear, concise instructions so we can reproduce the behavior (even if it seems obvious). The more detailed and specific you are, the faster we will be able to help you. Check out [How to Report Bugs Effectively](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html). 14 | 15 | Please be kind, remember that Ego comes at no cost to you, and you're getting free help. 16 | 17 | ## Discuss your design 18 | 19 | The project welcomes submissions but please let everyone know what you're working on if you want to change or add something to the Ego repositories. 20 | 21 | Before starting to write something new for the Ego project, please [file an issue](https://github.com/go-ego/ego/issues/new). Significant changes must go through the [change proposal process](https://github.com/go-ego/proposals) before they can be accepted. 22 | 23 | This process gives everyone a chance to validate the design, helps prevent duplication of effort, and ensures that the idea fits inside the goals for the project and tools. It also checks that the design is sound before code is written; the code review tool is not the place for high-level discussions. 24 | 25 | ## Testing redux 26 | 27 | Before sending code out for review, run all the tests for the whole tree to make sure the changes don't break other usage and keep the compatibility on upgrade. You must be test on Mac, Windows, Linux and other. You should install the CLI for Circle CI, as we are using the server for continous testing. 28 | 29 | ## Code review 30 | 31 | In addition to the owner, Changes to Ego must be reviewed before they are accepted, no matter who makes the change even if it is a maintainer. We use GitHub's pull request workflow to do that and we also use [LGTM](http://lgtm.co) to ensure every PR is reviewed by vz or least 2 maintainers. 32 | 33 | 34 | ## Sign your work 35 | 36 | The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. 37 | 38 | ## Maintainers 39 | 40 | To make sure every PR is checked, we got team maintainers. A maintainer should be a contributor of Ego and contributed at least 4 accepted PRs. 41 | 42 | ## Owners 43 | 44 | Since Ego is a pure community organization without any company support, Copyright 2016-2017 The go-ego Project Developers. 45 | 46 | 47 | ## Versions 48 | 49 | Ego has the `master` branch as a tip branch and has version branches such as `v0.30.0`. `v0.40.0` is a release branch and we will tag `v0.40.0` for binary download. If `v0.40.0` has bugs, we will accept pull requests on the `v0.40.0` branch and publish a `v0.40.1` tag, after bringing the bug fix also to the master branch. 50 | 51 | Since the `master` branch is a tip version, if you wish to use Ego in production, please download the latest release tag version. All the branches will be protected via GitHub, all the PRs to every branch must be reviewed by two maintainers and must pass the automatic tests. 52 | 53 | ## Copyright 54 | 55 | Code that you contribute should use the standard copyright header: 56 | 57 | ``` 58 | // Copyright 2016-2017 The go-ego Project Developers. See the COPYRIGHT 59 | // file at the top-level directory of this distribution and at 60 | // https://github.com/go-ego/ego/blob/master/LICENSE 61 | // 62 | // Licensed under the Apache License, Version 2.0 or the MIT license 64 | // , at your 65 | // option. This file may not be copied, modified, or distributed 66 | // except according to those terms. 67 | ``` 68 | 69 | Files in the repository contain copyright from the year they are added to the year they are last changed. If the copyright author is changed, just paste the header below the old one. 70 | -------------------------------------------------------------------------------- /mid/logger.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package mid 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "os" 11 | "time" 12 | 13 | "github.com/go-ego/ego" 14 | "github.com/go-ego/ego/mid/util" 15 | 16 | "github.com/mattn/go-isatty" 17 | ) 18 | 19 | var ( 20 | green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109}) 21 | white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109}) 22 | yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109}) 23 | red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109}) 24 | blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109}) 25 | magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109}) 26 | cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109}) 27 | reset = string([]byte{27, 91, 48, 109}) 28 | disableColor = false 29 | ) 30 | 31 | // DisableConsoleColor disables color output in the console. 32 | func DisableConsoleColor() { 33 | disableColor = true 34 | } 35 | 36 | // ErrorLogger returns a handlerfunc for any error type. 37 | func ErrorLogger() ego.HandlerFunc { 38 | return ErrorLoggerT(util.ErrorTypeAny) 39 | } 40 | 41 | // ErrorLoggerT returns a handlerfunc for a given error type. 42 | func ErrorLoggerT(typ util.ErrorType) ego.HandlerFunc { 43 | return func(c *ego.Context) { 44 | c.Next() 45 | errors := c.Errors.ByType(typ) 46 | if len(errors) > 0 { 47 | c.JSON(-1, errors) 48 | } 49 | } 50 | } 51 | 52 | // Logger instances a Logger middleware that will write the logs to mid.DefaultWriter. 53 | // By default mid.DefaultWriter = os.Stdout. 54 | func Logger() ego.HandlerFunc { 55 | return LoggerWithWriter(ego.DefaultWriter) 56 | } 57 | 58 | // LoggerWithWriter instance a Logger middleware with the specified writter buffer. 59 | // Example: os.Stdout, a file opened in write mode, a socket... 60 | func LoggerWithWriter(out io.Writer, notlogged ...string) ego.HandlerFunc { 61 | isTerm := true 62 | 63 | // if w, ok := out.(*os.File); !ok || !isatty.IsTerminal(w.Fd()) { 64 | if w, ok := out.(*os.File); !ok || 65 | (os.Getenv("TERM") == "dumb" || (!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd()))) || 66 | disableColor { 67 | isTerm = false 68 | } 69 | 70 | var skip map[string]struct{} 71 | 72 | if length := len(notlogged); length > 0 { 73 | skip = make(map[string]struct{}, length) 74 | 75 | for _, path := range notlogged { 76 | skip[path] = struct{}{} 77 | } 78 | } 79 | 80 | return func(c *ego.Context) { 81 | // Start timer 82 | start := time.Now() 83 | path := c.Request.URL.Path 84 | raw := c.Request.URL.RawQuery 85 | 86 | // Process request 87 | c.Next() 88 | 89 | // Log only when path is not being skipped 90 | if _, ok := skip[path]; !ok { 91 | // Stop timer 92 | end := time.Now() 93 | latency := end.Sub(start) 94 | 95 | clientIP := c.ClientIP() 96 | // method := Colorize(c.Request.Method, "note") 97 | method := c.Request.Method 98 | statusCode := c.Writer.Status() 99 | var statusColor, methodColor, resetColor string 100 | if isTerm { 101 | statusColor = colorForStatus(statusCode) 102 | methodColor = colorForMethod(method) 103 | resetColor = reset 104 | } 105 | comment := c.Errors.ByType(util.ErrorTypePrivate).String() 106 | 107 | if raw != "" { 108 | path = path + "?" + raw 109 | } 110 | // fmt.Fprintf(out, "[EGO] %v |%s %3d %s| %13v | %15s |%s %s %-7s %s\n%s", 111 | fmt.Fprintf(out, "[EGO] %v |%s %3d %s| %13v | %15s |%s %-7s %s %s\n%s", 112 | end.Format("2006/01/02 - 15:04:05"), 113 | statusColor, statusCode, resetColor, 114 | latency, 115 | clientIP, 116 | methodColor, method, resetColor, 117 | path, 118 | comment, 119 | ) 120 | } 121 | } 122 | } 123 | 124 | func colorForStatus(code int) string { 125 | switch { 126 | case code >= 200 && code < 300: 127 | return green 128 | case code >= 300 && code < 400: 129 | return white 130 | case code >= 400 && code < 500: 131 | return yellow 132 | default: 133 | return red 134 | } 135 | } 136 | 137 | func colorForMethod(method string) string { 138 | switch method { 139 | case "GET": 140 | return blue 141 | case "POST": 142 | return cyan 143 | case "PUT": 144 | return yellow 145 | case "DELETE": 146 | return red 147 | case "PATCH": 148 | return green 149 | case "HEAD": 150 | return magenta 151 | case "OPTIONS": 152 | return white 153 | default: 154 | return reset 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /test/benchmarks_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "html/template" 5 | "net/http" 6 | "os" 7 | "testing" 8 | 9 | "github.com/go-ego/ego" 10 | ) 11 | 12 | func BenchmarkOneRoute(B *testing.B) { 13 | router := ego.New() 14 | router.GET("/ping", func(c *ego.Context) {}) 15 | runRequest(B, router, "GET", "/ping") 16 | } 17 | 18 | func BenchmarkRecoveryMiddleware(B *testing.B) { 19 | router := ego.New() 20 | router.Use(ego.Recovery()) 21 | router.GET("/", func(c *ego.Context) {}) 22 | runRequest(B, router, "GET", "/") 23 | } 24 | 25 | func BenchmarkLoggerMiddleware(B *testing.B) { 26 | router := ego.New() 27 | router.Use(ego.LoggerWithWriter(newMockWriter())) 28 | router.GET("/", func(c *ego.Context) {}) 29 | runRequest(B, router, "GET", "/") 30 | } 31 | 32 | func BenchmarkManyHandlers(B *testing.B) { 33 | router := ego.New() 34 | router.Use(ego.Recovery(), ego.LoggerWithWriter(newMockWriter())) 35 | router.Use(func(c *ego.Context) {}) 36 | router.Use(func(c *ego.Context) {}) 37 | router.GET("/ping", func(c *ego.Context) {}) 38 | runRequest(B, router, "GET", "/ping") 39 | } 40 | 41 | func Benchmark5Params(B *testing.B) { 42 | ego.DefaultWriter = os.Stdout 43 | router := ego.New() 44 | router.Use(func(c *ego.Context) {}) 45 | router.GET("/param/:param1/:params2/:param3/:param4/:param5", func(c *ego.Context) {}) 46 | runRequest(B, router, "GET", "/param/path/to/parameter/john/12345") 47 | } 48 | 49 | func BenchmarkOneRouteJSON(B *testing.B) { 50 | router := ego.New() 51 | data := struct { 52 | Status string `json:"status"` 53 | }{"ok"} 54 | router.GET("/json", func(c *ego.Context) { 55 | c.JSON(200, data) 56 | }) 57 | runRequest(B, router, "GET", "/json") 58 | } 59 | 60 | // var htmlContentType = []string{"text/html; charset=utf-8"} 61 | 62 | func BenchmarkOneRouteHTML(B *testing.B) { 63 | router := ego.New() 64 | t := template.Must(template.New("index").Parse(` 65 |

{{.}}

`)) 66 | router.SetHTMLTemplate(t) 67 | 68 | router.GET("/html", func(c *ego.Context) { 69 | c.HTML(200, "index", "hola") 70 | }) 71 | runRequest(B, router, "GET", "/html") 72 | } 73 | 74 | func BenchmarkOneRouteSet(B *testing.B) { 75 | router := ego.New() 76 | router.GET("/ping", func(c *ego.Context) { 77 | c.Set("key", "value") 78 | }) 79 | runRequest(B, router, "GET", "/ping") 80 | } 81 | 82 | func BenchmarkOneRouteString(B *testing.B) { 83 | router := ego.New() 84 | router.GET("/text", func(c *ego.Context) { 85 | c.String(200, "this is a plain text") 86 | }) 87 | runRequest(B, router, "GET", "/text") 88 | } 89 | 90 | func BenchmarkManyRoutesFist(B *testing.B) { 91 | router := ego.New() 92 | router.Any("/ping", func(c *ego.Context) {}) 93 | runRequest(B, router, "GET", "/ping") 94 | } 95 | 96 | func BenchmarkManyRoutesLast(B *testing.B) { 97 | router := ego.New() 98 | router.Any("/ping", func(c *ego.Context) {}) 99 | runRequest(B, router, "OPTIONS", "/ping") 100 | } 101 | 102 | func Benchmark404(B *testing.B) { 103 | router := ego.New() 104 | router.Any("/something", func(c *ego.Context) {}) 105 | router.NoRoute(func(c *ego.Context) {}) 106 | runRequest(B, router, "GET", "/ping") 107 | } 108 | 109 | func Benchmark404Many(B *testing.B) { 110 | router := ego.New() 111 | router.GET("/", func(c *ego.Context) {}) 112 | router.GET("/path/to/something", func(c *ego.Context) {}) 113 | router.GET("/post/:id", func(c *ego.Context) {}) 114 | router.GET("/view/:id", func(c *ego.Context) {}) 115 | router.GET("/favicon.ico", func(c *ego.Context) {}) 116 | router.GET("/robots.txt", func(c *ego.Context) {}) 117 | router.GET("/delete/:id", func(c *ego.Context) {}) 118 | router.GET("/user/:id/:mode", func(c *ego.Context) {}) 119 | 120 | router.NoRoute(func(c *ego.Context) {}) 121 | runRequest(B, router, "GET", "/viewfake") 122 | } 123 | 124 | type mockWriter struct { 125 | headers http.Header 126 | } 127 | 128 | func newMockWriter() *mockWriter { 129 | return &mockWriter{ 130 | http.Header{}, 131 | } 132 | } 133 | 134 | func (m *mockWriter) Header() (h http.Header) { 135 | return m.headers 136 | } 137 | 138 | func (m *mockWriter) Write(p []byte) (n int, err error) { 139 | return len(p), nil 140 | } 141 | 142 | func (m *mockWriter) WriteString(s string) (n int, err error) { 143 | return len(s), nil 144 | } 145 | 146 | func (m *mockWriter) WriteHeader(int) {} 147 | 148 | func runRequest(B *testing.B, r *ego.Engine, method, path string) { 149 | // create fake request 150 | req, err := http.NewRequest(method, path, nil) 151 | if err != nil { 152 | panic(err) 153 | } 154 | w := newMockWriter() 155 | B.ReportAllocs() 156 | B.ResetTimer() 157 | for i := 0; i < B.N; i++ { 158 | r.ServeHTTP(w, req) 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /mid/rego/file.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | 13 | import ( 14 | "fmt" 15 | "io" 16 | "io/ioutil" 17 | "os" 18 | "path" 19 | "path/filepath" 20 | "strings" 21 | ) 22 | 23 | func fileExist(filename string) bool { 24 | _, err := os.Stat(filename) 25 | return err == nil || os.IsExist(err) 26 | } 27 | 28 | func PathExists(path string) (bool, error) { 29 | _, err := os.Stat(path) 30 | if err == nil { 31 | return true, nil 32 | } 33 | if os.IsNotExist(err) { 34 | return false, nil 35 | } 36 | return false, err 37 | } 38 | 39 | func CopyFile(src, dst string) (w int64, err error) { 40 | srcFile, err := os.Open(src) 41 | if err != nil { 42 | return 43 | } 44 | defer srcFile.Close() 45 | // if fileExist(dst) != true { 46 | if !fileExist(dst) { 47 | Writefile("", dst) 48 | } 49 | dstFile, err := os.Create(dst) 50 | if err != nil { 51 | fmt.Println(err.Error()) 52 | return 53 | } 54 | defer dstFile.Close() 55 | return io.Copy(dstFile, srcFile) 56 | } 57 | 58 | func CopyOFile(srcName, dstName string) (written int64, err error) { 59 | src, err := os.Open(srcName) 60 | if err != nil { 61 | return 62 | } 63 | defer src.Close() 64 | dst, err := os.OpenFile(dstName, os.O_WRONLY|os.O_CREATE, 0644) 65 | if err != nil { 66 | fmt.Println(err) 67 | return 68 | } 69 | defer dst.Close() 70 | return io.Copy(dst, src) 71 | } 72 | 73 | func Readfile(fname string) (string, error) { 74 | userFile := fname 75 | fin, err := os.Open(userFile) 76 | defer fin.Close() 77 | if err != nil { 78 | fmt.Println(userFile, err) 79 | return "", err 80 | } 81 | var restr string = "" 82 | 83 | buf := make([]byte, 1024) 84 | for { 85 | n, _ := fin.Read(buf) 86 | if 0 == n { 87 | break 88 | } 89 | // os.Stdout.Write(buf[:n]) 90 | 91 | strbuf := string(buf[:n]) 92 | 93 | restr += strbuf 94 | } 95 | 96 | return restr, nil 97 | } 98 | 99 | func Writefile(writeStr string, userFile string) { 100 | os.MkdirAll(path.Dir(userFile), os.ModePerm) 101 | 102 | fout, err := os.Create(userFile) 103 | defer fout.Close() 104 | if err != nil { 105 | fmt.Println(userFile, err) 106 | return 107 | } 108 | 109 | fout.WriteString(writeStr) 110 | } 111 | 112 | func ListFile(dirPth string, suffix string) (files []string, err error) { 113 | files = make([]string, 0, 10) 114 | dir, err := ioutil.ReadDir(dirPth) 115 | if err != nil { 116 | return nil, err 117 | } 118 | PthSep := string(os.PathSeparator) 119 | suffix = strings.ToUpper(suffix) 120 | for _, fi := range dir { 121 | if fi.IsDir() { 122 | continue 123 | } 124 | if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) { 125 | files = append(files, dirPth+PthSep+fi.Name()) 126 | } 127 | } 128 | return files, nil 129 | } 130 | 131 | func ListDir(dirPth string, suffix string) (files []string, err error) { 132 | files = make([]string, 0, 10) 133 | dir, err := ioutil.ReadDir(dirPth) 134 | if err != nil { 135 | return nil, err 136 | } 137 | PthSep := string(os.PathSeparator) 138 | suffix = strings.ToUpper(suffix) 139 | for _, fi := range dir { 140 | if !fi.IsDir() { 141 | continue 142 | } 143 | if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) { 144 | files = append(files, dirPth+PthSep+fi.Name()) 145 | } 146 | } 147 | return files, nil 148 | } 149 | 150 | func WalkFile(dirPth, suffix string) (files []string, err error) { 151 | files = make([]string, 0, 30) 152 | suffix = strings.ToUpper(suffix) 153 | err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, err error) error { 154 | if fi.IsDir() { 155 | return nil 156 | } 157 | 158 | if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) { 159 | files = append(files, filename) 160 | } 161 | return nil 162 | }) 163 | return files, err 164 | } 165 | 166 | func WalkDir(dirPth, suffix string) (files []string, err error) { 167 | files = make([]string, 0, 30) 168 | suffix = strings.ToUpper(suffix) 169 | err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, err error) error { 170 | 171 | if !fi.IsDir() { 172 | return nil 173 | } 174 | 175 | if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) { 176 | files = append(files, filename) 177 | } 178 | return nil 179 | }) 180 | return files, err 181 | } 182 | -------------------------------------------------------------------------------- /mid/rego/fn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The go-ego Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution and at 3 | // https://github.com/go-ego/ego/blob/master/LICENSE 4 | // 5 | // Licensed under the Apache License, Version 2.0 or the MIT license 7 | // , at your 8 | // option. This file may not be copied, modified, or distributed 9 | // except according to those terms. 10 | 11 | package rego 12 | 13 | import ( 14 | "fmt" 15 | "log" 16 | "regexp" 17 | "strings" 18 | ) 19 | 20 | type Map map[string]interface{} 21 | 22 | func re(str string, amap Map) string { 23 | var text string 24 | var status int 25 | for k, v := range amap { 26 | status++ 27 | if status == 1 { 28 | text = strings.Replace(str, k, v.(string), -1) 29 | } else { 30 | text = strings.Replace(text, k, v.(string), -1) 31 | } 32 | } 33 | 34 | return text 35 | } 36 | 37 | func styScr(text string) ([]string, []string) { 38 | var recss []string 39 | var rejs []string 40 | 41 | sty := FindSty(text) 42 | scr := FindScr(text) 43 | 44 | for i := 0; i < len(sty); i++ { 45 | recss = append(recss, reCss(sty[i])) 46 | } 47 | 48 | for i := 0; i < len(scr); i++ { 49 | rejs = append(rejs, reJs(scr[i])) 50 | } 51 | 52 | return recss, rejs 53 | } 54 | 55 | func isOdd(i int) bool { 56 | 57 | return i%2 == 0 58 | // return (i & 1) != 0 59 | } 60 | 61 | func findstr(text, reg string) []string { 62 | regt := regexp.MustCompile(reg) 63 | // str := regt.FindAllString(text, 1) 64 | str := regt.FindAllString(text, -1) 65 | 66 | return str 67 | } 68 | 69 | func strReo(str, k, v string) string { 70 | text := strings.Replace(str, k, v, 1) 71 | return text 72 | } 73 | 74 | func strRe(str, k, v string) string { 75 | text := strings.Replace(str, k, v, -1) 76 | return text 77 | } 78 | 79 | func ReRegOne(str, reg string, val []string) string { 80 | var text string 81 | 82 | find := findstr(str, reg) 83 | 84 | for i := 0; i < len(find); i++ { 85 | if i == 0 { 86 | text = strings.Replace(str, find[i], val[i], 1) 87 | } else { 88 | text = strings.Replace(text, find[i], val[i], 1) 89 | } 90 | } 91 | 92 | return text 93 | } 94 | 95 | func ReRegNil(str, reg string) string { 96 | var text string 97 | 98 | find := findstr(str, reg) 99 | 100 | fmt.Println("find---------", find) 101 | 102 | for i := 0; i < len(find); i++ { 103 | if i == 0 { 104 | text = strings.Replace(str, find[i], "", -1) 105 | } else { 106 | text = strings.Replace(text, find[i], "", -1) 107 | } 108 | } 109 | 110 | return text 111 | } 112 | 113 | func ReReg(str, reg string, val []string) string { 114 | var text string 115 | 116 | find := findstr(str, reg) 117 | 118 | for i := 0; i < len(find); i++ { 119 | if i == 0 { 120 | text = strings.Replace(str, find[i], val[i], -1) 121 | } else { 122 | text = strings.Replace(text, find[i], val[i], -1) 123 | } 124 | } 125 | 126 | return text 127 | } 128 | 129 | func ReArro(str string, find, val []string) string { 130 | var text string 131 | 132 | for i := 0; i < len(find); i++ { 133 | if i == 0 { 134 | text = strings.Replace(str, find[i], val[i], 1) 135 | } else { 136 | text = strings.Replace(text, find[i], val[i], 1) 137 | } 138 | } 139 | 140 | return text 141 | } 142 | 143 | func ReArrOne(str string, oldFind, newVal []string) string { 144 | var text string 145 | 146 | for i := 0; i < len(oldFind); i++ { 147 | if i == 0 { 148 | text = strings.Replace(str, oldFind[i], newVal[i], 1) 149 | } else { 150 | text = strings.Replace(text, oldFind[i], newVal[i], 1) 151 | } 152 | } 153 | 154 | return text 155 | } 156 | 157 | func ReArr(str string, find, val []string) string { 158 | var text string 159 | 160 | for i := 0; i < len(find); i++ { 161 | if i == 0 { 162 | text = strings.Replace(str, find[i], val[i], -1) 163 | } else { 164 | text = strings.Replace(text, find[i], val[i], -1) 165 | } 166 | } 167 | 168 | return text 169 | } 170 | 171 | func reAllL(str, reg, rep string) string { 172 | regexp := regexp.MustCompile(reg) 173 | restr := regexp.ReplaceAllLiteralString(str, rep) 174 | 175 | return restr 176 | } 177 | 178 | func reAll(str, reg, rep string) string { 179 | regt := regexp.MustCompile(reg) 180 | restr := regt.ReplaceAllString(str, rep) 181 | 182 | return restr 183 | } 184 | 185 | func PrintErr(err error) { 186 | if err != nil { 187 | fmt.Println(err) 188 | // return false 189 | } 190 | // return true 191 | } 192 | 193 | func checkErr(err error) { 194 | if err != nil { 195 | log.Fatal(err) 196 | } 197 | } 198 | 199 | func checkError(err error) { 200 | if err != nil { 201 | log.Fatal(err) 202 | } 203 | } 204 | 205 | func useUn(obj interface{}) interface{} { 206 | return obj 207 | } 208 | -------------------------------------------------------------------------------- /routergroup_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package ego 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/go-ego/ego/mid/util" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func init() { 15 | SetMode(TestMode) 16 | } 17 | 18 | func TestRouterGroupBasic(t *testing.T) { 19 | router := New() 20 | group := router.Group("/hola", func(c *Context) {}) 21 | group.Use(func(c *Context) {}) 22 | 23 | assert.Len(t, group.Handlers, 2) 24 | assert.Equal(t, "/hola", group.BasePath()) 25 | assert.Equal(t, router, group.engine) 26 | 27 | group2 := group.Group("manu") 28 | group2.Use(func(c *Context) {}, func(c *Context) {}) 29 | 30 | assert.Len(t, group2.Handlers, 4) 31 | assert.Equal(t, "/hola/manu", group2.BasePath()) 32 | assert.Equal(t, router, group2.engine) 33 | } 34 | 35 | func TestRouterGroupBasicHandle(t *testing.T) { 36 | performRequestInGroup(t, "GET") 37 | performRequestInGroup(t, "POST") 38 | performRequestInGroup(t, "PUT") 39 | performRequestInGroup(t, "PATCH") 40 | performRequestInGroup(t, "DELETE") 41 | performRequestInGroup(t, "HEAD") 42 | performRequestInGroup(t, "OPTIONS") 43 | } 44 | 45 | func performRequestInGroup(t *testing.T, method string) { 46 | router := New() 47 | v1 := router.Group("v1", func(c *Context) {}) 48 | assert.Equal(t, "/v1", v1.BasePath()) 49 | 50 | login := v1.Group("/login/", func(c *Context) {}, func(c *Context) {}) 51 | assert.Equal(t, "/v1/login/", login.BasePath()) 52 | 53 | handler := func(c *Context) { 54 | c.String(400, "the method was %s and index %d", c.Request.Method, c.index) 55 | } 56 | 57 | switch method { 58 | case "GET": 59 | v1.GET("/test", handler) 60 | login.GET("/test", handler) 61 | case "POST": 62 | v1.POST("/test", handler) 63 | login.POST("/test", handler) 64 | case "PUT": 65 | v1.PUT("/test", handler) 66 | login.PUT("/test", handler) 67 | case "PATCH": 68 | v1.PATCH("/test", handler) 69 | login.PATCH("/test", handler) 70 | case "DELETE": 71 | v1.DELETE("/test", handler) 72 | login.DELETE("/test", handler) 73 | case "HEAD": 74 | v1.HEAD("/test", handler) 75 | login.HEAD("/test", handler) 76 | case "OPTIONS": 77 | v1.OPTIONS("/test", handler) 78 | login.OPTIONS("/test", handler) 79 | default: 80 | panic("unknown method") 81 | } 82 | 83 | w := performRequest(router, method, "/v1/login/test") 84 | assert.Equal(t, 400, w.Code) 85 | assert.Equal(t, "the method was "+method+" and index 3", w.Body.String()) 86 | 87 | w = performRequest(router, method, "/v1/test") 88 | assert.Equal(t, 400, w.Code) 89 | assert.Equal(t, "the method was "+method+" and index 1", w.Body.String()) 90 | } 91 | 92 | func TestRouterGroupInvalidStatic(t *testing.T) { 93 | router := New() 94 | assert.Panics(t, func() { 95 | router.Static("/path/:param", "/") 96 | }) 97 | 98 | assert.Panics(t, func() { 99 | router.Static("/path/*param", "/") 100 | }) 101 | } 102 | 103 | func TestRouterGroupInvalidStaticFile(t *testing.T) { 104 | router := New() 105 | assert.Panics(t, func() { 106 | router.StaticFile("/path/:param", "favicon.ico") 107 | }) 108 | 109 | assert.Panics(t, func() { 110 | router.StaticFile("/path/*param", "favicon.ico") 111 | }) 112 | } 113 | 114 | func TestRouterGroupTooManyHandlers(t *testing.T) { 115 | router := New() 116 | handlers1 := make([]HandlerFunc, 40) 117 | router.Use(handlers1...) 118 | 119 | handlers2 := make([]HandlerFunc, 26) 120 | assert.Panics(t, func() { 121 | router.Use(handlers2...) 122 | }) 123 | assert.Panics(t, func() { 124 | router.GET("/", handlers2...) 125 | }) 126 | } 127 | 128 | func TestRouterGroupBadMethod(t *testing.T) { 129 | router := New() 130 | assert.Panics(t, func() { 131 | router.Handle("get", "/") 132 | }) 133 | assert.Panics(t, func() { 134 | router.Handle(" GET", "/") 135 | }) 136 | assert.Panics(t, func() { 137 | router.Handle("GET ", "/") 138 | }) 139 | assert.Panics(t, func() { 140 | router.Handle("", "/") 141 | }) 142 | assert.Panics(t, func() { 143 | router.Handle("PO ST", "/") 144 | }) 145 | assert.Panics(t, func() { 146 | router.Handle("1GET", "/") 147 | }) 148 | assert.Panics(t, func() { 149 | router.Handle("PATCh", "/") 150 | }) 151 | } 152 | 153 | func TestRouterGroupPipeline(t *testing.T) { 154 | router := New() 155 | testRoutesInterface(t, router) 156 | 157 | v1 := router.Group("/v1") 158 | testRoutesInterface(t, v1) 159 | } 160 | 161 | func testRoutesInterface(t *testing.T, r IRoutes) { 162 | handler := func(c *Context) {} 163 | assert.Equal(t, r, r.Use(handler)) 164 | 165 | assert.Equal(t, r, r.Handle("GET", "/handler", handler)) 166 | assert.Equal(t, r, r.Any("/any", handler)) 167 | assert.Equal(t, r, r.GET("/", handler)) 168 | assert.Equal(t, r, r.POST("/", handler)) 169 | assert.Equal(t, r, r.DELETE("/", handler)) 170 | assert.Equal(t, r, r.PATCH("/", handler)) 171 | assert.Equal(t, r, r.PUT("/", handler)) 172 | assert.Equal(t, r, r.OPTIONS("/", handler)) 173 | assert.Equal(t, r, r.HEAD("/", handler)) 174 | 175 | assert.Equal(t, r, r.StaticFile("/file", ".")) 176 | assert.Equal(t, r, r.Static("/static", ".")) 177 | assert.Equal(t, r, r.StaticFS("/static2", util.Dir(".", false))) 178 | } 179 | -------------------------------------------------------------------------------- /egoS/egos.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package egoS 6 | 7 | import ( 8 | "html/template" 9 | "net/http" 10 | "sync" 11 | 12 | "github.com/go-ego/ego" 13 | ) 14 | 15 | var ( 16 | once sync.Once 17 | internalEngine *ego.Engine 18 | ) 19 | 20 | func engine() *ego.Engine { 21 | once.Do(func() { 22 | internalEngine = ego.Default() 23 | }) 24 | return internalEngine 25 | } 26 | 27 | func LoadHTMLGlob(pattern string) { 28 | engine().LoadHTMLGlob(pattern) 29 | } 30 | 31 | func LoadHTMLFiles(files ...string) { 32 | engine().LoadHTMLFiles(files...) 33 | } 34 | 35 | func SetHTMLTemplate(templ *template.Template) { 36 | engine().SetHTMLTemplate(templ) 37 | } 38 | 39 | // NoRoute adds handlers for NoRoute. It return a 404 code by default. 40 | func NoRoute(handlers ...ego.HandlerFunc) { 41 | engine().NoRoute(handlers...) 42 | } 43 | 44 | // NoMethod sets the handlers called when... TODO 45 | func NoMethod(handlers ...ego.HandlerFunc) { 46 | engine().NoMethod(handlers...) 47 | } 48 | 49 | // Group creates a new router group. You should add all the routes that have common middlwares or the same path prefix. 50 | // For example, all the routes that use a common middlware for authorization could be grouped. 51 | func Group(relativePath string, handlers ...ego.HandlerFunc) *ego.RouterGroup { 52 | return engine().Group(relativePath, handlers...) 53 | } 54 | 55 | func Handle(httpMethod, relativePath string, handlers ...ego.HandlerFunc) ego.IRoutes { 56 | return engine().Handle(httpMethod, relativePath, handlers...) 57 | } 58 | 59 | // POST is a shortcut for router.Handle("POST", path, handle) 60 | func POST(relativePath string, handlers ...ego.HandlerFunc) ego.IRoutes { 61 | return engine().POST(relativePath, handlers...) 62 | } 63 | 64 | // GET is a shortcut for router.Handle("GET", path, handle) 65 | func GET(relativePath string, handlers ...ego.HandlerFunc) ego.IRoutes { 66 | return engine().GET(relativePath, handlers...) 67 | } 68 | 69 | // DELETE is a shortcut for router.Handle("DELETE", path, handle) 70 | func DELETE(relativePath string, handlers ...ego.HandlerFunc) ego.IRoutes { 71 | return engine().DELETE(relativePath, handlers...) 72 | } 73 | 74 | // PATCH is a shortcut for router.Handle("PATCH", path, handle) 75 | func PATCH(relativePath string, handlers ...ego.HandlerFunc) ego.IRoutes { 76 | return engine().PATCH(relativePath, handlers...) 77 | } 78 | 79 | // PUT is a shortcut for router.Handle("PUT", path, handle) 80 | func PUT(relativePath string, handlers ...ego.HandlerFunc) ego.IRoutes { 81 | return engine().PUT(relativePath, handlers...) 82 | } 83 | 84 | // OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle) 85 | func OPTIONS(relativePath string, handlers ...ego.HandlerFunc) ego.IRoutes { 86 | return engine().OPTIONS(relativePath, handlers...) 87 | } 88 | 89 | // HEAD is a shortcut for router.Handle("HEAD", path, handle) 90 | func HEAD(relativePath string, handlers ...ego.HandlerFunc) ego.IRoutes { 91 | return engine().HEAD(relativePath, handlers...) 92 | } 93 | 94 | func Any(relativePath string, handlers ...ego.HandlerFunc) ego.IRoutes { 95 | return engine().Any(relativePath, handlers...) 96 | } 97 | 98 | func StaticFile(relativePath, filepath string) ego.IRoutes { 99 | return engine().StaticFile(relativePath, filepath) 100 | } 101 | 102 | // Static serves files from the given file system root. 103 | // Internally a http.FileServer is used, therefore http.NotFound is used instead 104 | // of the Router's NotFound handler. 105 | // To use the operating system's file system implementation, 106 | // use : 107 | // router.Static("/static", "/var/www") 108 | func Static(relativePath, root string) ego.IRoutes { 109 | return engine().Static(relativePath, root) 110 | } 111 | 112 | func StaticFS(relativePath string, fs http.FileSystem) ego.IRoutes { 113 | return engine().StaticFS(relativePath, fs) 114 | } 115 | 116 | // Use attachs a global middleware to the router. ie. the middlewares attached though Use() will be 117 | // included in the handlers chain for every single request. Even 404, 405, static files... 118 | // For example, this is the right place for a logger or error management middleware. 119 | func Use(middlewares ...ego.HandlerFunc) ego.IRoutes { 120 | return engine().Use(middlewares...) 121 | } 122 | 123 | // Run : The router is attached to a http.Server and starts listening and serving HTTP requests. 124 | // It is a shortcut for http.ListenAndServe(addr, router) 125 | // Note: this method will block the calling goroutine undefinitelly unless an error happens. 126 | func Run(addr ...string) (err error) { 127 | return engine().Run(addr...) 128 | } 129 | 130 | // RunTLS : The router is attached to a http.Server and starts listening and serving HTTPS requests. 131 | // It is a shortcut for http.ListenAndServeTLS(addr, certFile, keyFile, router) 132 | // Note: this method will block the calling goroutine undefinitelly unless an error happens. 133 | func RunTLS(addr, certFile, keyFile string) (err error) { 134 | return engine().RunTLS(addr, certFile, keyFile) 135 | } 136 | 137 | // RunUnix : The router is attached to a http.Server and starts listening and serving HTTP requests 138 | // through the specified unix socket (ie. a file) 139 | // Note: this method will block the calling goroutine undefinitelly unless an error happens. 140 | func RunUnix(file string) (err error) { 141 | return engine().RunUnix(file) 142 | } 143 | -------------------------------------------------------------------------------- /examples/grpc/pb/helloworld.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. 2 | // source: helloworld.proto 3 | // DO NOT EDIT! 4 | 5 | /* 6 | Package helloworld is a generated protocol buffer package. 7 | 8 | It is generated from these files: 9 | helloworld.proto 10 | 11 | It has these top-level messages: 12 | HelloRequest 13 | HelloReply 14 | */ 15 | package helloworld 16 | 17 | import proto "github.com/golang/protobuf/proto" 18 | import fmt "fmt" 19 | import math "math" 20 | 21 | import ( 22 | context "golang.org/x/net/context" 23 | grpc "google.golang.org/grpc" 24 | ) 25 | 26 | // Reference imports to suppress errors if they are not otherwise used. 27 | var _ = proto.Marshal 28 | var _ = fmt.Errorf 29 | var _ = math.Inf 30 | 31 | // This is a compile-time assertion to ensure that this generated file 32 | // is compatible with the proto package it is being compiled against. 33 | // A compilation error at this line likely means your copy of the 34 | // proto package needs to be updated. 35 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package 36 | 37 | // The request message containing the user's name. 38 | type HelloRequest struct { 39 | Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 40 | } 41 | 42 | func (m *HelloRequest) Reset() { *m = HelloRequest{} } 43 | func (m *HelloRequest) String() string { return proto.CompactTextString(m) } 44 | func (*HelloRequest) ProtoMessage() {} 45 | func (*HelloRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } 46 | 47 | // The response message containing the greetings 48 | type HelloReply struct { 49 | Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` 50 | } 51 | 52 | func (m *HelloReply) Reset() { *m = HelloReply{} } 53 | func (m *HelloReply) String() string { return proto.CompactTextString(m) } 54 | func (*HelloReply) ProtoMessage() {} 55 | func (*HelloReply) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } 56 | 57 | func init() { 58 | proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest") 59 | proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply") 60 | } 61 | 62 | // Reference imports to suppress errors if they are not otherwise used. 63 | var _ context.Context 64 | var _ grpc.ClientConn 65 | 66 | // This is a compile-time assertion to ensure that this generated file 67 | // is compatible with the grpc package it is being compiled against. 68 | const _ = grpc.SupportPackageIsVersion4 69 | 70 | // Client API for Greeter service 71 | 72 | type GreeterClient interface { 73 | // Sends a greeting 74 | SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) 75 | } 76 | 77 | type greeterClient struct { 78 | cc *grpc.ClientConn 79 | } 80 | 81 | func NewGreeterClient(cc *grpc.ClientConn) GreeterClient { 82 | return &greeterClient{cc} 83 | } 84 | 85 | func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { 86 | out := new(HelloReply) 87 | err := grpc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, c.cc, opts...) 88 | if err != nil { 89 | return nil, err 90 | } 91 | return out, nil 92 | } 93 | 94 | // Server API for Greeter service 95 | 96 | type GreeterServer interface { 97 | // Sends a greeting 98 | SayHello(context.Context, *HelloRequest) (*HelloReply, error) 99 | } 100 | 101 | func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { 102 | s.RegisterService(&_Greeter_serviceDesc, srv) 103 | } 104 | 105 | func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 106 | in := new(HelloRequest) 107 | if err := dec(in); err != nil { 108 | return nil, err 109 | } 110 | if interceptor == nil { 111 | return srv.(GreeterServer).SayHello(ctx, in) 112 | } 113 | info := &grpc.UnaryServerInfo{ 114 | Server: srv, 115 | FullMethod: "/helloworld.Greeter/SayHello", 116 | } 117 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 118 | return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) 119 | } 120 | return interceptor(ctx, in, info, handler) 121 | } 122 | 123 | var _Greeter_serviceDesc = grpc.ServiceDesc{ 124 | ServiceName: "helloworld.Greeter", 125 | HandlerType: (*GreeterServer)(nil), 126 | Methods: []grpc.MethodDesc{ 127 | { 128 | MethodName: "SayHello", 129 | Handler: _Greeter_SayHello_Handler, 130 | }, 131 | }, 132 | Streams: []grpc.StreamDesc{}, 133 | Metadata: "helloworld.proto", 134 | } 135 | 136 | func init() { proto.RegisterFile("helloworld.proto", fileDescriptor0) } 137 | 138 | var fileDescriptor0 = []byte{ 139 | // 174 bytes of a gzipped FileDescriptorProto 140 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9, 141 | 0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88, 142 | 0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42, 143 | 0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92, 144 | 0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71, 145 | 0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a, 146 | 0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64, 147 | 0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x00, 0xad, 0x50, 0x62, 0x70, 0x32, 0xe0, 0x92, 0xce, 0xcc, 0xd7, 148 | 0x4b, 0x2f, 0x2a, 0x48, 0xd6, 0x4b, 0xad, 0x48, 0xcc, 0x2d, 0xc8, 0x49, 0x2d, 0x46, 0x52, 0xeb, 149 | 0xc4, 0x0f, 0x56, 0x1c, 0x0e, 0x62, 0x07, 0x80, 0xbc, 0x14, 0xc0, 0x98, 0xc4, 0x06, 0xf6, 0x9b, 150 | 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x0f, 0xb7, 0xcd, 0xf2, 0xef, 0x00, 0x00, 0x00, 151 | } 152 | --------------------------------------------------------------------------------