mirror of
https://github.com/gofiber/fiber.git
synced 2025-02-21 04:52:38 +00:00
v2.0.0
This commit is contained in:
parent
bdaac2d116
commit
813850c83c
4
.github/README.md
vendored
4
.github/README.md
vendored
@ -208,11 +208,11 @@ func main() {
|
||||
app := fiber.New()
|
||||
|
||||
app.Static("/public")
|
||||
|
||||
|
||||
app.Get("/demo", func(c *fiber.Ctx) {
|
||||
c.Send("This is a demo!")
|
||||
})
|
||||
|
||||
|
||||
app.Post("/register", func(c *fiber.Ctx) {
|
||||
c.Send("Welcome!")
|
||||
})
|
||||
|
4
.github/README_de.md
vendored
4
.github/README_de.md
vendored
@ -1,8 +1,8 @@
|
||||
<p align="center">
|
||||
<a href="https://fiber.wiki">
|
||||
<img alt="Fiber" height="100" src="https://github.com/gofiber/docs/blob/master/static/logo.svg">
|
||||
<img alt="Fiber" height="125" src="https://github.com/gofiber/docs/blob/master/static/fiber_v2_logo.svg">
|
||||
</a>
|
||||
<br><br>
|
||||
<br>
|
||||
<a href="https://github.com/gofiber/fiber/blob/master/.github/README.md">
|
||||
<img height="20px" src="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.4.6/flags/4x3/gb.svg">
|
||||
</a>
|
||||
|
4
.github/README_es.md
vendored
4
.github/README_es.md
vendored
@ -1,8 +1,8 @@
|
||||
<p align="center">
|
||||
<a href="https://fiber.wiki">
|
||||
<img alt="Fiber" height="100" src="https://github.com/gofiber/docs/blob/master/static/logo.svg">
|
||||
<img alt="Fiber" height="125" src="https://github.com/gofiber/docs/blob/master/static/fiber_v2_logo.svg">
|
||||
</a>
|
||||
<br><br>
|
||||
<br>
|
||||
<a href="https://github.com/gofiber/fiber/blob/master/.github/README.md">
|
||||
<img height="20px" src="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.4.6/flags/4x3/gb.svg">
|
||||
</a>
|
||||
|
4
.github/README_fr.md
vendored
4
.github/README_fr.md
vendored
@ -1,8 +1,8 @@
|
||||
<p align="center">
|
||||
<a href="https://fiber.wiki">
|
||||
<img alt="Fiber" height="100" src="https://github.com/gofiber/docs/blob/master/static/logo.svg">
|
||||
<img alt="Fiber" height="125" src="https://github.com/gofiber/docs/blob/master/static/fiber_v2_logo.svg">
|
||||
</a>
|
||||
<br><br>
|
||||
<br>
|
||||
<a href="https://github.com/gofiber/fiber/blob/master/.github/README.md">
|
||||
<img height="20px" src="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.4.6/flags/4x3/gb.svg">
|
||||
</a>
|
||||
|
4
.github/README_ja.md
vendored
4
.github/README_ja.md
vendored
@ -1,8 +1,8 @@
|
||||
<p align="center">
|
||||
<a href="https://fiber.wiki">
|
||||
<img alt="Fiber" height="100" src="https://github.com/gofiber/docs/blob/master/static/logo.svg">
|
||||
<img alt="Fiber" height="125" src="https://github.com/gofiber/docs/blob/master/static/fiber_v2_logo.svg">
|
||||
</a>
|
||||
<br><br>
|
||||
<br>
|
||||
<a href="https://github.com/gofiber/fiber/blob/master/.github/README.md">
|
||||
<img height="20px" src="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.4.6/flags/4x3/gb.svg">
|
||||
</a>
|
||||
|
4
.github/README_ko.md
vendored
4
.github/README_ko.md
vendored
@ -1,8 +1,8 @@
|
||||
<p align="center">
|
||||
<a href="https://fiber.wiki">
|
||||
<img alt="Fiber" height="100" src="https://github.com/gofiber/docs/blob/master/static/logo.svg">
|
||||
<img alt="Fiber" height="125" src="https://github.com/gofiber/docs/blob/master/static/fiber_v2_logo.svg">
|
||||
</a>
|
||||
<br><br>
|
||||
<br>
|
||||
<a href="https://github.com/gofiber/fiber/blob/master/.github/README.md">
|
||||
<img height="20px" src="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.4.6/flags/4x3/gb.svg">
|
||||
</a>
|
||||
|
4
.github/README_pt.md
vendored
4
.github/README_pt.md
vendored
@ -1,8 +1,8 @@
|
||||
<p align="center">
|
||||
<a href="https://fiber.wiki">
|
||||
<img alt="Fiber" height="100" src="https://github.com/gofiber/docs/blob/master/static/logo.svg">
|
||||
<img alt="Fiber" height="125" src="https://github.com/gofiber/docs/blob/master/static/fiber_v2_logo.svg">
|
||||
</a>
|
||||
<br><br>
|
||||
<br>
|
||||
<a href="https://github.com/gofiber/fiber/blob/master/.github/README.md">
|
||||
<img height="20px" src="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.4.6/flags/4x3/gb.svg">
|
||||
</a>
|
||||
|
8
.github/README_ru.md
vendored
8
.github/README_ru.md
vendored
@ -1,8 +1,8 @@
|
||||
<p align="center">
|
||||
<a href="https://fiber.wiki">
|
||||
<img alt="Fiber" height="100" src="https://github.com/gofiber/docs/blob/master/static/logo.svg">
|
||||
<img alt="Fiber" height="125" src="https://github.com/gofiber/docs/blob/master/static/fiber_v2_logo.svg">
|
||||
</a>
|
||||
<br><br>
|
||||
<br>
|
||||
<a href="https://github.com/gofiber/fiber/blob/master/.github/README.md">
|
||||
<img height="20px" src="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.4.6/flags/4x3/gb.svg">
|
||||
</a>
|
||||
@ -208,11 +208,11 @@ func main() {
|
||||
app := fiber.New()
|
||||
|
||||
app.Static("/public")
|
||||
|
||||
|
||||
app.Get("/demo", func(c *fiber.Ctx) {
|
||||
c.Send("This is a demo!")
|
||||
})
|
||||
|
||||
|
||||
app.Post("/register", func(c *fiber.Ctx) {
|
||||
c.Send("Welcome!")
|
||||
})
|
||||
|
4
.github/README_tr.md
vendored
4
.github/README_tr.md
vendored
@ -1,8 +1,8 @@
|
||||
<p align="center">
|
||||
<a href="https://fiber.wiki">
|
||||
<img alt="Fiber" height="100" src="https://github.com/gofiber/docs/blob/master/static/logo.svg">
|
||||
<img alt="Fiber" height="125" src="https://github.com/gofiber/docs/blob/master/static/fiber_v2_logo.svg">
|
||||
</a>
|
||||
<br><br>
|
||||
<br>
|
||||
<a href="https://github.com/gofiber/fiber/blob/master/.github/README.md">
|
||||
<img height="20px" src="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.4.6/flags/4x3/gb.svg">
|
||||
</a>
|
||||
|
4
.github/README_zh-CN.md
vendored
4
.github/README_zh-CN.md
vendored
@ -1,8 +1,8 @@
|
||||
<p align="center">
|
||||
<a href="https://fiber.wiki">
|
||||
<img alt="Fiber" height="100" src="https://github.com/gofiber/docs/blob/master/static/logo.svg">
|
||||
<img alt="Fiber" height="125" src="https://github.com/gofiber/docs/blob/master/static/fiber_v2_logo.svg">
|
||||
</a>
|
||||
<br><br>
|
||||
<br>
|
||||
<a href="https://github.com/gofiber/fiber/blob/master/.github/README.md">
|
||||
<img height="20px" src="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.4.6/flags/4x3/gb.svg">
|
||||
</a>
|
||||
|
18
.github/workflows/security.yml
vendored
Normal file
18
.github/workflows/security.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
on: [push]
|
||||
name: Security
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.13.x]
|
||||
platform: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Security
|
||||
run: go get github.com/securego/gosec/cmd/gosec; `go env GOPATH`/bin/gosec ./...
|
367
app.go
Normal file
367
app.go
Normal file
@ -0,0 +1,367 @@
|
||||
package fiber
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
fasthttp "github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Version of Fiber
|
||||
const Version = "2.0.0"
|
||||
|
||||
type (
|
||||
// App denotes the Fiber application.
|
||||
App struct {
|
||||
server *fasthttp.Server
|
||||
routes []*Route
|
||||
child bool
|
||||
recover func(*Ctx)
|
||||
Settings *Settings
|
||||
}
|
||||
// Map defines a generic map of type `map[string]interface{}`.
|
||||
Map map[string]interface{}
|
||||
// Settings is a struct holding the server settings
|
||||
Settings struct {
|
||||
// fiber settings
|
||||
Prefork bool `default:"false"`
|
||||
// Enable strict routing. When enabled, the router treats "/foo" and "/foo/" as different. Otherwise, the router treats "/foo" and "/foo/" as the same.
|
||||
StrictRouting bool `default:"false"`
|
||||
// Enable case sensitivity. When enabled, "/Foo" and "/foo" are different routes. When disabled, "/Foo" and "/foo" are treated the same.
|
||||
CaseSensitive bool `default:"false"`
|
||||
// Enables the "Server: value" HTTP header.
|
||||
ServerHeader string `default:""`
|
||||
// fasthttp settings
|
||||
GETOnly bool `default:"false"`
|
||||
IdleTimeout time.Duration `default:"0"`
|
||||
Concurrency int `default:"256 * 1024"`
|
||||
ReadTimeout time.Duration `default:"0"`
|
||||
WriteTimeout time.Duration `default:"0"`
|
||||
TCPKeepalive bool `default:"false"`
|
||||
MaxConnsPerIP int `default:"0"`
|
||||
ReadBufferSize int `default:"4096"`
|
||||
WriteBufferSize int `default:"4096"`
|
||||
ConcurrencySleep time.Duration `default:"0"`
|
||||
DisableKeepAlive bool `default:"false"`
|
||||
ReduceMemoryUsage bool `default:"false"`
|
||||
MaxRequestsPerConn int `default:"0"`
|
||||
TCPKeepalivePeriod time.Duration `default:"0"`
|
||||
MaxRequestBodySize int `default:"4 * 1024 * 1024"`
|
||||
NoHeaderNormalizing bool `default:"false"`
|
||||
NoDefaultContentType bool `default:"false"`
|
||||
// template settings
|
||||
ViewCache bool `default:"false"`
|
||||
ViewFolder string `default:""`
|
||||
ViewEngine string `default:""`
|
||||
ViewExtension string `default:""`
|
||||
}
|
||||
)
|
||||
|
||||
var prefork, child bool
|
||||
|
||||
func regBoolVar(p *bool, name string, value bool, usage string) {
|
||||
if flag.Lookup(name) == nil {
|
||||
flag.BoolVar(p, name, value, usage)
|
||||
}
|
||||
}
|
||||
func getBoolFlag(name string) bool {
|
||||
return flag.Lookup(name).Value.(flag.Getter).Get().(bool)
|
||||
}
|
||||
func init() {
|
||||
regBoolVar(&prefork, "prefork", false, "use prefork")
|
||||
regBoolVar(&child, "child", false, "is child process")
|
||||
}
|
||||
|
||||
// New ...
|
||||
func New(settings ...*Settings) (app *App) {
|
||||
flag.Parse()
|
||||
prefork = getBoolFlag("prefork")
|
||||
child = getBoolFlag("child")
|
||||
|
||||
app = &App{
|
||||
child: child,
|
||||
}
|
||||
if len(settings) > 0 {
|
||||
opt := settings[0]
|
||||
if !opt.Prefork {
|
||||
opt.Prefork = prefork
|
||||
}
|
||||
if opt.Concurrency == 0 {
|
||||
opt.Concurrency = 256 * 1024
|
||||
}
|
||||
if opt.ReadBufferSize == 0 {
|
||||
opt.ReadBufferSize = 4096
|
||||
}
|
||||
if opt.WriteBufferSize == 0 {
|
||||
opt.WriteBufferSize = 4096
|
||||
}
|
||||
if opt.MaxRequestBodySize == 0 {
|
||||
opt.MaxRequestBodySize = 4 * 1024 * 1024
|
||||
}
|
||||
app.Settings = opt
|
||||
return
|
||||
}
|
||||
app.Settings = &Settings{
|
||||
Prefork: prefork,
|
||||
Concurrency: 256 * 1024,
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
MaxRequestBodySize: 4 * 1024 * 1024,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Static ...
|
||||
func (app *App) Static(args ...string) *App {
|
||||
app.registerStatic("/", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// WebSocket ...
|
||||
func (app *App) WebSocket(args ...interface{}) *App {
|
||||
app.register("GET", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Connect ...
|
||||
func (app *App) Connect(args ...interface{}) *App {
|
||||
app.register("CONNECT", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Put ...
|
||||
func (app *App) Put(args ...interface{}) *App {
|
||||
app.register("PUT", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Post ...
|
||||
func (app *App) Post(args ...interface{}) *App {
|
||||
app.register("POST", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (app *App) Delete(args ...interface{}) *App {
|
||||
app.register("DELETE", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Head ...
|
||||
func (app *App) Head(args ...interface{}) *App {
|
||||
app.register("HEAD", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Patch ...
|
||||
func (app *App) Patch(args ...interface{}) *App {
|
||||
app.register("PATCH", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Options ...
|
||||
func (app *App) Options(args ...interface{}) *App {
|
||||
app.register("OPTIONS", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Trace ...
|
||||
func (app *App) Trace(args ...interface{}) *App {
|
||||
app.register("TRACE", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Get ...
|
||||
func (app *App) Get(args ...interface{}) *App {
|
||||
app.register("GET", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// All ...
|
||||
func (app *App) All(args ...interface{}) *App {
|
||||
app.register("ALL", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Use ...
|
||||
func (app *App) Use(args ...interface{}) *App {
|
||||
app.register("USE", "", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Listen : https://fiber.wiki/application#listen
|
||||
func (app *App) Listen(address interface{}, tls ...string) error {
|
||||
addr, ok := address.(string)
|
||||
if !ok {
|
||||
port, ok := address.(int)
|
||||
if !ok {
|
||||
return fmt.Errorf("Listen: Host must be an INT port or STRING address")
|
||||
}
|
||||
addr = strconv.Itoa(port)
|
||||
}
|
||||
if !strings.Contains(addr, ":") {
|
||||
addr = ":" + addr
|
||||
}
|
||||
// Create fasthttp server
|
||||
app.server = app.newServer()
|
||||
// Print banner
|
||||
// if app.Settings.Banner && !app.child {
|
||||
// fmt.Printf("Fiber-%s is listening on %s\n", Version, addr)
|
||||
// }
|
||||
var ln net.Listener
|
||||
var err error
|
||||
// Prefork enabled
|
||||
if app.Settings.Prefork && runtime.NumCPU() > 1 {
|
||||
if ln, err = app.prefork(addr); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if ln, err = net.Listen("tcp4", addr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// enable TLS/HTTPS
|
||||
if len(tls) > 1 {
|
||||
return app.server.ServeTLS(ln, tls[0], tls[1])
|
||||
}
|
||||
return app.server.Serve(ln)
|
||||
}
|
||||
|
||||
// Shutdown server gracefully
|
||||
func (app *App) Shutdown() error {
|
||||
if app.server == nil {
|
||||
return fmt.Errorf("Server is not running")
|
||||
}
|
||||
return app.server.Shutdown()
|
||||
}
|
||||
|
||||
// Test takes a http.Request and execute a fake connection to the application
|
||||
// It returns a http.Response when the connection was successful
|
||||
func (app *App) Test(req *http.Request) (*http.Response, error) {
|
||||
// Get raw http request
|
||||
reqRaw, err := httputil.DumpRequest(req, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Setup a fiber server struct
|
||||
app.server = app.newServer()
|
||||
// Create fake connection
|
||||
conn := &testConn{}
|
||||
// Pass HTTP request to conn
|
||||
_, err = conn.r.Write(reqRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Serve conn to server
|
||||
channel := make(chan error)
|
||||
go func() {
|
||||
channel <- app.server.ServeConn(conn)
|
||||
}()
|
||||
// Wait for callback
|
||||
select {
|
||||
case err := <-channel:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Throw timeout error after 200ms
|
||||
case <-time.After(1000 * time.Millisecond):
|
||||
return nil, fmt.Errorf("timeout")
|
||||
}
|
||||
// Get raw HTTP response
|
||||
respRaw, err := ioutil.ReadAll(&conn.w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Create buffer
|
||||
reader := strings.NewReader(getString(respRaw))
|
||||
buffer := bufio.NewReader(reader)
|
||||
// Convert raw HTTP response to http.Response
|
||||
resp, err := http.ReadResponse(buffer, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Return *http.Response
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/
|
||||
func (app *App) prefork(address string) (ln net.Listener, err error) {
|
||||
// Master proc
|
||||
if !app.child {
|
||||
addr, err := net.ResolveTCPAddr("tcp", address)
|
||||
if err != nil {
|
||||
return ln, err
|
||||
}
|
||||
tcplistener, err := net.ListenTCP("tcp", addr)
|
||||
if err != nil {
|
||||
return ln, err
|
||||
}
|
||||
fl, err := tcplistener.File()
|
||||
if err != nil {
|
||||
return ln, err
|
||||
}
|
||||
childs := make([]*exec.Cmd, runtime.NumCPU()/2)
|
||||
|
||||
// #nosec G204
|
||||
for i := range childs {
|
||||
childs[i] = exec.Command(os.Args[0], "-prefork", "-child")
|
||||
childs[i].Stdout = os.Stdout
|
||||
childs[i].Stderr = os.Stderr
|
||||
childs[i].ExtraFiles = []*os.File{fl}
|
||||
if err := childs[i].Start(); err != nil {
|
||||
return ln, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, child := range childs {
|
||||
if err := child.Wait(); err != nil {
|
||||
return ln, err
|
||||
}
|
||||
}
|
||||
os.Exit(0)
|
||||
} else {
|
||||
ln, err = net.FileListener(os.NewFile(3, ""))
|
||||
}
|
||||
return ln, err
|
||||
}
|
||||
|
||||
func (app *App) newServer() *fasthttp.Server {
|
||||
return &fasthttp.Server{
|
||||
Handler: app.handler,
|
||||
ErrorHandler: func(ctx *fasthttp.RequestCtx, err error) {
|
||||
ctx.Response.SetStatusCode(400)
|
||||
ctx.Response.SetBodyString("Bad Request")
|
||||
},
|
||||
Name: app.Settings.ServerHeader,
|
||||
Concurrency: app.Settings.Concurrency,
|
||||
SleepWhenConcurrencyLimitsExceeded: app.Settings.ConcurrencySleep,
|
||||
DisableKeepalive: app.Settings.DisableKeepAlive,
|
||||
ReadBufferSize: app.Settings.ReadBufferSize,
|
||||
WriteBufferSize: app.Settings.WriteBufferSize,
|
||||
ReadTimeout: app.Settings.ReadTimeout,
|
||||
WriteTimeout: app.Settings.WriteTimeout,
|
||||
IdleTimeout: app.Settings.IdleTimeout,
|
||||
MaxConnsPerIP: app.Settings.MaxConnsPerIP,
|
||||
MaxRequestsPerConn: app.Settings.MaxRequestsPerConn,
|
||||
TCPKeepalive: app.Settings.TCPKeepalive,
|
||||
TCPKeepalivePeriod: app.Settings.TCPKeepalivePeriod,
|
||||
MaxRequestBodySize: app.Settings.MaxRequestBodySize,
|
||||
ReduceMemoryUsage: app.Settings.ReduceMemoryUsage,
|
||||
GetOnly: app.Settings.GETOnly,
|
||||
DisableHeaderNamesNormalizing: app.Settings.NoHeaderNormalizing,
|
||||
NoDefaultServerHeader: app.Settings.ServerHeader == "",
|
||||
NoDefaultContentType: app.Settings.NoDefaultContentType,
|
||||
}
|
||||
}
|
@ -7,51 +7,67 @@ import (
|
||||
|
||||
var handler = func(c *Ctx) {}
|
||||
|
||||
func is200(t *testing.T, app *App, url string, m ...string) {
|
||||
method := "GET"
|
||||
if len(m) > 0 {
|
||||
method = m[0]
|
||||
}
|
||||
req, _ := http.NewRequest(method, url, nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("%s - %s - %v", method, url, err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf("%s - %s - %v", method, url, resp.StatusCode)
|
||||
}
|
||||
}
|
||||
func Test_Methods(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
methods := []string{"CONNECT", "PUT", "POST", "DELETE", "HEAD", "PATCH", "OPTIONS", "TRACE", "GET", "ALL", "USE"}
|
||||
app.Connect("", handler)
|
||||
app.Connect("/CONNECT", handler)
|
||||
app.Put("/PUT", handler)
|
||||
app.Post("/POST", handler)
|
||||
app.Delete("/DELETE", handler)
|
||||
app.Head("/HEAD", handler)
|
||||
app.Patch("/PATCH", handler)
|
||||
app.Options("/OPTIONS", handler)
|
||||
app.Trace("/TRACE", handler)
|
||||
app.Get("/GET", handler)
|
||||
app.All("/ALL", handler)
|
||||
app.Use("/USE", handler)
|
||||
app.Connect("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "CONNECT")
|
||||
|
||||
app.Connect("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "CONNECT")
|
||||
|
||||
app.Put("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "CONNECT")
|
||||
|
||||
app.Post("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "POST")
|
||||
|
||||
app.Delete("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "DELETE")
|
||||
|
||||
app.Head("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "HEAD")
|
||||
|
||||
app.Patch("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "PATCH")
|
||||
|
||||
app.Options("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "OPTIONS")
|
||||
|
||||
app.Trace("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "TRACE")
|
||||
|
||||
app.Get("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "GET")
|
||||
|
||||
app.All("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "POST")
|
||||
|
||||
app.Use("/:john?/:doe?", handler)
|
||||
is200(t, app, "/", "GET")
|
||||
|
||||
for _, method := range methods {
|
||||
var req *http.Request
|
||||
if method == "ALL" {
|
||||
req, _ = http.NewRequest("CONNECT", "/"+method, nil)
|
||||
} else if method == "USE" {
|
||||
req, _ = http.NewRequest("OPTIONS", "/"+method+"/test", nil)
|
||||
} else {
|
||||
req, _ = http.NewRequest(method, "/"+method, nil)
|
||||
}
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s %s`, t.Name(), method, err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: %s expecting 200 but received %v`, t.Name(), method, resp.StatusCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Static(t *testing.T) {
|
||||
app := New()
|
||||
grp := app.Group("/v1")
|
||||
grp.Static("/v2", ".travis.yml")
|
||||
grp.Static(".travis.yml")
|
||||
app.Static("/yesyes*", ".github/FUNDING.yml")
|
||||
app.Static("./.github")
|
||||
app.Static("github", ".github/FUNDING.yml")
|
||||
app.Static("/*", "./.github")
|
||||
app.Static("/john", "./.github")
|
||||
req, _ := http.NewRequest("GET", "/stale.yml", nil)
|
||||
resp, err := app.Test(req)
|
||||
@ -98,37 +114,53 @@ func Test_Static(t *testing.T) {
|
||||
t.Fatalf(`%s: Missing Content-Length`, t.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Group(t *testing.T) {
|
||||
app := New()
|
||||
|
||||
grp := app.Group("/test")
|
||||
grp.Get("/", handler)
|
||||
is200(t, app, "/test", "GET")
|
||||
|
||||
grp.Get("/:demo?", handler)
|
||||
is200(t, app, "/test/john", "GET")
|
||||
|
||||
grp.Connect("/CONNECT", handler)
|
||||
is200(t, app, "/test/CONNECT", "CONNECT")
|
||||
|
||||
grp.Put("/PUT", handler)
|
||||
is200(t, app, "/test/PUT", "PUT")
|
||||
|
||||
grp.Post("/POST", handler)
|
||||
is200(t, app, "/test/POST", "POST")
|
||||
|
||||
grp.Delete("/DELETE", handler)
|
||||
is200(t, app, "/test/DELETE", "DELETE")
|
||||
|
||||
grp.Head("/HEAD", handler)
|
||||
is200(t, app, "/test/HEAD", "HEAD")
|
||||
|
||||
grp.Patch("/PATCH", handler)
|
||||
is200(t, app, "/test/PATCH", "PATCH")
|
||||
|
||||
grp.Options("/OPTIONS", handler)
|
||||
is200(t, app, "/test/OPTIONS", "OPTIONS")
|
||||
|
||||
grp.Trace("/TRACE", handler)
|
||||
is200(t, app, "/test/TRACE", "TRACE")
|
||||
|
||||
grp.All("/ALL", handler)
|
||||
is200(t, app, "/test/ALL", "POST")
|
||||
|
||||
grp.Use("/USE", handler)
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
req, _ = http.NewRequest("GET", "/test/test", nil)
|
||||
resp, err = app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
is200(t, app, "/test/USE/oke", "GET")
|
||||
|
||||
api := grp.Group("/v1")
|
||||
api.Post("/", handler)
|
||||
is200(t, app, "/test/v1/", "POST")
|
||||
|
||||
api.Get("/users", handler)
|
||||
is200(t, app, "/test/v1/users", "GET")
|
||||
}
|
||||
|
||||
// func Test_Listen(t *testing.T) {
|
549
application.go
549
application.go
@ -1,549 +0,0 @@
|
||||
// 🚀 Fiber is an Express.js inspired web framework written in Go with 💖
|
||||
// 📌 Please open an issue if you got suggestions or found a bug!
|
||||
// 🖥 Links: https://github.com/gofiber/fiber, https://fiber.wiki
|
||||
|
||||
// 🦸 Not all heroes wear capes, thank you to some amazing people
|
||||
// 💖 @valyala, @erikdubbelboer, @savsgio, @julienschmidt, @koddr
|
||||
|
||||
package fiber
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
fasthttp "github.com/valyala/fasthttp"
|
||||
reuseport "github.com/valyala/fasthttp/reuseport"
|
||||
)
|
||||
|
||||
const (
|
||||
// Version : Fiber release
|
||||
Version = "1.7.0"
|
||||
// Website : Fiber website
|
||||
Website = "https://fiber.wiki"
|
||||
banner = "\x1b[1;32m" + ` ______ __ ______ ______ ______
|
||||
/\ ___\ /\ \ /\ == \ /\ ___\ /\ == \
|
||||
\ \ __\ \ \ \ \ \ __< \ \ __\ \ \ __<
|
||||
\ \_\ \ \_\ \ \_____\ \ \_____\ \ \_\ \_\
|
||||
\/_/ \/_/ \/_____/ \/_____/ \/_/ /_/
|
||||
|
||||
` + "\x1b[0mFiber \x1b[1;32mv%s\x1b[0m %s on \x1b[1;32m%s\x1b[0m, visit \x1b[1;32m%s\x1b[0m\n\n"
|
||||
)
|
||||
|
||||
var (
|
||||
prefork = flag.Bool("prefork", false, "use prefork")
|
||||
child = flag.Bool("child", false, "is child process")
|
||||
)
|
||||
|
||||
// Application structure
|
||||
type Application struct {
|
||||
// Server name header
|
||||
Server string
|
||||
// HTTP server struct
|
||||
httpServer *fasthttp.Server
|
||||
// Show fiber banner
|
||||
Banner bool
|
||||
// https://github.com/valyala/fasthttp/blob/master/server.go#L150
|
||||
Engine *engine
|
||||
// https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/
|
||||
Prefork bool
|
||||
// is child process
|
||||
child bool
|
||||
// Stores all routes
|
||||
routes []*Route
|
||||
// Recover holds a handler that is executed on a panic
|
||||
recover func(*Ctx)
|
||||
}
|
||||
|
||||
// Fasthttp settings
|
||||
// https://github.com/valyala/fasthttp/blob/master/server.go#L150
|
||||
type engine struct {
|
||||
Concurrency int
|
||||
DisableKeepAlive bool
|
||||
ReadBufferSize int
|
||||
WriteBufferSize int
|
||||
ReadTimeout time.Duration
|
||||
WriteTimeout time.Duration
|
||||
IdleTimeout time.Duration
|
||||
MaxConnsPerIP int
|
||||
MaxRequestsPerConn int
|
||||
TCPKeepalive bool
|
||||
TCPKeepalivePeriod time.Duration
|
||||
MaxRequestBodySize int
|
||||
ReduceMemoryUsage bool
|
||||
GetOnly bool
|
||||
DisableHeaderNamesNormalizing bool
|
||||
SleepWhenConcurrencyLimitsExceeded time.Duration
|
||||
NoDefaultContentType bool
|
||||
KeepHijackedConns bool
|
||||
}
|
||||
|
||||
// New https://fiber.wiki/application#new
|
||||
func New() *Application {
|
||||
flag.Parse()
|
||||
schemaDecoder.SetAliasTag("form")
|
||||
return &Application{
|
||||
Server: "",
|
||||
httpServer: nil,
|
||||
Banner: true,
|
||||
Prefork: *prefork,
|
||||
child: *child,
|
||||
Engine: &engine{
|
||||
Concurrency: 256 * 1024,
|
||||
DisableKeepAlive: false,
|
||||
ReadBufferSize: 4096,
|
||||
WriteBufferSize: 4096,
|
||||
WriteTimeout: 0,
|
||||
ReadTimeout: 0,
|
||||
IdleTimeout: 0,
|
||||
MaxConnsPerIP: 0,
|
||||
MaxRequestsPerConn: 0,
|
||||
TCPKeepalive: false,
|
||||
TCPKeepalivePeriod: 0,
|
||||
MaxRequestBodySize: 4 * 1024 * 1024,
|
||||
ReduceMemoryUsage: false,
|
||||
GetOnly: false,
|
||||
DisableHeaderNamesNormalizing: false,
|
||||
SleepWhenConcurrencyLimitsExceeded: 0,
|
||||
NoDefaultContentType: false,
|
||||
KeepHijackedConns: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Recover catches panics and avoids crashes
|
||||
func (app *Application) Recover(ctx func(*Ctx)) {
|
||||
app.recover = ctx
|
||||
}
|
||||
|
||||
// Recover binding for groups
|
||||
func (grp *Group) Recover(ctx func(*Ctx)) {
|
||||
grp.app.recover = ctx
|
||||
}
|
||||
|
||||
// Group :
|
||||
type Group struct {
|
||||
path string
|
||||
app *Application
|
||||
}
|
||||
|
||||
// Group :
|
||||
func (app *Application) Group(path string) *Group {
|
||||
return &Group{
|
||||
path: path,
|
||||
app: app,
|
||||
}
|
||||
}
|
||||
|
||||
// Connect establishes a tunnel to the server
|
||||
// identified by the target resource.
|
||||
func (app *Application) Connect(args ...interface{}) *Application {
|
||||
app.register("CONNECT", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Connect for group
|
||||
func (grp *Group) Connect(args ...interface{}) *Group {
|
||||
grp.register("CONNECT", args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Put replaces all current representations
|
||||
// of the target resource with the request payload.
|
||||
func (app *Application) Put(args ...interface{}) *Application {
|
||||
app.register("PUT", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Put for group
|
||||
func (grp *Group) Put(args ...interface{}) *Group {
|
||||
grp.register("PUT", args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Post is used to submit an entity to the specified resource,
|
||||
// often causing a change in state or side effects on the server.
|
||||
func (app *Application) Post(args ...interface{}) *Application {
|
||||
app.register("POST", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Post for group
|
||||
func (grp *Group) Post(args ...interface{}) *Group {
|
||||
grp.register("POST", args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Delete deletes the specified resource.
|
||||
func (app *Application) Delete(args ...interface{}) *Application {
|
||||
app.register("DELETE", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Delete for group
|
||||
func (grp *Group) Delete(args ...interface{}) *Group {
|
||||
grp.register("DELETE", args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Head asks for a response identical to that of a GET request,
|
||||
// but without the response body.
|
||||
func (app *Application) Head(args ...interface{}) *Application {
|
||||
app.register("HEAD", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Head for group
|
||||
func (grp *Group) Head(args ...interface{}) *Group {
|
||||
grp.register("HEAD", args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Patch is used to apply partial modifications to a resource.
|
||||
func (app *Application) Patch(args ...interface{}) *Application {
|
||||
app.register("PATCH", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Patch for group
|
||||
func (grp *Group) Patch(args ...interface{}) *Group {
|
||||
grp.register("PATCH", args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Options is used to describe the communication options
|
||||
// for the target resource.
|
||||
func (app *Application) Options(args ...interface{}) *Application {
|
||||
app.register("OPTIONS", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Options for group
|
||||
func (grp *Group) Options(args ...interface{}) *Group {
|
||||
grp.register("OPTIONS", args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Trace performs a message loop-back test
|
||||
// along the path to the target resource.
|
||||
func (app *Application) Trace(args ...interface{}) *Application {
|
||||
app.register("TRACE", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Trace for group
|
||||
func (grp *Group) Trace(args ...interface{}) *Group {
|
||||
grp.register("TRACE", args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Get requests a representation of the specified resource.
|
||||
// Requests using GET should only retrieve data.
|
||||
func (app *Application) Get(args ...interface{}) *Application {
|
||||
app.register("GET", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Get for group
|
||||
func (grp *Group) Get(args ...interface{}) *Group {
|
||||
grp.register("GET", args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// All matches any HTTP method
|
||||
func (app *Application) All(args ...interface{}) *Application {
|
||||
app.register("ALL", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// All for group
|
||||
func (grp *Group) All(args ...interface{}) *Group {
|
||||
grp.register("ALL", args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Use only matches the starting path
|
||||
func (app *Application) Use(args ...interface{}) *Application {
|
||||
app.register("USE", args...)
|
||||
return app
|
||||
}
|
||||
|
||||
// Use for group
|
||||
func (grp *Group) Use(args ...interface{}) *Group {
|
||||
grp.register("USE", args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Static for groups
|
||||
func (grp *Group) Static(args ...string) {
|
||||
prefix := grp.path
|
||||
root := "./"
|
||||
|
||||
if len(args) == 1 {
|
||||
root = args[0]
|
||||
} else if len(args) == 2 {
|
||||
root = args[1]
|
||||
prefix = prefix + args[0]
|
||||
prefix = strings.Replace(prefix, "//", "/", -1)
|
||||
prefix = filepath.Clean(prefix)
|
||||
prefix = filepath.ToSlash(prefix)
|
||||
}
|
||||
grp.app.Static(prefix, root)
|
||||
}
|
||||
|
||||
// Static https://fiber.wiki/application#static
|
||||
func (app *Application) Static(args ...string) {
|
||||
prefix := "/"
|
||||
root := "./"
|
||||
wildcard := false
|
||||
midware := false
|
||||
// enable / disable gzipping somewhere?
|
||||
// todo v2.0.0
|
||||
gzip := true
|
||||
|
||||
if len(args) == 1 {
|
||||
root = args[0]
|
||||
} else if len(args) == 2 {
|
||||
prefix = args[0]
|
||||
root = args[1]
|
||||
if prefix[0] != '/' {
|
||||
prefix = "/" + prefix
|
||||
}
|
||||
}
|
||||
|
||||
// Check if wildcard for single files
|
||||
// app.Static("*", "./public/index.html")
|
||||
// app.Static("/*", "./public/index.html")
|
||||
if prefix == "*" || prefix == "/*" {
|
||||
wildcard = true
|
||||
} else if strings.Contains(prefix, "*") {
|
||||
prefix = strings.Replace(prefix, "*", "", -1)
|
||||
midware = true
|
||||
}
|
||||
|
||||
// Lets get all files from root
|
||||
files, _, err := getFiles(root)
|
||||
if err != nil {
|
||||
log.Fatal("Static: ", err)
|
||||
}
|
||||
|
||||
// ./static/compiled => static/compiled
|
||||
mount := filepath.Clean(root)
|
||||
|
||||
// Loop over all files
|
||||
for _, file := range files {
|
||||
// Ignore the .gzipped files by fasthttp
|
||||
if strings.Contains(file, ".fasthttp.gz") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Time to create a fake path for the route match
|
||||
// static/index.html => /index.html
|
||||
path := filepath.Join(prefix, strings.Replace(file, mount, "", 1))
|
||||
// for windows: static\index.html => /index.html
|
||||
path = filepath.ToSlash(path)
|
||||
// Store file path to use in ctx handler
|
||||
filePath := file
|
||||
|
||||
// If the file is an index.html, bind the prefix to index.html directly
|
||||
if filepath.Base(filePath) == "index.html" || filepath.Base(filePath) == "index.htm" {
|
||||
app.routes = append(app.routes, &Route{"GET", prefix, midware, wildcard, nil, nil, func(c *Ctx) {
|
||||
c.SendFile(filePath, gzip)
|
||||
}})
|
||||
}
|
||||
|
||||
// Add the route + SendFile(filepath) to routes
|
||||
app.routes = append(app.routes, &Route{"GET", path, midware, wildcard, nil, nil, func(c *Ctx) {
|
||||
c.SendFile(filePath, gzip)
|
||||
}})
|
||||
}
|
||||
}
|
||||
|
||||
// Listen : https://fiber.wiki/application#listen
|
||||
func (app *Application) Listen(address interface{}, tls ...string) {
|
||||
host := ""
|
||||
switch val := address.(type) {
|
||||
case int:
|
||||
host = ":" + strconv.Itoa(val) // 8080 => ":8080"
|
||||
case string:
|
||||
if !strings.Contains(val, ":") {
|
||||
val = ":" + val // "8080" => ":8080"
|
||||
}
|
||||
host = val
|
||||
default:
|
||||
log.Fatal("Listen: Host must be an INT port or STRING address")
|
||||
}
|
||||
// Create fasthttp server
|
||||
app.httpServer = app.setupServer()
|
||||
|
||||
// Prefork enabled
|
||||
if app.Prefork && runtime.NumCPU() > 1 {
|
||||
if app.Banner && !app.child {
|
||||
fmt.Printf(banner, Version, "preforking", host, "fiber.wiki")
|
||||
}
|
||||
app.prefork(host, tls...)
|
||||
}
|
||||
|
||||
// Prefork disabled
|
||||
if app.Banner {
|
||||
fmt.Printf(banner, Version, "listening", host, "fiber.wiki")
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp4", host)
|
||||
if err != nil {
|
||||
log.Fatal("Listen: ", err)
|
||||
}
|
||||
|
||||
// enable TLS/HTTPS
|
||||
if len(tls) > 1 {
|
||||
if err := app.httpServer.ServeTLS(ln, tls[0], tls[1]); err != nil {
|
||||
log.Fatal("Listen: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := app.httpServer.Serve(ln); err != nil {
|
||||
log.Fatal("Listen: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown server gracefully
|
||||
func (app *Application) Shutdown() error {
|
||||
if app.httpServer == nil {
|
||||
return fmt.Errorf("server is not running")
|
||||
}
|
||||
return app.httpServer.Shutdown()
|
||||
}
|
||||
|
||||
// Test takes a http.Request and execute a fake connection to the application
|
||||
// It returns a http.Response when the connection was successful
|
||||
func (app *Application) Test(req *http.Request) (*http.Response, error) {
|
||||
// Get raw http request
|
||||
reqRaw, err := httputil.DumpRequest(req, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Setup a fiber server struct
|
||||
app.httpServer = app.setupServer()
|
||||
// Create fake connection
|
||||
conn := &conn{}
|
||||
// Pass HTTP request to conn
|
||||
_, err = conn.r.Write(reqRaw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Serve conn to server
|
||||
channel := make(chan error)
|
||||
go func() {
|
||||
channel <- app.httpServer.ServeConn(conn)
|
||||
}()
|
||||
// Wait for callback
|
||||
select {
|
||||
case err := <-channel:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Throw timeout error after 200ms
|
||||
case <-time.After(1000 * time.Millisecond):
|
||||
return nil, fmt.Errorf("timeout")
|
||||
}
|
||||
// Get raw HTTP response
|
||||
respRaw, err := ioutil.ReadAll(&conn.w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Create buffer
|
||||
reader := strings.NewReader(getString(respRaw))
|
||||
buffer := bufio.NewReader(reader)
|
||||
// Convert raw HTTP response to http.Response
|
||||
resp, err := http.ReadResponse(buffer, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Return *http.Response
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/
|
||||
func (app *Application) prefork(host string, tls ...string) {
|
||||
// Master proc
|
||||
if !app.child {
|
||||
// Create babies
|
||||
childs := make([]*exec.Cmd, runtime.NumCPU())
|
||||
|
||||
// #nosec G204
|
||||
for i := range childs {
|
||||
childs[i] = exec.Command(os.Args[0], "-prefork", "-child")
|
||||
childs[i].Stdout = os.Stdout
|
||||
childs[i].Stderr = os.Stderr
|
||||
if err := childs[i].Start(); err != nil {
|
||||
log.Fatal("Listen-prefork: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, child := range childs {
|
||||
if err := child.Wait(); err != nil {
|
||||
log.Fatal("Listen-prefork: ", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Child proc
|
||||
runtime.GOMAXPROCS(1)
|
||||
|
||||
ln, err := reuseport.Listen("tcp4", host)
|
||||
if err != nil {
|
||||
log.Fatal("Listen-prefork: ", err)
|
||||
}
|
||||
|
||||
// enable TLS/HTTPS
|
||||
if len(tls) > 1 {
|
||||
if err := app.httpServer.ServeTLS(ln, tls[0], tls[1]); err != nil {
|
||||
log.Fatal("Listen-prefork: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := app.httpServer.Serve(ln); err != nil {
|
||||
log.Fatal("Listen-prefork: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *Application) setupServer() *fasthttp.Server {
|
||||
return &fasthttp.Server{
|
||||
Handler: app.handler,
|
||||
Name: app.Server,
|
||||
Concurrency: app.Engine.Concurrency,
|
||||
DisableKeepalive: app.Engine.DisableKeepAlive,
|
||||
ReadBufferSize: app.Engine.ReadBufferSize,
|
||||
WriteBufferSize: app.Engine.WriteBufferSize,
|
||||
ReadTimeout: app.Engine.ReadTimeout,
|
||||
WriteTimeout: app.Engine.WriteTimeout,
|
||||
IdleTimeout: app.Engine.IdleTimeout,
|
||||
MaxConnsPerIP: app.Engine.MaxConnsPerIP,
|
||||
MaxRequestsPerConn: app.Engine.MaxRequestsPerConn,
|
||||
TCPKeepalive: app.Engine.TCPKeepalive,
|
||||
TCPKeepalivePeriod: app.Engine.TCPKeepalivePeriod,
|
||||
MaxRequestBodySize: app.Engine.MaxRequestBodySize,
|
||||
ReduceMemoryUsage: app.Engine.ReduceMemoryUsage,
|
||||
GetOnly: app.Engine.GetOnly,
|
||||
DisableHeaderNamesNormalizing: app.Engine.DisableHeaderNamesNormalizing,
|
||||
SleepWhenConcurrencyLimitsExceeded: app.Engine.SleepWhenConcurrencyLimitsExceeded,
|
||||
NoDefaultServerHeader: app.Server == "",
|
||||
NoDefaultContentType: app.Engine.NoDefaultContentType,
|
||||
KeepHijackedConns: app.Engine.KeepHijackedConns,
|
||||
}
|
||||
}
|
869
context.go
Normal file
869
context.go
Normal file
@ -0,0 +1,869 @@
|
||||
package fiber
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
// templates
|
||||
pug "github.com/Joker/jade"
|
||||
handlebars "github.com/aymerick/raymond"
|
||||
mustache "github.com/cbroglie/mustache"
|
||||
amber "github.com/eknkc/amber"
|
||||
// core
|
||||
websocket "github.com/fasthttp/websocket"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
fasthttp "github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Ctx represents the Context which hold the HTTP request and response.
|
||||
// It has methods for the request query string, parameters, body, HTTP headers and so on.
|
||||
// For more information please visit our documentation: https://fiber.wiki/context
|
||||
type Ctx struct {
|
||||
app *App
|
||||
route *Route
|
||||
next bool
|
||||
error error
|
||||
params *[]string
|
||||
values []string
|
||||
Fasthttp *fasthttp.RequestCtx
|
||||
Socket *websocket.Conn
|
||||
}
|
||||
|
||||
// Ctx pool
|
||||
var poolCtx = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(Ctx)
|
||||
},
|
||||
}
|
||||
|
||||
// Acquire Ctx from pool
|
||||
func acquireCtx(fctx *fasthttp.RequestCtx) *Ctx {
|
||||
ctx := poolCtx.Get().(*Ctx)
|
||||
ctx.Fasthttp = fctx
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Return Ctx to pool
|
||||
func releaseCtx(ctx *Ctx) {
|
||||
ctx.route = nil
|
||||
ctx.next = false
|
||||
ctx.error = nil
|
||||
ctx.params = nil
|
||||
ctx.values = nil
|
||||
ctx.Fasthttp = nil
|
||||
ctx.Socket = nil
|
||||
poolCtx.Put(ctx)
|
||||
}
|
||||
|
||||
// Conn https://godoc.org/github.com/gorilla/websocket#pkg-index
|
||||
type Conn struct {
|
||||
params *[]string
|
||||
values []string
|
||||
*websocket.Conn
|
||||
}
|
||||
|
||||
// Params : https://fiber.wiki/application#websocket
|
||||
func (conn *Conn) Params(key string) string {
|
||||
if conn.params == nil {
|
||||
return ""
|
||||
}
|
||||
for i := 0; i < len(*conn.params); i++ {
|
||||
if (*conn.params)[i] == key {
|
||||
return conn.values[i]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Conn pool
|
||||
var poolConn = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(Conn)
|
||||
},
|
||||
}
|
||||
|
||||
// Acquire Conn from pool
|
||||
func acquireConn(fconn *websocket.Conn) *Conn {
|
||||
conn := poolConn.Get().(*Conn)
|
||||
conn.Conn = fconn
|
||||
return conn
|
||||
}
|
||||
|
||||
// Return Conn to pool
|
||||
func releaseConn(conn *Conn) {
|
||||
conn.Close()
|
||||
conn.params = nil
|
||||
conn.values = nil
|
||||
conn.Conn = nil
|
||||
poolConn.Put(conn)
|
||||
}
|
||||
|
||||
// Cookie : struct
|
||||
type Cookie struct {
|
||||
Expire int // time.Unix(1578981376, 0)
|
||||
MaxAge int
|
||||
Domain string
|
||||
Path string
|
||||
|
||||
HTTPOnly bool
|
||||
Secure bool
|
||||
SameSite string
|
||||
}
|
||||
|
||||
// Accepts : https://fiber.wiki/context#accepts
|
||||
func (ctx *Ctx) Accepts(offers ...string) string {
|
||||
if len(offers) == 0 {
|
||||
return ""
|
||||
}
|
||||
h := ctx.Get(fasthttp.HeaderAccept)
|
||||
if h == "" {
|
||||
return offers[0]
|
||||
}
|
||||
|
||||
specs := strings.Split(h, ",")
|
||||
for _, offer := range offers {
|
||||
mimetype := getType(offer)
|
||||
// if mimetype != "" {
|
||||
// mimetype = strings.Split(mimetype, ";")[0]
|
||||
// } else {
|
||||
// mimetype = offer
|
||||
// }
|
||||
for _, spec := range specs {
|
||||
spec = strings.TrimSpace(spec)
|
||||
if strings.HasPrefix(spec, "*/*") {
|
||||
return offer
|
||||
}
|
||||
|
||||
if strings.HasPrefix(spec, mimetype) {
|
||||
return offer
|
||||
}
|
||||
|
||||
if strings.Contains(spec, "/*") {
|
||||
if strings.HasPrefix(spec, strings.Split(mimetype, "/")[0]) {
|
||||
return offer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// AcceptsCharsets : https://fiber.wiki/context#acceptscharsets
|
||||
func (ctx *Ctx) AcceptsCharsets(offers ...string) string {
|
||||
if len(offers) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
h := ctx.Get(fasthttp.HeaderAcceptCharset)
|
||||
if h == "" {
|
||||
return offers[0]
|
||||
}
|
||||
|
||||
specs := strings.Split(h, ",")
|
||||
for _, offer := range offers {
|
||||
for _, spec := range specs {
|
||||
spec = strings.TrimSpace(spec)
|
||||
if strings.HasPrefix(spec, "*") {
|
||||
return offer
|
||||
}
|
||||
if strings.HasPrefix(spec, offer) {
|
||||
return offer
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// AcceptsEncodings : https://fiber.wiki/context#acceptsencodings
|
||||
func (ctx *Ctx) AcceptsEncodings(offers ...string) string {
|
||||
if len(offers) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
h := ctx.Get(fasthttp.HeaderAcceptEncoding)
|
||||
if h == "" {
|
||||
return offers[0]
|
||||
}
|
||||
|
||||
specs := strings.Split(h, ",")
|
||||
for _, offer := range offers {
|
||||
for _, spec := range specs {
|
||||
spec = strings.TrimSpace(spec)
|
||||
if strings.HasPrefix(spec, "*") {
|
||||
return offer
|
||||
}
|
||||
if strings.HasPrefix(spec, offer) {
|
||||
return offer
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// AcceptsLanguages : https://fiber.wiki/context#acceptslanguages
|
||||
func (ctx *Ctx) AcceptsLanguages(offers ...string) string {
|
||||
if len(offers) == 0 {
|
||||
return ""
|
||||
}
|
||||
h := ctx.Get(fasthttp.HeaderAcceptLanguage)
|
||||
if h == "" {
|
||||
return offers[0]
|
||||
}
|
||||
|
||||
specs := strings.Split(h, ",")
|
||||
for _, offer := range offers {
|
||||
for _, spec := range specs {
|
||||
spec = strings.TrimSpace(spec)
|
||||
if strings.HasPrefix(spec, "*") {
|
||||
return offer
|
||||
}
|
||||
if strings.HasPrefix(spec, offer) {
|
||||
return offer
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Append : https://fiber.wiki/context#append
|
||||
func (ctx *Ctx) Append(field string, values ...string) {
|
||||
if len(values) == 0 {
|
||||
return
|
||||
}
|
||||
h := getString(ctx.Fasthttp.Response.Header.Peek(field))
|
||||
for i := range values {
|
||||
if h == "" {
|
||||
h += values[i]
|
||||
} else {
|
||||
h += ", " + values[i]
|
||||
}
|
||||
}
|
||||
ctx.Set(field, h)
|
||||
}
|
||||
|
||||
// Attachment : https://fiber.wiki/context#attachment
|
||||
func (ctx *Ctx) Attachment(name ...string) {
|
||||
if len(name) > 0 {
|
||||
filename := filepath.Base(name[0])
|
||||
ctx.Type(filepath.Ext(filename))
|
||||
ctx.Set(fasthttp.HeaderContentDisposition, `attachment; filename="`+filename+`"`)
|
||||
return
|
||||
}
|
||||
ctx.Set(fasthttp.HeaderContentDisposition, "attachment")
|
||||
}
|
||||
|
||||
// BaseURL : https://fiber.wiki/context#baseurl
|
||||
func (ctx *Ctx) BaseURL() string {
|
||||
return ctx.Protocol() + "://" + ctx.Hostname()
|
||||
}
|
||||
|
||||
// Body : https://fiber.wiki/context#body
|
||||
func (ctx *Ctx) Body(args ...interface{}) string {
|
||||
if len(args) == 0 {
|
||||
return getString(ctx.Fasthttp.Request.Body())
|
||||
}
|
||||
|
||||
if len(args) == 1 {
|
||||
switch arg := args[0].(type) {
|
||||
case string:
|
||||
return getString(ctx.Fasthttp.Request.PostArgs().Peek(arg))
|
||||
case []byte:
|
||||
return getString(ctx.Fasthttp.Request.PostArgs().PeekBytes(arg))
|
||||
case func(string, string):
|
||||
ctx.Fasthttp.Request.PostArgs().VisitAll(func(k []byte, v []byte) {
|
||||
arg(getString(k), getString(v))
|
||||
})
|
||||
default:
|
||||
return getString(ctx.Fasthttp.Request.Body())
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// BodyParser : https://fiber.wiki/context#bodyparser
|
||||
func (ctx *Ctx) BodyParser(v interface{}) error {
|
||||
ctype := getString(ctx.Fasthttp.Request.Header.ContentType())
|
||||
// application/json
|
||||
if strings.HasPrefix(ctype, MIMEApplicationJSON) {
|
||||
return jsoniter.Unmarshal(ctx.Fasthttp.Request.Body(), v)
|
||||
}
|
||||
// application/xml text/xml
|
||||
if strings.HasPrefix(ctype, MIMEApplicationXML) || strings.HasPrefix(ctype, MIMETextXML) {
|
||||
return xml.Unmarshal(ctx.Fasthttp.Request.Body(), v)
|
||||
}
|
||||
// application/x-www-form-urlencoded
|
||||
if strings.HasPrefix(ctype, MIMEApplicationForm) {
|
||||
data, err := url.ParseQuery(getString(ctx.Fasthttp.PostBody()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return schemaDecoder.Decode(v, data)
|
||||
}
|
||||
// multipart/form-data
|
||||
if strings.HasPrefix(ctype, MIMEMultipartForm) {
|
||||
data, err := ctx.Fasthttp.MultipartForm()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return schemaDecoder.Decode(v, data.Value)
|
||||
|
||||
}
|
||||
return fmt.Errorf("cannot parse content-type: %v", ctype)
|
||||
}
|
||||
|
||||
// ClearCookie : https://fiber.wiki/context#clearcookie
|
||||
func (ctx *Ctx) ClearCookie(name ...string) {
|
||||
if len(name) > 0 {
|
||||
for i := range name {
|
||||
//ctx.Fasthttp.Request.Header.DelAllCookies()
|
||||
ctx.Fasthttp.Response.Header.DelClientCookie(name[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
//ctx.Fasthttp.Response.Header.DelAllCookies()
|
||||
ctx.Fasthttp.Request.Header.VisitAllCookie(func(k, v []byte) {
|
||||
ctx.Fasthttp.Response.Header.DelClientCookie(getString(k))
|
||||
})
|
||||
}
|
||||
|
||||
// Cookie : https://fiber.wiki/context#cookie
|
||||
func (ctx *Ctx) Cookie(key, value string, options ...interface{}) {
|
||||
cook := &fasthttp.Cookie{}
|
||||
|
||||
cook.SetKey(key)
|
||||
cook.SetValue(value)
|
||||
|
||||
if len(options) > 0 {
|
||||
switch opt := options[0].(type) {
|
||||
case *Cookie:
|
||||
if opt.Expire > 0 {
|
||||
cook.SetExpire(time.Unix(int64(opt.Expire), 0))
|
||||
}
|
||||
if opt.MaxAge > 0 {
|
||||
cook.SetMaxAge(opt.MaxAge)
|
||||
}
|
||||
if opt.Domain != "" {
|
||||
cook.SetDomain(opt.Domain)
|
||||
}
|
||||
if opt.Path != "" {
|
||||
cook.SetPath(opt.Path)
|
||||
}
|
||||
if opt.HTTPOnly {
|
||||
cook.SetHTTPOnly(opt.HTTPOnly)
|
||||
}
|
||||
if opt.Secure {
|
||||
cook.SetSecure(opt.Secure)
|
||||
}
|
||||
if opt.SameSite != "" {
|
||||
sameSite := fasthttp.CookieSameSiteDefaultMode
|
||||
if strings.EqualFold(opt.SameSite, "lax") {
|
||||
sameSite = fasthttp.CookieSameSiteLaxMode
|
||||
} else if strings.EqualFold(opt.SameSite, "strict") {
|
||||
sameSite = fasthttp.CookieSameSiteStrictMode
|
||||
} else if strings.EqualFold(opt.SameSite, "none") {
|
||||
sameSite = fasthttp.CookieSameSiteNoneMode
|
||||
}
|
||||
// } else {
|
||||
// sameSite = fasthttp.CookieSameSiteDisabled
|
||||
// }
|
||||
cook.SetSameSite(sameSite)
|
||||
}
|
||||
default:
|
||||
log.Println("Cookie: Invalid &Cookie{} struct")
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Fasthttp.Response.Header.SetCookie(cook)
|
||||
}
|
||||
|
||||
// Cookies : https://fiber.wiki/context#cookies
|
||||
func (ctx *Ctx) Cookies(args ...interface{}) string {
|
||||
if len(args) == 0 {
|
||||
return ctx.Get(fasthttp.HeaderCookie)
|
||||
}
|
||||
|
||||
switch arg := args[0].(type) {
|
||||
case string:
|
||||
return getString(ctx.Fasthttp.Request.Header.Cookie(arg))
|
||||
case []byte:
|
||||
return getString(ctx.Fasthttp.Request.Header.CookieBytes(arg))
|
||||
case func(string, string):
|
||||
ctx.Fasthttp.Request.Header.VisitAllCookie(func(k, v []byte) {
|
||||
arg(getString(k), getString(v))
|
||||
})
|
||||
default:
|
||||
return ctx.Get(fasthttp.HeaderCookie)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// Download : https://fiber.wiki/context#download
|
||||
func (ctx *Ctx) Download(file string, name ...string) {
|
||||
filename := filepath.Base(file)
|
||||
|
||||
if len(name) > 0 {
|
||||
filename = name[0]
|
||||
}
|
||||
|
||||
ctx.Set(fasthttp.HeaderContentDisposition, "attachment; filename="+filename)
|
||||
ctx.SendFile(file)
|
||||
}
|
||||
|
||||
// Error returns err that is passed via Next(err)
|
||||
func (ctx *Ctx) Error() error {
|
||||
return ctx.error
|
||||
}
|
||||
|
||||
// Format : https://fiber.wiki/context#format
|
||||
func (ctx *Ctx) Format(args ...interface{}) {
|
||||
var body string
|
||||
|
||||
accept := ctx.Accepts("html", "json")
|
||||
|
||||
for i := range args {
|
||||
switch arg := args[i].(type) {
|
||||
case string:
|
||||
body = arg
|
||||
case []byte:
|
||||
body = getString(arg)
|
||||
default:
|
||||
body = fmt.Sprintf("%v", arg)
|
||||
}
|
||||
switch accept {
|
||||
case "html":
|
||||
ctx.SendString("<p>" + body + "</p>")
|
||||
case "json":
|
||||
if err := ctx.JSON(body); err != nil {
|
||||
log.Println("Format: error serializing json ", err)
|
||||
}
|
||||
default:
|
||||
ctx.SendString(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FormFile : https://fiber.wiki/context#formfile
|
||||
func (ctx *Ctx) FormFile(key string) (*multipart.FileHeader, error) {
|
||||
return ctx.Fasthttp.FormFile(key)
|
||||
}
|
||||
|
||||
// FormValue : https://fiber.wiki/context#formvalue
|
||||
func (ctx *Ctx) FormValue(key string) string {
|
||||
return getString(ctx.Fasthttp.FormValue(key))
|
||||
}
|
||||
|
||||
// Fresh : https://fiber.wiki/context#fresh
|
||||
func (ctx *Ctx) Fresh() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Get : https://fiber.wiki/context#get
|
||||
func (ctx *Ctx) Get(key string) string {
|
||||
if key == "referrer" {
|
||||
key = "referer"
|
||||
}
|
||||
return getString(ctx.Fasthttp.Request.Header.Peek(key))
|
||||
}
|
||||
|
||||
// Hostname : https://fiber.wiki/context#hostname
|
||||
func (ctx *Ctx) Hostname() string {
|
||||
return getString(ctx.Fasthttp.URI().Host())
|
||||
}
|
||||
|
||||
// IP : https://fiber.wiki/context#Ip
|
||||
func (ctx *Ctx) IP() string {
|
||||
return ctx.Fasthttp.RemoteIP().String()
|
||||
}
|
||||
|
||||
// IPs : https://fiber.wiki/context#ips
|
||||
func (ctx *Ctx) IPs() []string {
|
||||
ips := strings.Split(ctx.Get(fasthttp.HeaderXForwardedFor), ",")
|
||||
for i := range ips {
|
||||
ips[i] = strings.TrimSpace(ips[i])
|
||||
}
|
||||
return ips
|
||||
}
|
||||
|
||||
// Is : https://fiber.wiki/context#is
|
||||
func (ctx *Ctx) Is(ext string) bool {
|
||||
if ext[0] != '.' {
|
||||
ext = "." + ext
|
||||
}
|
||||
|
||||
exts, _ := mime.ExtensionsByType(ctx.Get(fasthttp.HeaderContentType))
|
||||
if len(exts) > 0 {
|
||||
for _, item := range exts {
|
||||
if item == ext {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// JSON : https://fiber.wiki/context#json
|
||||
func (ctx *Ctx) JSON(v interface{}) error {
|
||||
ctx.Fasthttp.Response.Header.SetContentType(MIMEApplicationJSON)
|
||||
raw, err := jsoniter.Marshal(&v)
|
||||
if err != nil {
|
||||
ctx.Fasthttp.Response.SetBodyString("")
|
||||
return err
|
||||
}
|
||||
ctx.Fasthttp.Response.SetBodyString(getString(raw))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// JSONP : https://fiber.wiki/context#jsonp
|
||||
func (ctx *Ctx) JSONP(v interface{}, cb ...string) error {
|
||||
raw, err := jsoniter.Marshal(&v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str := "callback("
|
||||
if len(cb) > 0 {
|
||||
str = cb[0] + "("
|
||||
}
|
||||
str += getString(raw) + ");"
|
||||
|
||||
ctx.Set(fasthttp.HeaderXContentTypeOptions, "nosniff")
|
||||
ctx.Fasthttp.Response.Header.SetContentType(MIMEApplicationJavaScript)
|
||||
ctx.Fasthttp.Response.SetBodyString(str)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Links : https://fiber.wiki/context#links
|
||||
func (ctx *Ctx) Links(link ...string) {
|
||||
h := ""
|
||||
for i, l := range link {
|
||||
if i%2 == 0 {
|
||||
h += "<" + l + ">"
|
||||
} else {
|
||||
h += `; rel="` + l + `",`
|
||||
}
|
||||
}
|
||||
|
||||
if len(link) > 0 {
|
||||
h = strings.TrimSuffix(h, ",")
|
||||
ctx.Set(fasthttp.HeaderLink, h)
|
||||
}
|
||||
}
|
||||
|
||||
// Locals : https://fiber.wiki/context#locals
|
||||
func (ctx *Ctx) Locals(key string, val ...interface{}) interface{} {
|
||||
if len(val) == 0 {
|
||||
return ctx.Fasthttp.UserValue(key)
|
||||
}
|
||||
ctx.Fasthttp.SetUserValue(key, val[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Location : https://fiber.wiki/context#location
|
||||
func (ctx *Ctx) Location(path string) {
|
||||
ctx.Set(fasthttp.HeaderLocation, path)
|
||||
}
|
||||
|
||||
// Method : https://fiber.wiki/context#method
|
||||
func (ctx *Ctx) Method() string {
|
||||
return getString(ctx.Fasthttp.Request.Header.Method())
|
||||
}
|
||||
|
||||
// MultipartForm : https://fiber.wiki/context#multipartform
|
||||
func (ctx *Ctx) MultipartForm() (*multipart.Form, error) {
|
||||
return ctx.Fasthttp.MultipartForm()
|
||||
}
|
||||
|
||||
// Next : https://fiber.wiki/context#next
|
||||
func (ctx *Ctx) Next(err ...error) {
|
||||
ctx.route = nil
|
||||
ctx.next = true
|
||||
ctx.params = nil
|
||||
ctx.values = nil
|
||||
if len(err) > 0 {
|
||||
ctx.error = err[0]
|
||||
}
|
||||
}
|
||||
|
||||
// OriginalURL : https://fiber.wiki/context#originalurl
|
||||
func (ctx *Ctx) OriginalURL() string {
|
||||
return getString(ctx.Fasthttp.Request.Header.RequestURI())
|
||||
}
|
||||
|
||||
// Params : https://fiber.wiki/context#params
|
||||
func (ctx *Ctx) Params(key string) string {
|
||||
if ctx.params == nil {
|
||||
return ""
|
||||
}
|
||||
for i := 0; i < len(*ctx.params); i++ {
|
||||
if (*ctx.params)[i] == key {
|
||||
return ctx.values[i]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Path : https://fiber.wiki/context#path
|
||||
func (ctx *Ctx) Path() string {
|
||||
return getString(ctx.Fasthttp.URI().Path())
|
||||
}
|
||||
|
||||
// Protocol : https://fiber.wiki/context#protocol
|
||||
func (ctx *Ctx) Protocol() string {
|
||||
if ctx.Fasthttp.IsTLS() {
|
||||
return "https"
|
||||
}
|
||||
return "http"
|
||||
}
|
||||
|
||||
// Query : https://fiber.wiki/context#query
|
||||
func (ctx *Ctx) Query(key string) string {
|
||||
return getString(ctx.Fasthttp.QueryArgs().Peek(key))
|
||||
}
|
||||
|
||||
// Range : https://fiber.wiki/context#range
|
||||
func (ctx *Ctx) Range() {
|
||||
// https://expressjs.com/en/api.html#req.range
|
||||
// https://github.com/jshttp/range-parser/blob/master/index.js
|
||||
// r := ctx.Fasthttp.Request.Header.Peek(fasthttp.HeaderRange)
|
||||
// *magic*
|
||||
}
|
||||
|
||||
// Redirect : https://fiber.wiki/context#redirect
|
||||
func (ctx *Ctx) Redirect(path string, status ...int) {
|
||||
code := 302
|
||||
if len(status) > 0 {
|
||||
code = status[0]
|
||||
}
|
||||
|
||||
ctx.Set(fasthttp.HeaderLocation, path)
|
||||
ctx.Fasthttp.Response.SetStatusCode(code)
|
||||
}
|
||||
|
||||
// Render : https://fiber.wiki/context#render
|
||||
func (ctx *Ctx) Render(file string, data interface{}, e ...string) error {
|
||||
var err error
|
||||
var raw []byte
|
||||
var html string
|
||||
var engine string
|
||||
|
||||
if len(e) > 0 {
|
||||
engine = e[0]
|
||||
} else if ctx.app.Settings.ViewEngine != "" {
|
||||
engine = ctx.app.Settings.ViewEngine
|
||||
} else {
|
||||
engine = filepath.Ext(file)[1:]
|
||||
}
|
||||
if ctx.app.Settings.ViewFolder != "" {
|
||||
file = filepath.Join(ctx.app.Settings.ViewFolder, file)
|
||||
}
|
||||
if ctx.app.Settings.ViewExtension != "" {
|
||||
file = file + ctx.app.Settings.ViewExtension
|
||||
}
|
||||
if raw, err = ioutil.ReadFile(filepath.Clean(file)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch engine {
|
||||
case "amber": // https://github.com/eknkc/amber
|
||||
var buf bytes.Buffer
|
||||
var tmpl *template.Template
|
||||
|
||||
if tmpl, err = amber.Compile(getString(raw), amber.DefaultOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tmpl.Execute(&buf, data); err != nil {
|
||||
return err
|
||||
}
|
||||
html = buf.String()
|
||||
|
||||
case "handlebars": // https://github.com/aymerick/raymond
|
||||
if html, err = handlebars.Render(getString(raw), data); err != nil {
|
||||
return err
|
||||
}
|
||||
case "mustache": // https://github.com/cbroglie/mustache
|
||||
if html, err = mustache.Render(getString(raw), data); err != nil {
|
||||
return err
|
||||
}
|
||||
case "pug": // https://github.com/Joker/jade
|
||||
var parsed string
|
||||
var buf bytes.Buffer
|
||||
var tmpl *template.Template
|
||||
if parsed, err = pug.Parse("", raw); err != nil {
|
||||
return err
|
||||
}
|
||||
if tmpl, err = template.New("").Parse(parsed); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tmpl.Execute(&buf, data); err != nil {
|
||||
return err
|
||||
}
|
||||
html = buf.String()
|
||||
|
||||
default: // https://golang.org/pkg/text/template/
|
||||
var buf bytes.Buffer
|
||||
var tmpl *template.Template
|
||||
|
||||
if tmpl, err = template.New("").Parse(getString(raw)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = tmpl.Execute(&buf, data); err != nil {
|
||||
return err
|
||||
}
|
||||
html = buf.String()
|
||||
}
|
||||
ctx.Set("Content-Type", "text/html")
|
||||
ctx.SendString(html)
|
||||
return err
|
||||
}
|
||||
|
||||
// Route : https://fiber.wiki/context#route
|
||||
func (ctx *Ctx) Route() *Route {
|
||||
return ctx.route
|
||||
}
|
||||
|
||||
// SaveFile : https://fiber.wiki/context#secure
|
||||
func (ctx *Ctx) SaveFile(fh *multipart.FileHeader, path string) error {
|
||||
return fasthttp.SaveMultipartFile(fh, path)
|
||||
}
|
||||
|
||||
// Secure : https://fiber.wiki/context#secure
|
||||
func (ctx *Ctx) Secure() bool {
|
||||
return ctx.Fasthttp.IsTLS()
|
||||
}
|
||||
|
||||
// Send : https://fiber.wiki/context#send
|
||||
func (ctx *Ctx) Send(args ...interface{}) {
|
||||
if len(args) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
switch body := args[0].(type) {
|
||||
case string:
|
||||
ctx.Fasthttp.Response.SetBodyString(body)
|
||||
case []byte:
|
||||
ctx.Fasthttp.Response.SetBodyString(getString(body))
|
||||
default:
|
||||
ctx.Fasthttp.Response.SetBodyString(fmt.Sprintf("%v", body))
|
||||
}
|
||||
}
|
||||
|
||||
// SendBytes : https://fiber.wiki/context#sendbytes
|
||||
func (ctx *Ctx) SendBytes(body []byte) {
|
||||
ctx.Fasthttp.Response.SetBodyString(getString(body))
|
||||
}
|
||||
|
||||
// SendFile : https://fiber.wiki/context#sendfile
|
||||
func (ctx *Ctx) SendFile(file string, gzip ...bool) {
|
||||
// Disable gzipping
|
||||
if len(gzip) > 0 && !gzip[0] {
|
||||
fasthttp.ServeFileUncompressed(ctx.Fasthttp, file)
|
||||
return
|
||||
}
|
||||
fasthttp.ServeFile(ctx.Fasthttp, file)
|
||||
// https://github.com/valyala/fasthttp/blob/master/fs.go#L81
|
||||
//ctx.Type(filepath.Ext(path))
|
||||
//ctx.Fasthttp.SendFile(path)
|
||||
}
|
||||
|
||||
// SendStatus : https://fiber.wiki/context#sendstatus
|
||||
func (ctx *Ctx) SendStatus(status int) {
|
||||
ctx.Fasthttp.Response.SetStatusCode(status)
|
||||
// Only set status body when there is no response body
|
||||
if len(ctx.Fasthttp.Response.Body()) == 0 {
|
||||
ctx.Fasthttp.Response.SetBodyString(getStatus(status))
|
||||
}
|
||||
}
|
||||
|
||||
// SendString : https://fiber.wiki/context#sendstring
|
||||
func (ctx *Ctx) SendString(body string) {
|
||||
ctx.Fasthttp.Response.SetBodyString(body)
|
||||
}
|
||||
|
||||
// Set : https://fiber.wiki/context#set
|
||||
func (ctx *Ctx) Set(key string, val string) {
|
||||
ctx.Fasthttp.Response.Header.SetCanonical(getBytes(key), getBytes(val))
|
||||
}
|
||||
|
||||
// Subdomains : https://fiber.wiki/context#subdomains
|
||||
func (ctx *Ctx) Subdomains(offset ...int) (subs []string) {
|
||||
o := 2
|
||||
if len(offset) > 0 {
|
||||
o = offset[0]
|
||||
}
|
||||
subs = strings.Split(ctx.Hostname(), ".")
|
||||
subs = subs[:len(subs)-o]
|
||||
return subs
|
||||
}
|
||||
|
||||
// SignedCookies : https://fiber.wiki/context#signedcookies
|
||||
func (ctx *Ctx) SignedCookies() {
|
||||
|
||||
}
|
||||
|
||||
// Stale : https://fiber.wiki/context#stale
|
||||
func (ctx *Ctx) Stale() bool {
|
||||
return !ctx.Fresh()
|
||||
}
|
||||
|
||||
// Status : https://fiber.wiki/context#status
|
||||
func (ctx *Ctx) Status(status int) *Ctx {
|
||||
ctx.Fasthttp.Response.SetStatusCode(status)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Type : https://fiber.wiki/context#type
|
||||
func (ctx *Ctx) Type(ext string) *Ctx {
|
||||
ctx.Fasthttp.Response.Header.SetContentType(getType(ext))
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Vary : https://fiber.wiki/context#vary
|
||||
func (ctx *Ctx) Vary(fields ...string) {
|
||||
if len(fields) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
h := getString(ctx.Fasthttp.Response.Header.Peek(fasthttp.HeaderVary))
|
||||
for i := range fields {
|
||||
if h == "" {
|
||||
h += fields[i]
|
||||
} else {
|
||||
h += ", " + fields[i]
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Set(fasthttp.HeaderVary, h)
|
||||
}
|
||||
|
||||
// Write : https://fiber.wiki/context#write
|
||||
func (ctx *Ctx) Write(args ...interface{}) {
|
||||
for i := range args {
|
||||
switch body := args[i].(type) {
|
||||
case string:
|
||||
ctx.Fasthttp.Response.AppendBodyString(body)
|
||||
case []byte:
|
||||
ctx.Fasthttp.Response.AppendBodyString(getString(body))
|
||||
default:
|
||||
ctx.Fasthttp.Response.AppendBodyString(fmt.Sprintf("%v", body))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// XHR : https://fiber.wiki/context#xhr
|
||||
func (ctx *Ctx) XHR() bool {
|
||||
return ctx.Get(fasthttp.HeaderXRequestedWith) == "XMLHttpRequest"
|
||||
}
|
@ -3,6 +3,7 @@ package fiber
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@ -92,11 +93,6 @@ func Test_AcceptsLanguages(t *testing.T) {
|
||||
if result != expect {
|
||||
t.Fatalf(`%s: Expecting %s, got %s`, t.Name(), expect, result)
|
||||
}
|
||||
expect = "*"
|
||||
result = c.AcceptsLanguages(expect)
|
||||
if result != expect {
|
||||
t.Fatalf(`%s: Expecting %s, got %s`, t.Name(), expect, result)
|
||||
}
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
req.Header.Set("Accept-Language", "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5")
|
||||
@ -111,7 +107,6 @@ func Test_AcceptsLanguages(t *testing.T) {
|
||||
func Test_BaseURL(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.BaseUrl() // deprecated
|
||||
expect := "http://google.com"
|
||||
result := c.BaseURL()
|
||||
if result != expect {
|
||||
@ -127,29 +122,6 @@ func Test_BaseURL(t *testing.T) {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
}
|
||||
func Test_BasicAuth(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
expect1 := "john"
|
||||
expect2 := "doe"
|
||||
result1, result2, _ := c.BasicAuth()
|
||||
if result1 != expect1 {
|
||||
t.Fatalf(`%s: Expecting %s, got %s`, t.Name(), expect1, expect1)
|
||||
}
|
||||
if result2 != expect2 {
|
||||
t.Fatalf(`%s: Expecting %s, got %s`, t.Name(), result2, expect2)
|
||||
}
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
req.SetBasicAuth("john", "doe")
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
}
|
||||
func Test_Body(t *testing.T) {
|
||||
app := New()
|
||||
app.Post("/test", func(c *Ctx) {
|
||||
@ -345,7 +317,6 @@ func Test_Hostname(t *testing.T) {
|
||||
func Test_IP(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Ip() // deprecated
|
||||
expect := "0.0.0.0"
|
||||
result := c.IP()
|
||||
if result != expect {
|
||||
@ -364,7 +335,6 @@ func Test_IP(t *testing.T) {
|
||||
func Test_IPs(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Ips() // deprecated
|
||||
expect := []string{"0.0.0.0", "1.1.1.1"}
|
||||
result := c.IPs()
|
||||
if result[0] != expect[0] && result[1] != expect[1] {
|
||||
@ -402,7 +372,6 @@ func Test_IPs(t *testing.T) {
|
||||
// t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
// }
|
||||
// }
|
||||
|
||||
func Test_Locals(t *testing.T) {
|
||||
app := New()
|
||||
app.Use(func(c *Ctx) {
|
||||
@ -509,7 +478,6 @@ func Test_MultipartForm(t *testing.T) {
|
||||
func Test_OriginalURL(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.OriginalUrl() // deprecated
|
||||
expect := "/test?search=demo"
|
||||
result := c.OriginalURL()
|
||||
if result != expect {
|
||||
@ -682,10 +650,6 @@ func Test_Subdomains(t *testing.T) {
|
||||
if result[0] != expect[0] && result[1] != expect[1] {
|
||||
t.Fatalf(`%s: Expecting %s, got %s`, t.Name(), expect, result)
|
||||
}
|
||||
result = c.Subdomains(1)
|
||||
if result[0] != expect[0] && result[1] != expect[1] {
|
||||
t.Fatalf(`%s: Expecting %s, got %s`, t.Name(), expect, result)
|
||||
}
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://john.doe.google.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
@ -699,7 +663,6 @@ func Test_Subdomains(t *testing.T) {
|
||||
func Test_XHR(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Xhr() // deprecated
|
||||
expect := true
|
||||
result := c.XHR()
|
||||
if result != expect {
|
||||
@ -716,3 +679,493 @@ func Test_XHR(t *testing.T) {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Append(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Append("X-Test", "hel")
|
||||
c.Append("X-Test", "lo", "world")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("X-Test") != "hel, lo, world" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "X-Test: hel, lo, world")
|
||||
}
|
||||
}
|
||||
func Test_Attachment(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Attachment()
|
||||
c.Attachment("./static/img/logo.png")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Content-Disposition") != `attachment; filename="logo.png"` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `attachment; filename="logo.png"`)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "image/png" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "image/png")
|
||||
}
|
||||
}
|
||||
func Test_ClearCookie(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.ClearCookie()
|
||||
})
|
||||
app.Get("/test2", func(c *Ctx) {
|
||||
c.ClearCookie("john")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
req.AddCookie(&http.Cookie{Name: "john", Value: "doe"})
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if !strings.Contains(resp.Header.Get("Set-Cookie"), "expires=") {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "expires=")
|
||||
}
|
||||
req, _ = http.NewRequest("GET", "/test2", nil)
|
||||
req.AddCookie(&http.Cookie{Name: "john", Value: "doe"})
|
||||
resp, err = app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if !strings.Contains(resp.Header.Get("Set-Cookie"), "expires=") {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "expires=")
|
||||
}
|
||||
}
|
||||
func Test_Cookie(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
options := &Cookie{
|
||||
MaxAge: 60,
|
||||
Domain: "example.com",
|
||||
Path: "/",
|
||||
HTTPOnly: true,
|
||||
Secure: false,
|
||||
SameSite: "lax",
|
||||
}
|
||||
c.Cookie("name", "john", options)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if !strings.Contains(resp.Header.Get("Set-Cookie"), "name=john; max-age=60; domain=example.com; path=/; HttpOnly; SameSite=Lax") {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "name=john; max-age=60; domain=example.com; path=/; HttpOnly; SameSite=Lax")
|
||||
}
|
||||
}
|
||||
func Test_Download(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
func Test_Format(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Format("Hello, World!")
|
||||
})
|
||||
app.Get("/test2", func(c *Ctx) {
|
||||
c.Format([]byte("Hello, World!"))
|
||||
c.Format("Hello, World!")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
req.Header.Set("Accept", "text/html")
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != "<p>Hello, World!</p>" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "<p>Hello, World!</p>")
|
||||
}
|
||||
|
||||
req, _ = http.NewRequest("GET", "http://example.com/test2", nil)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
resp, err = app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `"Hello, World!"` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `"Hello, World!"`)
|
||||
}
|
||||
}
|
||||
func Test_HeadersSent(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
func Test_JSON(t *testing.T) {
|
||||
type SomeStruct struct {
|
||||
Name string
|
||||
Age uint8
|
||||
}
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
if err := c.JSON(""); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
data := SomeStruct{
|
||||
Name: "Grame",
|
||||
Age: 20,
|
||||
}
|
||||
if err := c.JSON(data); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "application/json")
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `{"Name":"Grame","Age":20}` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `{"Name":"Grame","Age":20}`)
|
||||
}
|
||||
}
|
||||
func Test_JSONP(t *testing.T) {
|
||||
type SomeStruct struct {
|
||||
Name string
|
||||
Age uint8
|
||||
}
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
if err := c.JSONP(""); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
data := SomeStruct{
|
||||
Name: "Grame",
|
||||
Age: 20,
|
||||
}
|
||||
if err := c.JSONP(data, "alwaysjohn"); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "application/javascript" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "application/javascript")
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `alwaysjohn({"Name":"Grame","Age":20});` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `alwaysjohn({"Name":"Grame","Age":20});`)
|
||||
}
|
||||
}
|
||||
func Test_Links(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Links(
|
||||
"http://api.example.com/users?page=2", "next",
|
||||
"http://api.example.com/users?page=5", "last",
|
||||
)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Link") != `<http://api.example.com/users?page=2>; rel="next",<http://api.example.com/users?page=5>; rel="last"` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Link: <http://api.example.com/users?page=2>; rel="next",<http://api.example.com/users?page=5>; rel="last"`)
|
||||
}
|
||||
}
|
||||
func Test_Location(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Location("http://example.com")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Location") != "http://example.com" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "http://example.com")
|
||||
}
|
||||
}
|
||||
func Test_Next(t *testing.T) {
|
||||
app := New()
|
||||
app.Use("/", func(c *Ctx) {
|
||||
c.Next()
|
||||
})
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Set("X-Next-Result", "Works")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("X-Next-Result") != "Works" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "X-Next-Results: Works")
|
||||
}
|
||||
}
|
||||
func Test_Redirect(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Redirect("http://example.com", 301)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 301 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Location") != "http://example.com" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "Location: http://example.com")
|
||||
}
|
||||
}
|
||||
func Test_Render(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
func Test_Send(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Send([]byte("Hello, World"))
|
||||
c.Send("Don't crash please")
|
||||
c.Send(1337)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `1337` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `1337`)
|
||||
}
|
||||
}
|
||||
func Test_SendBytes(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.SendBytes([]byte("Hello, World"))
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `Hello, World` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Hello, World`)
|
||||
}
|
||||
}
|
||||
func Test_SendStatus(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.SendStatus(415)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 415 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `Unsupported Media Type` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Unsupported Media Type`)
|
||||
}
|
||||
}
|
||||
func Test_SendString(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.SendString("Don't crash please")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `Don't crash please` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Don't crash please`)
|
||||
}
|
||||
}
|
||||
func Test_Set(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Set("X-1", "1")
|
||||
c.Set("X-2", "2")
|
||||
c.Set("X-3", "3")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("X-1") != "1" {
|
||||
t.Fatalf(`%s: Expected %v`, t.Name(), "X-1: 1")
|
||||
}
|
||||
if resp.Header.Get("X-2") != "2" {
|
||||
t.Fatalf(`%s: Expected %v`, t.Name(), "X-2: 2")
|
||||
}
|
||||
if resp.Header.Get("X-3") != "3" {
|
||||
t.Fatalf(`%s: Expected %v`, t.Name(), "X-3: 3")
|
||||
}
|
||||
}
|
||||
func Test_Status(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Status(400)
|
||||
c.Status(415).Send("Hello, World")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 415 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `Hello, World` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Hello, World`)
|
||||
}
|
||||
}
|
||||
func Test_Type(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Type(".json")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf(`%s: Expected %v`, t.Name(), `Content-Type: application/json`)
|
||||
}
|
||||
}
|
||||
func Test_Vary(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Vary("Origin")
|
||||
c.Vary("User-Agent")
|
||||
c.Vary("Accept-Encoding", "Accept")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Vary") != "Origin, User-Agent, Accept-Encoding, Accept" {
|
||||
t.Fatalf(`%s: Expected %v`, t.Name(), `Vary: Origin, User-Agent, Accept-Encoding, Accept`)
|
||||
}
|
||||
}
|
||||
func Test_Write(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Write("Hello, ")
|
||||
c.Write([]byte("World! "))
|
||||
c.Write(123)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `Hello, World! 123` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Hello, World! 123`)
|
||||
}
|
||||
}
|
8
go.mod
8
go.mod
@ -1,15 +1,15 @@
|
||||
module github.com/gofiber/fiber
|
||||
|
||||
go 1.13
|
||||
go 1.11
|
||||
|
||||
require (
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect
|
||||
github.com/CloudyKit/jet v2.1.2+incompatible
|
||||
github.com/Joker/jade v1.0.0
|
||||
github.com/aymerick/raymond v2.0.2+incompatible
|
||||
github.com/cbroglie/mustache v1.0.1
|
||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385
|
||||
github.com/fasthttp/websocket v1.4.1
|
||||
github.com/gorilla/schema v1.1.0
|
||||
github.com/json-iterator/go v1.1.9
|
||||
github.com/valyala/fasthttp v1.9.0
|
||||
github.com/yosssi/ace v0.0.5
|
||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||
)
|
||||
|
21
go.sum
21
go.sum
@ -1,7 +1,6 @@
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c=
|
||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
||||
github.com/CloudyKit/jet v2.1.2+incompatible h1:ybZoYzMBdoijK6I+Ke3vg9GZsmlKo/ZhKdNMWz0P26c=
|
||||
github.com/CloudyKit/jet v2.1.2+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
|
||||
github.com/Joker/hpp v0.0.0-20180418125244-6893e659854a/go.mod h1:MzD2WMdSxvbHw5fM/OXOFily/lipJWRc9C1px0Mt0ZE=
|
||||
github.com/Joker/jade v1.0.0 h1:lOCEPvTAtWfLpSZYMOv/g44MGQFAolbKh2khHHGu0Kc=
|
||||
github.com/Joker/jade v1.0.0/go.mod h1:efZIdO0py/LtcJRSa/j2WEklMSAw84WV0zZVMxNToB8=
|
||||
github.com/aymerick/raymond v2.0.2+incompatible h1:VEp3GpgdAnv9B2GFyTvqgcKvY+mfKMjPOA3SbKLtnU0=
|
||||
github.com/aymerick/raymond v2.0.2+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
|
||||
github.com/cbroglie/mustache v1.0.1 h1:ivMg8MguXq/rrz2eu3tw6g3b16+PQhoTn6EZAhst2mw=
|
||||
@ -10,13 +9,17 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
|
||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
||||
github.com/fasthttp/websocket v1.4.1 h1:fisgNMCNCbIPM5GRRRTAckRrynbSzf76fevcJYJYnSM=
|
||||
github.com/fasthttp/websocket v1.4.1/go.mod h1:toetUvZ3KISxtZERe0wzPPpnaN8GZCKHCowWctwA50o=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
|
||||
github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2KCcs=
|
||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
@ -24,17 +27,21 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/savsgio/gotils v0.0.0-20190714152828-365999d0a274 h1:F52t1X2ziOrMcQMVHo8ZxwOrDTMAq6MrlKtL1Atu2wU=
|
||||
github.com/savsgio/gotils v0.0.0-20190714152828-365999d0a274/go.mod h1:w803/Fg1m0hrp1ZT9KNfQe4E4+WOMMFLcgzPvOcye10=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.4.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
|
||||
github.com/valyala/fasthttp v1.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw=
|
||||
github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a h1:0R4NLDRDZX6JcmhJgXi5E4b8Wg84ihbmUKp/GvSPEzc=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=
|
||||
github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
121
group.go
Normal file
121
group.go
Normal file
@ -0,0 +1,121 @@
|
||||
package fiber
|
||||
|
||||
import "strings"
|
||||
|
||||
// Group ...
|
||||
type Group struct {
|
||||
prefix string
|
||||
app *App
|
||||
}
|
||||
|
||||
// Group ...
|
||||
func (app *App) Group(prefix string, args ...interface{}) *Group {
|
||||
if len(args) > 0 {
|
||||
app.register("USE", prefix, args...)
|
||||
}
|
||||
return &Group{
|
||||
prefix: prefix,
|
||||
app: app,
|
||||
}
|
||||
}
|
||||
|
||||
// Group ...
|
||||
func (grp *Group) Group(newPrfx string, args ...interface{}) *Group {
|
||||
var prefix = grp.prefix
|
||||
if len(newPrfx) > 0 && newPrfx[0] != '/' && newPrfx[0] != '*' {
|
||||
newPrfx = "/" + newPrfx
|
||||
}
|
||||
// When grouping, always remove single slash
|
||||
if len(prefix) > 0 && newPrfx == "/" {
|
||||
newPrfx = ""
|
||||
}
|
||||
// Prepent group prefix if exist
|
||||
prefix = prefix + newPrfx
|
||||
// Clean path by removing double "//" => "/"
|
||||
prefix = strings.Replace(prefix, "//", "/", -1)
|
||||
if len(args) > 0 {
|
||||
grp.app.register("USE", prefix, args...)
|
||||
}
|
||||
return &Group{
|
||||
prefix: prefix,
|
||||
app: grp.app,
|
||||
}
|
||||
}
|
||||
|
||||
// Static ...
|
||||
func (grp *Group) Static(args ...string) *Group {
|
||||
grp.app.registerStatic(grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// WebSocket ...
|
||||
func (grp *Group) WebSocket(args ...interface{}) *Group {
|
||||
grp.app.register("GET", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Connect ...
|
||||
func (grp *Group) Connect(args ...interface{}) *Group {
|
||||
grp.app.register("CONNECT", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Put ...
|
||||
func (grp *Group) Put(args ...interface{}) *Group {
|
||||
grp.app.register("PUT", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Post ...
|
||||
func (grp *Group) Post(args ...interface{}) *Group {
|
||||
grp.app.register("POST", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Delete ...
|
||||
func (grp *Group) Delete(args ...interface{}) *Group {
|
||||
grp.app.register("DELETE", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Head ...
|
||||
func (grp *Group) Head(args ...interface{}) *Group {
|
||||
grp.app.register("HEAD", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Patch ...
|
||||
func (grp *Group) Patch(args ...interface{}) *Group {
|
||||
grp.app.register("PATCH", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Options ...
|
||||
func (grp *Group) Options(args ...interface{}) *Group {
|
||||
grp.app.register("OPTIONS", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Trace ...
|
||||
func (grp *Group) Trace(args ...interface{}) *Group {
|
||||
grp.app.register("TRACE", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Get ...
|
||||
func (grp *Group) Get(args ...interface{}) *Group {
|
||||
grp.app.register("GET", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// All ...
|
||||
func (grp *Group) All(args ...interface{}) *Group {
|
||||
grp.app.register("ALL", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Use ...
|
||||
func (grp *Group) Use(args ...interface{}) *Group {
|
||||
grp.app.register("USE", grp.prefix, args...)
|
||||
return grp
|
||||
}
|
442
request.go
442
request.go
@ -1,442 +0,0 @@
|
||||
// 🚀 Fiber is an Express.js inspired web framework written in Go with 💖
|
||||
// 📌 Please open an issue if you got suggestions or found a bug!
|
||||
// 🖥 Links: https://github.com/gofiber/fiber, https://fiber.wiki
|
||||
|
||||
// 🦸 Not all heroes wear capes, thank you to some amazing people
|
||||
// 💖 @valyala, @erikdubbelboer, @savsgio, @julienschmidt, @koddr
|
||||
|
||||
package fiber
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
fasthttp "github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Accepts : https://fiber.wiki/context#accepts
|
||||
func (ctx *Ctx) Accepts(offers ...string) string {
|
||||
if len(offers) == 0 {
|
||||
return ""
|
||||
}
|
||||
h := ctx.Get(fasthttp.HeaderAccept)
|
||||
if h == "" {
|
||||
return offers[0]
|
||||
}
|
||||
|
||||
specs := strings.Split(h, ",")
|
||||
for _, offer := range offers {
|
||||
mimetype := getType(offer)
|
||||
// if mimetype != "" {
|
||||
// mimetype = strings.Split(mimetype, ";")[0]
|
||||
// } else {
|
||||
// mimetype = offer
|
||||
// }
|
||||
for _, spec := range specs {
|
||||
spec = strings.TrimSpace(spec)
|
||||
if strings.HasPrefix(spec, "*/*") {
|
||||
return offer
|
||||
}
|
||||
|
||||
if strings.HasPrefix(spec, mimetype) {
|
||||
return offer
|
||||
}
|
||||
|
||||
if strings.Contains(spec, "/*") {
|
||||
if strings.HasPrefix(spec, strings.Split(mimetype, "/")[0]) {
|
||||
return offer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// AcceptsCharsets : https://fiber.wiki/context#acceptscharsets
|
||||
func (ctx *Ctx) AcceptsCharsets(offers ...string) string {
|
||||
if len(offers) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
h := ctx.Get(fasthttp.HeaderAcceptCharset)
|
||||
if h == "" {
|
||||
return offers[0]
|
||||
}
|
||||
|
||||
specs := strings.Split(h, ",")
|
||||
for _, offer := range offers {
|
||||
for _, spec := range specs {
|
||||
spec = strings.TrimSpace(spec)
|
||||
if strings.HasPrefix(spec, "*") {
|
||||
return offer
|
||||
}
|
||||
if strings.HasPrefix(spec, offer) {
|
||||
return offer
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// AcceptsEncodings : https://fiber.wiki/context#acceptsencodings
|
||||
func (ctx *Ctx) AcceptsEncodings(offers ...string) string {
|
||||
if len(offers) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
h := ctx.Get(fasthttp.HeaderAcceptEncoding)
|
||||
if h == "" {
|
||||
return offers[0]
|
||||
}
|
||||
|
||||
specs := strings.Split(h, ",")
|
||||
for _, offer := range offers {
|
||||
for _, spec := range specs {
|
||||
spec = strings.TrimSpace(spec)
|
||||
if strings.HasPrefix(spec, "*") {
|
||||
return offer
|
||||
}
|
||||
if strings.HasPrefix(spec, offer) {
|
||||
return offer
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// AcceptsLanguages : https://fiber.wiki/context#acceptslanguages
|
||||
func (ctx *Ctx) AcceptsLanguages(offers ...string) string {
|
||||
if len(offers) == 0 {
|
||||
return ""
|
||||
}
|
||||
h := ctx.Get(fasthttp.HeaderAcceptLanguage)
|
||||
if h == "" {
|
||||
return offers[0]
|
||||
}
|
||||
|
||||
specs := strings.Split(h, ",")
|
||||
for _, offer := range offers {
|
||||
for _, spec := range specs {
|
||||
spec = strings.TrimSpace(spec)
|
||||
if strings.HasPrefix(spec, "*") {
|
||||
return offer
|
||||
}
|
||||
if strings.HasPrefix(spec, offer) {
|
||||
return offer
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// BaseUrl will be removed in v2
|
||||
func (ctx *Ctx) BaseUrl() string {
|
||||
fmt.Println("Fiber deprecated c.BaseUrl(), this will be removed in v2: Use c.BaseURL() instead")
|
||||
return ctx.BaseURL()
|
||||
}
|
||||
|
||||
// BaseURL : https://fiber.wiki/context#baseurl
|
||||
func (ctx *Ctx) BaseURL() string {
|
||||
return ctx.Protocol() + "://" + ctx.Hostname()
|
||||
}
|
||||
|
||||
// BasicAuth : https://fiber.wiki/context#basicauth
|
||||
func (ctx *Ctx) BasicAuth() (user, pass string, ok bool) {
|
||||
fmt.Println("Fiber deprecated c.BasicAuth(), this will be removed in v2 and be available as a separate middleware")
|
||||
auth := ctx.Get(fasthttp.HeaderAuthorization)
|
||||
if auth == "" {
|
||||
return
|
||||
}
|
||||
|
||||
const prefix = "Basic "
|
||||
|
||||
// Case insensitive prefix match.
|
||||
if len(auth) < len(prefix) || !strings.EqualFold(auth[:len(prefix)], prefix) {
|
||||
return
|
||||
}
|
||||
|
||||
c, err := base64.StdEncoding.DecodeString(auth[len(prefix):])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cs := getString(c)
|
||||
s := strings.IndexByte(cs, ':')
|
||||
if s < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
return cs[:s], cs[s+1:], true
|
||||
}
|
||||
|
||||
// Body : https://fiber.wiki/context#body
|
||||
func (ctx *Ctx) Body(args ...interface{}) string {
|
||||
if len(args) == 0 {
|
||||
return getString(ctx.Fasthttp.Request.Body())
|
||||
}
|
||||
|
||||
if len(args) == 1 {
|
||||
switch arg := args[0].(type) {
|
||||
case string:
|
||||
return getString(ctx.Fasthttp.Request.PostArgs().Peek(arg))
|
||||
case []byte:
|
||||
return getString(ctx.Fasthttp.Request.PostArgs().PeekBytes(arg))
|
||||
case func(string, string):
|
||||
ctx.Fasthttp.Request.PostArgs().VisitAll(func(k []byte, v []byte) {
|
||||
arg(getString(k), getString(v))
|
||||
})
|
||||
default:
|
||||
return getString(ctx.Fasthttp.Request.Body())
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// BodyParser : https://fiber.wiki/context#bodyparser
|
||||
func (ctx *Ctx) BodyParser(v interface{}) error {
|
||||
ctype := getString(ctx.Fasthttp.Request.Header.ContentType())
|
||||
// application/json
|
||||
if strings.HasPrefix(ctype, mimeApplicationJSON) {
|
||||
return jsoniter.Unmarshal(ctx.Fasthttp.Request.Body(), v)
|
||||
}
|
||||
// application/xml text/xml
|
||||
if strings.HasPrefix(ctype, mimeApplicationXML) || strings.HasPrefix(ctype, mimeTextXML) {
|
||||
return xml.Unmarshal(ctx.Fasthttp.Request.Body(), v)
|
||||
}
|
||||
// application/x-www-form-urlencoded
|
||||
if strings.HasPrefix(ctype, mimeApplicationForm) {
|
||||
data, err := url.ParseQuery(getString(ctx.Fasthttp.PostBody()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return schemaDecoder.Decode(v, data)
|
||||
}
|
||||
// multipart/form-data
|
||||
if strings.HasPrefix(ctype, mimeMultipartForm) {
|
||||
data, err := ctx.Fasthttp.MultipartForm()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return schemaDecoder.Decode(v, data.Value)
|
||||
|
||||
}
|
||||
return fmt.Errorf("cannot parse content-type: %v", ctype)
|
||||
}
|
||||
|
||||
// Cookies : https://fiber.wiki/context#cookies
|
||||
func (ctx *Ctx) Cookies(args ...interface{}) string {
|
||||
if len(args) == 0 {
|
||||
return ctx.Get(fasthttp.HeaderCookie)
|
||||
}
|
||||
|
||||
switch arg := args[0].(type) {
|
||||
case string:
|
||||
return getString(ctx.Fasthttp.Request.Header.Cookie(arg))
|
||||
case []byte:
|
||||
return getString(ctx.Fasthttp.Request.Header.CookieBytes(arg))
|
||||
case func(string, string):
|
||||
ctx.Fasthttp.Request.Header.VisitAllCookie(func(k, v []byte) {
|
||||
arg(getString(k), getString(v))
|
||||
})
|
||||
default:
|
||||
return ctx.Get(fasthttp.HeaderCookie)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// Error returns err that is passed via Next(err)
|
||||
func (ctx *Ctx) Error() error {
|
||||
return ctx.error
|
||||
}
|
||||
|
||||
// FormFile : https://fiber.wiki/context#formfile
|
||||
func (ctx *Ctx) FormFile(key string) (*multipart.FileHeader, error) {
|
||||
return ctx.Fasthttp.FormFile(key)
|
||||
}
|
||||
|
||||
// FormValue : https://fiber.wiki/context#formvalue
|
||||
func (ctx *Ctx) FormValue(key string) string {
|
||||
return getString(ctx.Fasthttp.FormValue(key))
|
||||
}
|
||||
|
||||
// Fresh : https://fiber.wiki/context#fresh
|
||||
func (ctx *Ctx) Fresh() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Get : https://fiber.wiki/context#get
|
||||
func (ctx *Ctx) Get(key string) string {
|
||||
if key == "referrer" {
|
||||
key = "referer"
|
||||
}
|
||||
return getString(ctx.Fasthttp.Request.Header.Peek(key))
|
||||
}
|
||||
|
||||
// Hostname : https://fiber.wiki/context#hostname
|
||||
func (ctx *Ctx) Hostname() string {
|
||||
return getString(ctx.Fasthttp.URI().Host())
|
||||
}
|
||||
|
||||
// Ip will be removed in v2
|
||||
func (ctx *Ctx) Ip() string {
|
||||
fmt.Println("Fiber deprecated c.Ip(), this will be removed in v2: Use c.IP() instead")
|
||||
return ctx.IP()
|
||||
}
|
||||
|
||||
// IP : https://fiber.wiki/context#Ip
|
||||
func (ctx *Ctx) IP() string {
|
||||
return ctx.Fasthttp.RemoteIP().String()
|
||||
}
|
||||
|
||||
// Ips will be removed in v2
|
||||
func (ctx *Ctx) Ips() []string { // NOLINT
|
||||
fmt.Println("Fiber deprecated c.Ips(), this will be removed in v2: Use c.IPs() instead")
|
||||
return ctx.IPs()
|
||||
}
|
||||
|
||||
// IPs : https://fiber.wiki/context#ips
|
||||
func (ctx *Ctx) IPs() []string {
|
||||
ips := strings.Split(ctx.Get(fasthttp.HeaderXForwardedFor), ",")
|
||||
for i := range ips {
|
||||
ips[i] = strings.TrimSpace(ips[i])
|
||||
}
|
||||
return ips
|
||||
}
|
||||
|
||||
// Is : https://fiber.wiki/context#is
|
||||
func (ctx *Ctx) Is(ext string) bool {
|
||||
if ext[0] != '.' {
|
||||
ext = "." + ext
|
||||
}
|
||||
|
||||
exts, _ := mime.ExtensionsByType(ctx.Get(fasthttp.HeaderContentType))
|
||||
if len(exts) > 0 {
|
||||
for _, item := range exts {
|
||||
if item == ext {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Locals : https://fiber.wiki/context#locals
|
||||
func (ctx *Ctx) Locals(key string, val ...interface{}) interface{} {
|
||||
if len(val) == 0 {
|
||||
return ctx.Fasthttp.UserValue(key)
|
||||
}
|
||||
|
||||
ctx.Fasthttp.SetUserValue(key, val[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Method : https://fiber.wiki/context#method
|
||||
func (ctx *Ctx) Method() string {
|
||||
return getString(ctx.Fasthttp.Request.Header.Method())
|
||||
}
|
||||
|
||||
// MultipartForm : https://fiber.wiki/context#multipartform
|
||||
func (ctx *Ctx) MultipartForm() (*multipart.Form, error) {
|
||||
return ctx.Fasthttp.MultipartForm()
|
||||
}
|
||||
|
||||
// OriginalUrl will be removed in v2
|
||||
func (ctx *Ctx) OriginalUrl() string {
|
||||
fmt.Println("Fiber deprecated c.OriginalUrl(), this will be removed in v2: Use c.OriginalURL() instead")
|
||||
return ctx.OriginalURL()
|
||||
}
|
||||
|
||||
// OriginalURL : https://fiber.wiki/context#originalurl
|
||||
func (ctx *Ctx) OriginalURL() string {
|
||||
return getString(ctx.Fasthttp.Request.Header.RequestURI())
|
||||
}
|
||||
|
||||
// Params : https://fiber.wiki/context#params
|
||||
func (ctx *Ctx) Params(key string) string {
|
||||
for i := 0; i < len(*ctx.params); i++ {
|
||||
if (*ctx.params)[i] == key {
|
||||
return ctx.values[i]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Path : https://fiber.wiki/context#path
|
||||
func (ctx *Ctx) Path() string {
|
||||
return getString(ctx.Fasthttp.URI().Path())
|
||||
}
|
||||
|
||||
// Protocol : https://fiber.wiki/context#protocol
|
||||
func (ctx *Ctx) Protocol() string {
|
||||
if ctx.Fasthttp.IsTLS() {
|
||||
return "https"
|
||||
}
|
||||
return "http"
|
||||
}
|
||||
|
||||
// Query : https://fiber.wiki/context#query
|
||||
func (ctx *Ctx) Query(key string) string {
|
||||
return getString(ctx.Fasthttp.QueryArgs().Peek(key))
|
||||
}
|
||||
|
||||
// Range : https://fiber.wiki/context#range
|
||||
func (ctx *Ctx) Range() {
|
||||
// https://expressjs.com/en/api.html#req.range
|
||||
// https://github.com/jshttp/range-parser/blob/master/index.js
|
||||
// r := ctx.Fasthttp.Request.Header.Peek(fasthttp.HeaderRange)
|
||||
// *magic*
|
||||
}
|
||||
|
||||
// Route : https://fiber.wiki/context#route
|
||||
func (ctx *Ctx) Route() *Route {
|
||||
return ctx.route
|
||||
}
|
||||
|
||||
// SaveFile : https://fiber.wiki/context#secure
|
||||
func (ctx *Ctx) SaveFile(fh *multipart.FileHeader, path string) error {
|
||||
return fasthttp.SaveMultipartFile(fh, path)
|
||||
}
|
||||
|
||||
// Secure : https://fiber.wiki/context#secure
|
||||
func (ctx *Ctx) Secure() bool {
|
||||
return ctx.Fasthttp.IsTLS()
|
||||
}
|
||||
|
||||
// SignedCookies : https://fiber.wiki/context#signedcookies
|
||||
func (ctx *Ctx) SignedCookies() {
|
||||
|
||||
}
|
||||
|
||||
// Stale : https://fiber.wiki/context#stale
|
||||
func (ctx *Ctx) Stale() bool {
|
||||
return !ctx.Fresh()
|
||||
}
|
||||
|
||||
// Subdomains : https://fiber.wiki/context#subdomains
|
||||
func (ctx *Ctx) Subdomains(offset ...int) (subs []string) {
|
||||
o := 2
|
||||
if len(offset) > 0 {
|
||||
o = offset[0]
|
||||
}
|
||||
subs = strings.Split(ctx.Hostname(), ".")
|
||||
subs = subs[:len(subs)-o]
|
||||
return subs
|
||||
}
|
||||
|
||||
// Xhr will be removed in v2
|
||||
func (ctx *Ctx) Xhr() bool {
|
||||
fmt.Println("Fiber deprecated c.Xhr(), this will be removed in v2: Use c.XHR() instead")
|
||||
return ctx.XHR()
|
||||
}
|
||||
|
||||
// XHR : https://fiber.wiki/context#xhr
|
||||
func (ctx *Ctx) XHR() bool {
|
||||
return ctx.Get(fasthttp.HeaderXRequestedWith) == "XMLHttpRequest"
|
||||
}
|
483
response.go
483
response.go
@ -1,483 +0,0 @@
|
||||
// 🚀 Fiber is an Express.js inspired web framework written in Go with 💖
|
||||
// 📌 Please open an issue if you got suggestions or found a bug!
|
||||
// 🖥 Links: https://github.com/gofiber/fiber, https://fiber.wiki
|
||||
|
||||
// 🦸 Not all heroes wear capes, thank you to some amazing people
|
||||
// 💖 @valyala, @erikdubbelboer, @savsgio, @julienschmidt, @koddr
|
||||
|
||||
package fiber
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/CloudyKit/jet"
|
||||
"github.com/aymerick/raymond"
|
||||
"github.com/cbroglie/mustache"
|
||||
"github.com/eknkc/amber"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
fasthttp "github.com/valyala/fasthttp"
|
||||
"github.com/yosssi/ace"
|
||||
)
|
||||
|
||||
// Cookie : struct
|
||||
type Cookie struct {
|
||||
Expire int // time.Unix(1578981376, 0)
|
||||
MaxAge int
|
||||
Domain string
|
||||
Path string
|
||||
|
||||
HTTPOnly bool
|
||||
Secure bool
|
||||
SameSite string
|
||||
}
|
||||
|
||||
// Append : https://fiber.wiki/context#append
|
||||
func (ctx *Ctx) Append(field string, values ...string) {
|
||||
if len(values) == 0 {
|
||||
return
|
||||
}
|
||||
h := getString(ctx.Fasthttp.Response.Header.Peek(field))
|
||||
for i := range values {
|
||||
if h == "" {
|
||||
h += values[i]
|
||||
} else {
|
||||
h += ", " + values[i]
|
||||
}
|
||||
}
|
||||
ctx.Set(field, h)
|
||||
}
|
||||
|
||||
// Attachment : https://fiber.wiki/context#attachment
|
||||
func (ctx *Ctx) Attachment(name ...string) {
|
||||
if len(name) > 0 {
|
||||
filename := filepath.Base(name[0])
|
||||
ctx.Type(filepath.Ext(filename))
|
||||
ctx.Set(fasthttp.HeaderContentDisposition, `attachment; filename="`+filename+`"`)
|
||||
return
|
||||
}
|
||||
ctx.Set(fasthttp.HeaderContentDisposition, "attachment")
|
||||
}
|
||||
|
||||
// ClearCookie : https://fiber.wiki/context#clearcookie
|
||||
func (ctx *Ctx) ClearCookie(name ...string) {
|
||||
if len(name) > 0 {
|
||||
for i := range name {
|
||||
//ctx.Fasthttp.Request.Header.DelAllCookies()
|
||||
ctx.Fasthttp.Response.Header.DelClientCookie(name[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
//ctx.Fasthttp.Response.Header.DelAllCookies()
|
||||
ctx.Fasthttp.Request.Header.VisitAllCookie(func(k, v []byte) {
|
||||
ctx.Fasthttp.Response.Header.DelClientCookie(getString(k))
|
||||
})
|
||||
}
|
||||
|
||||
// Cookie : https://fiber.wiki/context#cookie
|
||||
func (ctx *Ctx) Cookie(key, value string, options ...interface{}) {
|
||||
cook := &fasthttp.Cookie{}
|
||||
|
||||
cook.SetKey(key)
|
||||
cook.SetValue(value)
|
||||
|
||||
if len(options) > 0 {
|
||||
switch opt := options[0].(type) {
|
||||
case *Cookie:
|
||||
if opt.Expire > 0 {
|
||||
cook.SetExpire(time.Unix(int64(opt.Expire), 0))
|
||||
}
|
||||
if opt.MaxAge > 0 {
|
||||
cook.SetMaxAge(opt.MaxAge)
|
||||
}
|
||||
if opt.Domain != "" {
|
||||
cook.SetDomain(opt.Domain)
|
||||
}
|
||||
if opt.Path != "" {
|
||||
cook.SetPath(opt.Path)
|
||||
}
|
||||
if opt.HTTPOnly {
|
||||
cook.SetHTTPOnly(opt.HTTPOnly)
|
||||
}
|
||||
if opt.Secure {
|
||||
cook.SetSecure(opt.Secure)
|
||||
}
|
||||
if opt.SameSite != "" {
|
||||
sameSite := fasthttp.CookieSameSiteDefaultMode
|
||||
if strings.EqualFold(opt.SameSite, "lax") {
|
||||
sameSite = fasthttp.CookieSameSiteLaxMode
|
||||
} else if strings.EqualFold(opt.SameSite, "strict") {
|
||||
sameSite = fasthttp.CookieSameSiteStrictMode
|
||||
} else if strings.EqualFold(opt.SameSite, "none") {
|
||||
sameSite = fasthttp.CookieSameSiteNoneMode
|
||||
}
|
||||
// } else {
|
||||
// sameSite = fasthttp.CookieSameSiteDisabled
|
||||
// }
|
||||
cook.SetSameSite(sameSite)
|
||||
}
|
||||
default:
|
||||
log.Println("Cookie: Invalid &Cookie{} struct")
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Fasthttp.Response.Header.SetCookie(cook)
|
||||
}
|
||||
|
||||
// Download : https://fiber.wiki/context#download
|
||||
func (ctx *Ctx) Download(file string, name ...string) {
|
||||
filename := filepath.Base(file)
|
||||
|
||||
if len(name) > 0 {
|
||||
filename = name[0]
|
||||
}
|
||||
|
||||
ctx.Set(fasthttp.HeaderContentDisposition, "attachment; filename="+filename)
|
||||
ctx.SendFile(file)
|
||||
}
|
||||
|
||||
// End : https://fiber.wiki/context#end
|
||||
func (ctx *Ctx) End() {
|
||||
|
||||
}
|
||||
|
||||
// Format : https://fiber.wiki/context#format
|
||||
func (ctx *Ctx) Format(args ...interface{}) {
|
||||
var body string
|
||||
|
||||
accept := ctx.Accepts("html", "json")
|
||||
|
||||
for i := range args {
|
||||
switch arg := args[i].(type) {
|
||||
case string:
|
||||
body = arg
|
||||
case []byte:
|
||||
body = getString(arg)
|
||||
default:
|
||||
body = fmt.Sprintf("%v", arg)
|
||||
}
|
||||
switch accept {
|
||||
case "html":
|
||||
ctx.SendString("<p>" + body + "</p>")
|
||||
case "json":
|
||||
if err := ctx.JSON(body); err != nil {
|
||||
log.Println("Format: error serializing json ", err)
|
||||
}
|
||||
default:
|
||||
ctx.SendString(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HeadersSent indicates if the app sent HTTP headers for the response.
|
||||
// func (ctx *Ctx) HeadersSent() {}
|
||||
|
||||
// Json will be removed in v2
|
||||
func (ctx *Ctx) Json(v interface{}) error {
|
||||
fmt.Println("Fiber deprecated c.Json(), this will be removed in v2: Use c.JSON() instead")
|
||||
return ctx.JSON(v)
|
||||
}
|
||||
|
||||
// JSON : https://fiber.wiki/context#json
|
||||
func (ctx *Ctx) JSON(v interface{}) error {
|
||||
ctx.Fasthttp.Response.Header.SetContentType(mimeApplicationJSON)
|
||||
raw, err := jsoniter.Marshal(&v)
|
||||
if err != nil {
|
||||
ctx.Fasthttp.Response.SetBodyString("")
|
||||
return err
|
||||
}
|
||||
ctx.Fasthttp.Response.SetBodyString(getString(raw))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// JsonBytes ...
|
||||
func (ctx *Ctx) JsonBytes(raw []byte) {
|
||||
ctx.JSONBytes(raw)
|
||||
}
|
||||
|
||||
// JSONBytes will be removed in v2
|
||||
func (ctx *Ctx) JSONBytes(raw []byte) {
|
||||
fmt.Println("Fiber deprecated c.JSONBytes(), this will function be removed in v2")
|
||||
ctx.Fasthttp.Response.Header.SetContentType(mimeApplicationJSON)
|
||||
ctx.Fasthttp.Response.SetBodyString(getString(raw))
|
||||
}
|
||||
|
||||
// Jsonp will be removed in v2
|
||||
func (ctx *Ctx) Jsonp(v interface{}, cb ...string) error {
|
||||
fmt.Println("Fiber deprecated c.Jsonp(), this will be removed in v2: Use c.JSONP() instead")
|
||||
return ctx.JSONP(v, cb...)
|
||||
}
|
||||
|
||||
// JSONP : https://fiber.wiki/context#jsonp
|
||||
func (ctx *Ctx) JSONP(v interface{}, cb ...string) error {
|
||||
raw, err := jsoniter.Marshal(&v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str := "callback("
|
||||
if len(cb) > 0 {
|
||||
str = cb[0] + "("
|
||||
}
|
||||
str += getString(raw) + ");"
|
||||
|
||||
ctx.Set(fasthttp.HeaderXContentTypeOptions, "nosniff")
|
||||
ctx.Fasthttp.Response.Header.SetContentType(mimeApplicationJavascript)
|
||||
ctx.Fasthttp.Response.SetBodyString(str)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// JsonString ...
|
||||
func (ctx *Ctx) JsonString(raw string) {
|
||||
ctx.JSONString(raw)
|
||||
}
|
||||
|
||||
// JSONString will be removed in v2
|
||||
func (ctx *Ctx) JSONString(raw string) {
|
||||
fmt.Println("Fiber deprecated c.JSONString(), this function will be removed in v2")
|
||||
ctx.Fasthttp.Response.Header.SetContentType(mimeApplicationJSON)
|
||||
ctx.Fasthttp.Response.SetBodyString(raw)
|
||||
}
|
||||
|
||||
// Links : https://fiber.wiki/context#links
|
||||
func (ctx *Ctx) Links(link ...string) {
|
||||
h := ""
|
||||
for i, l := range link {
|
||||
if i%2 == 0 {
|
||||
h += "<" + l + ">"
|
||||
} else {
|
||||
h += `; rel="` + l + `",`
|
||||
}
|
||||
}
|
||||
|
||||
if len(link) > 0 {
|
||||
h = strings.TrimSuffix(h, ",")
|
||||
ctx.Set(fasthttp.HeaderLink, h)
|
||||
}
|
||||
}
|
||||
|
||||
// Location : https://fiber.wiki/context#location
|
||||
func (ctx *Ctx) Location(path string) {
|
||||
ctx.Set(fasthttp.HeaderLocation, path)
|
||||
}
|
||||
|
||||
// Next : https://fiber.wiki/context#next
|
||||
func (ctx *Ctx) Next(err ...error) {
|
||||
ctx.route = nil
|
||||
ctx.next = true
|
||||
ctx.params = nil
|
||||
ctx.values = nil
|
||||
if len(err) > 0 {
|
||||
ctx.error = err[0]
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect : https://fiber.wiki/context#redirect
|
||||
func (ctx *Ctx) Redirect(path string, status ...int) {
|
||||
code := 302
|
||||
if len(status) > 0 {
|
||||
code = status[0]
|
||||
}
|
||||
|
||||
ctx.Set(fasthttp.HeaderLocation, path)
|
||||
ctx.Fasthttp.Response.SetStatusCode(code)
|
||||
}
|
||||
|
||||
// Render : https://fiber.wiki/context#render
|
||||
func (ctx *Ctx) Render(file string, v ...interface{}) error {
|
||||
var err error
|
||||
var raw []byte
|
||||
var html string
|
||||
var data interface{}
|
||||
var tmpl *template.Template
|
||||
if len(v) > 0 {
|
||||
data = v[0]
|
||||
}
|
||||
if raw, err = ioutil.ReadFile(file); err != nil {
|
||||
return err
|
||||
}
|
||||
engine := filepath.Ext(file)
|
||||
switch engine {
|
||||
case ".template": // https://golang.org/pkg/text/template/
|
||||
if tmpl, err = template.New("test").Parse(getString(raw)); err != nil {
|
||||
return err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err = tmpl.Execute(&buf, data); err != nil {
|
||||
return err
|
||||
}
|
||||
html = buf.String()
|
||||
case ".ace": // https://github.com/yosssi/ace
|
||||
if tmpl, err = ace.Load(strings.TrimSuffix(file, filepath.Ext(file)), "", nil); err != nil {
|
||||
return err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err = tmpl.Execute(&buf, data); err != nil {
|
||||
return err
|
||||
}
|
||||
html = buf.String()
|
||||
case ".amber": // https://github.com/eknkc/amber
|
||||
if tmpl, err = amber.Compile(getString(raw), amber.DefaultOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err = tmpl.Execute(&buf, data); err != nil {
|
||||
return err
|
||||
}
|
||||
html = buf.String()
|
||||
case ".jet": // https://github.com/CloudyKit/jet
|
||||
d, f := filepath.Split(file)
|
||||
var jetview = jet.NewHTMLSet(d)
|
||||
var t *jet.Template
|
||||
if t, err = jetview.GetTemplate(f); err != nil {
|
||||
return err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err = t.Execute(&buf, make(jet.VarMap), data); err != nil {
|
||||
return err
|
||||
}
|
||||
html = buf.String()
|
||||
case ".mustache": // https://github.com/hoisie/mustache
|
||||
if html, err = mustache.Render(getString(raw), data); err != nil {
|
||||
return err
|
||||
}
|
||||
case ".raymond": // https://github.com/aymerick/raymond
|
||||
if html, err = raymond.Render(getString(raw), data); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("render: does not support the %s extension", engine)
|
||||
}
|
||||
ctx.Set("Content-Type", "text/html")
|
||||
ctx.SendString(html)
|
||||
return err
|
||||
}
|
||||
|
||||
// Send : https://fiber.wiki/context#send
|
||||
func (ctx *Ctx) Send(args ...interface{}) {
|
||||
if len(args) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
switch body := args[0].(type) {
|
||||
case string:
|
||||
ctx.Fasthttp.Response.SetBodyString(body)
|
||||
case []byte:
|
||||
ctx.Fasthttp.Response.SetBodyString(getString(body))
|
||||
default:
|
||||
ctx.Fasthttp.Response.SetBodyString(fmt.Sprintf("%v", body))
|
||||
}
|
||||
}
|
||||
|
||||
// SendBytes : https://fiber.wiki/context#sendbytes
|
||||
func (ctx *Ctx) SendBytes(body []byte) {
|
||||
ctx.Fasthttp.Response.SetBodyString(getString(body))
|
||||
}
|
||||
|
||||
// SendFile : https://fiber.wiki/context#sendfile
|
||||
func (ctx *Ctx) SendFile(file string, gzip ...bool) {
|
||||
// Disable gzipping
|
||||
if len(gzip) > 0 && !gzip[0] {
|
||||
fasthttp.ServeFileUncompressed(ctx.Fasthttp, file)
|
||||
return
|
||||
}
|
||||
fasthttp.ServeFile(ctx.Fasthttp, file)
|
||||
// https://github.com/valyala/fasthttp/blob/master/fs.go#L81
|
||||
//ctx.Type(filepath.Ext(path))
|
||||
//ctx.Fasthttp.SendFile(path)
|
||||
}
|
||||
|
||||
// SendStatus : https://fiber.wiki/context#sendstatus
|
||||
func (ctx *Ctx) SendStatus(status int) {
|
||||
ctx.Fasthttp.Response.SetStatusCode(status)
|
||||
|
||||
// Only set status body when there is no response body
|
||||
if len(ctx.Fasthttp.Response.Body()) == 0 {
|
||||
msg := getStatus(status)
|
||||
if msg != "" {
|
||||
ctx.Fasthttp.Response.SetBodyString(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SendString : https://fiber.wiki/context#sendstring
|
||||
func (ctx *Ctx) SendString(body string) {
|
||||
ctx.Fasthttp.Response.SetBodyString(body)
|
||||
}
|
||||
|
||||
// Set : https://fiber.wiki/context#set
|
||||
func (ctx *Ctx) Set(key string, val string) {
|
||||
ctx.Fasthttp.Response.Header.SetCanonical(getBytes(key), getBytes(val))
|
||||
}
|
||||
|
||||
// Status : https://fiber.wiki/context#status
|
||||
func (ctx *Ctx) Status(status int) *Ctx {
|
||||
ctx.Fasthttp.Response.SetStatusCode(status)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Type : https://fiber.wiki/context#type
|
||||
func (ctx *Ctx) Type(ext string) *Ctx {
|
||||
ctx.Fasthttp.Response.Header.SetContentType(getType(ext))
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Vary : https://fiber.wiki/context#vary
|
||||
func (ctx *Ctx) Vary(fields ...string) {
|
||||
if len(fields) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
h := getString(ctx.Fasthttp.Response.Header.Peek(fasthttp.HeaderVary))
|
||||
for i := range fields {
|
||||
if h == "" {
|
||||
h += fields[i]
|
||||
} else {
|
||||
h += ", " + fields[i]
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Set(fasthttp.HeaderVary, h)
|
||||
}
|
||||
|
||||
// Write : https://fiber.wiki/context#write
|
||||
func (ctx *Ctx) Write(args ...interface{}) {
|
||||
for i := range args {
|
||||
switch body := args[i].(type) {
|
||||
case string:
|
||||
ctx.Fasthttp.Response.AppendBodyString(body)
|
||||
case []byte:
|
||||
ctx.Fasthttp.Response.AppendBodyString(getString(body))
|
||||
default:
|
||||
ctx.Fasthttp.Response.AppendBodyString(fmt.Sprintf("%v", body))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Xml ...
|
||||
func (ctx *Ctx) Xml(v interface{}) error {
|
||||
return ctx.XML(v)
|
||||
}
|
||||
|
||||
// XML will be removed in v2
|
||||
func (ctx *Ctx) XML(v interface{}) error {
|
||||
fmt.Println("Fiber deprecated c.XML(), this function will be removed in v2")
|
||||
raw, err := xml.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.Fasthttp.Response.Header.SetContentType(mimeApplicationXML)
|
||||
ctx.Fasthttp.Response.SetBody(raw)
|
||||
|
||||
return nil
|
||||
}
|
590
response_test.go
590
response_test.go
@ -1,590 +0,0 @@
|
||||
package fiber
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Append(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Append("X-Test", "hel")
|
||||
c.Append("X-Test", "lo", "world")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("X-Test") != "hel, lo, world" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "X-Test: hel, lo, world")
|
||||
}
|
||||
}
|
||||
func Test_Attachment(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Attachment()
|
||||
c.Attachment("./static/img/logo.png")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Content-Disposition") != `attachment; filename="logo.png"` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `attachment; filename="logo.png"`)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "image/png" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "image/png")
|
||||
}
|
||||
}
|
||||
func Test_ClearCookie(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.ClearCookie()
|
||||
})
|
||||
app.Get("/test2", func(c *Ctx) {
|
||||
c.ClearCookie("john")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "/test", nil)
|
||||
req.AddCookie(&http.Cookie{Name: "john", Value: "doe"})
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if !strings.Contains(resp.Header.Get("Set-Cookie"), "expires=") {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "expires=")
|
||||
}
|
||||
req, _ = http.NewRequest("GET", "/test2", nil)
|
||||
req.AddCookie(&http.Cookie{Name: "john", Value: "doe"})
|
||||
resp, err = app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if !strings.Contains(resp.Header.Get("Set-Cookie"), "expires=") {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "expires=")
|
||||
}
|
||||
}
|
||||
func Test_Cookie(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
options := &Cookie{
|
||||
MaxAge: 60,
|
||||
Domain: "example.com",
|
||||
Path: "/",
|
||||
HTTPOnly: true,
|
||||
Secure: false,
|
||||
SameSite: "lax",
|
||||
}
|
||||
c.Cookie("name", "john", options)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if !strings.Contains(resp.Header.Get("Set-Cookie"), "name=john; max-age=60; domain=example.com; path=/; HttpOnly; SameSite=Lax") {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "name=john; max-age=60; domain=example.com; path=/; HttpOnly; SameSite=Lax")
|
||||
}
|
||||
}
|
||||
func Test_Download(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
func Test_Format(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Format("Hello, World!")
|
||||
})
|
||||
app.Get("/test2", func(c *Ctx) {
|
||||
c.Format([]byte("Hello, World!"))
|
||||
c.Format("Hello, World!")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
req.Header.Set("Accept", "text/html")
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != "<p>Hello, World!</p>" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "<p>Hello, World!</p>")
|
||||
}
|
||||
|
||||
req, _ = http.NewRequest("GET", "http://example.com/test2", nil)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
resp, err = app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `"Hello, World!"` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `"Hello, World!"`)
|
||||
}
|
||||
}
|
||||
func Test_HeadersSent(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
func Test_JSON(t *testing.T) {
|
||||
type SomeStruct struct {
|
||||
Name string
|
||||
Age uint8
|
||||
}
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
if err := c.Json(""); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if err := c.JSON(""); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
data := SomeStruct{
|
||||
Name: "Grame",
|
||||
Age: 20,
|
||||
}
|
||||
if err := c.JSON(data); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "application/json")
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `{"Name":"Grame","Age":20}` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `{"Name":"Grame","Age":20}`)
|
||||
}
|
||||
}
|
||||
func Test_JSONBytes(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.JsonBytes([]byte(""))
|
||||
c.JSONBytes([]byte(`{"Name":"Grame","Age":20}`))
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "application/json")
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `{"Name":"Grame","Age":20}` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `{"Name":"Grame","Age":20}`)
|
||||
}
|
||||
}
|
||||
func Test_JSONP(t *testing.T) {
|
||||
type SomeStruct struct {
|
||||
Name string
|
||||
Age uint8
|
||||
}
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
if err := c.Jsonp(""); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if err := c.JSONP(""); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
data := SomeStruct{
|
||||
Name: "Grame",
|
||||
Age: 20,
|
||||
}
|
||||
if err := c.JSONP(data, "alwaysjohn"); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "application/javascript" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "application/javascript")
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `alwaysjohn({"Name":"Grame","Age":20});` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `alwaysjohn({"Name":"Grame","Age":20});`)
|
||||
}
|
||||
}
|
||||
func Test_JSONString(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.JsonString("")
|
||||
c.JSONString(`{"Name":"Grame","Age":20}`)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "application/json")
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `{"Name":"Grame","Age":20}` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `{"Name":"Grame","Age":20}`)
|
||||
}
|
||||
}
|
||||
func Test_Links(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Links(
|
||||
"http://api.example.com/users?page=2", "next",
|
||||
"http://api.example.com/users?page=5", "last",
|
||||
)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Link") != `<http://api.example.com/users?page=2>; rel="next",<http://api.example.com/users?page=5>; rel="last"` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Link: <http://api.example.com/users?page=2>; rel="next",<http://api.example.com/users?page=5>; rel="last"`)
|
||||
}
|
||||
}
|
||||
func Test_Location(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Location("http://example.com")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Location") != "http://example.com" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "http://example.com")
|
||||
}
|
||||
}
|
||||
func Test_Next(t *testing.T) {
|
||||
app := New()
|
||||
app.Use("/", func(c *Ctx) {
|
||||
c.Next()
|
||||
})
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Set("X-Next-Result", "Works")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("X-Next-Result") != "Works" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "X-Next-Results: Works")
|
||||
}
|
||||
}
|
||||
func Test_Redirect(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Redirect("http://example.com", 301)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 301 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Location") != "http://example.com" {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), "Location: http://example.com")
|
||||
}
|
||||
}
|
||||
func Test_Render(t *testing.T) {
|
||||
// TODO
|
||||
}
|
||||
func Test_Send(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Send([]byte("Hello, World"))
|
||||
c.Send("Don't crash please")
|
||||
c.Send(1337)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `1337` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `1337`)
|
||||
}
|
||||
}
|
||||
func Test_SendBytes(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.SendBytes([]byte("Hello, World"))
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `Hello, World` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Hello, World`)
|
||||
}
|
||||
}
|
||||
func Test_SendStatus(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.SendStatus(415)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 415 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `Unsupported Media Type` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Unsupported Media Type`)
|
||||
}
|
||||
}
|
||||
func Test_SendString(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.SendString("Don't crash please")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `Don't crash please` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Don't crash please`)
|
||||
}
|
||||
}
|
||||
func Test_Set(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Set("X-1", "1")
|
||||
c.Set("X-2", "2")
|
||||
c.Set("X-3", "3")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("X-1") != "1" {
|
||||
t.Fatalf(`%s: Expected %v`, t.Name(), "X-1: 1")
|
||||
}
|
||||
if resp.Header.Get("X-2") != "2" {
|
||||
t.Fatalf(`%s: Expected %v`, t.Name(), "X-2: 2")
|
||||
}
|
||||
if resp.Header.Get("X-3") != "3" {
|
||||
t.Fatalf(`%s: Expected %v`, t.Name(), "X-3: 3")
|
||||
}
|
||||
}
|
||||
func Test_Status(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Status(400)
|
||||
c.Status(415).Send("Hello, World")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 415 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `Hello, World` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Hello, World`)
|
||||
}
|
||||
}
|
||||
func Test_Type(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Type(".json")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "application/json" {
|
||||
t.Fatalf(`%s: Expected %v`, t.Name(), `Content-Type: application/json`)
|
||||
}
|
||||
}
|
||||
func Test_Vary(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Vary("Origin")
|
||||
c.Vary("User-Agent")
|
||||
c.Vary("Accept-Encoding", "Accept")
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Vary") != "Origin, User-Agent, Accept-Encoding, Accept" {
|
||||
t.Fatalf(`%s: Expected %v`, t.Name(), `Vary: Origin, User-Agent, Accept-Encoding, Accept`)
|
||||
}
|
||||
}
|
||||
func Test_Write(t *testing.T) {
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
c.Write("Hello, ")
|
||||
c.Write([]byte("World! "))
|
||||
c.Write(123)
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `Hello, World! 123` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `Hello, World! 123`)
|
||||
}
|
||||
}
|
||||
func Test_XML(t *testing.T) {
|
||||
type person struct {
|
||||
Name string `xml:"name"`
|
||||
Stars int `xml:"stars"`
|
||||
}
|
||||
app := New()
|
||||
app.Get("/test", func(c *Ctx) {
|
||||
if err := c.Xml(""); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if err := c.XML(""); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if err := c.XML(person{"John", 50}); err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
})
|
||||
req, _ := http.NewRequest("GET", "http://example.com/test", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: %s`, t.Name(), err)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
t.Fatalf(`%s: StatusCode %v`, t.Name(), resp.StatusCode)
|
||||
}
|
||||
if resp.Header.Get("Content-Type") != "application/xml" {
|
||||
t.Fatalf(`%s: Expected %v`, t.Name(), "application/xml")
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf(`%s: Error %s`, t.Name(), err)
|
||||
}
|
||||
if string(body) != `<person><name>John</name><stars>50</stars></person>` {
|
||||
t.Fatalf(`%s: Expecting %s`, t.Name(), `<person><name>John</name><stars>50</stars></person>`)
|
||||
}
|
||||
}
|
481
router.go
481
router.go
@ -1,164 +1,276 @@
|
||||
// 🚀 Fiber is an Express.js inspired web framework written in Go with 💖
|
||||
// 📌 Please open an issue if you got suggestions or found a bug!
|
||||
// 🖥 Links: https://github.com/gofiber/fiber, https://fiber.wiki
|
||||
|
||||
// 🦸 Not all heroes wear capes, thank you to some amazing people
|
||||
// 💖 @valyala, @erikdubbelboer, @savsgio, @julienschmidt, @koddr
|
||||
|
||||
package fiber
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
websocket "github.com/fasthttp/websocket"
|
||||
fasthttp "github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Ctx is the context that contains everything
|
||||
type Ctx struct {
|
||||
route *Route
|
||||
next bool
|
||||
error error
|
||||
params *[]string
|
||||
values []string
|
||||
Fasthttp *fasthttp.RequestCtx
|
||||
}
|
||||
|
||||
// Route struct
|
||||
type Route struct {
|
||||
// HTTP method in uppercase, can be a * for Use() & All()
|
||||
// HTTP method in uppercase, can be a * for "Use" & "All" routes
|
||||
Method string
|
||||
// Stores the original path
|
||||
Path string
|
||||
// Bool that defines if the route is a Use() middleware
|
||||
Midware bool
|
||||
// wildcard bool is for routes without a path, * and /*
|
||||
Wildcard bool
|
||||
// Stores compiled regex special routes :params, *wildcards, optionals?
|
||||
// Prefix is for ending wildcards or middlewares
|
||||
Prefix string
|
||||
// Stores regex for :params & :optionals?
|
||||
Regex *regexp.Regexp
|
||||
// Store params if special routes :params, *wildcards, optionals?
|
||||
// Stores params keys for :params & :optionals?
|
||||
Params []string
|
||||
// Callback function for specific route
|
||||
Handler func(*Ctx)
|
||||
// Callback function for context
|
||||
HandlerCtx func(*Ctx)
|
||||
// Callback function for websockets
|
||||
HandlerConn func(*Conn)
|
||||
}
|
||||
|
||||
// Ctx pool
|
||||
var poolCtx = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(Ctx)
|
||||
},
|
||||
}
|
||||
func (app *App) registerStatic(grpPrefix string, args ...string) {
|
||||
var prefix = "/"
|
||||
var root = "./"
|
||||
// enable / disable gzipping somewhere?
|
||||
// todo v2.0.0
|
||||
gzip := true
|
||||
|
||||
// Get new Ctx from pool
|
||||
func acquireCtx(fctx *fasthttp.RequestCtx) *Ctx {
|
||||
ctx := poolCtx.Get().(*Ctx)
|
||||
ctx.Fasthttp = fctx
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Return Context to pool
|
||||
func releaseCtx(ctx *Ctx) {
|
||||
ctx.route = nil
|
||||
ctx.next = false
|
||||
ctx.error = nil
|
||||
ctx.params = nil
|
||||
ctx.values = nil
|
||||
ctx.Fasthttp = nil
|
||||
poolCtx.Put(ctx)
|
||||
}
|
||||
|
||||
func (grp *Group) register(method string, args ...interface{}) {
|
||||
path := grp.path
|
||||
var handler func(*Ctx)
|
||||
if len(args) == 1 {
|
||||
handler = args[0].(func(*Ctx))
|
||||
} else if len(args) > 1 {
|
||||
path = path + args[0].(string)
|
||||
handler = args[1].(func(*Ctx))
|
||||
if path[0] != '/' && path[0] != '*' {
|
||||
path = "/" + path
|
||||
root = args[0]
|
||||
}
|
||||
if len(args) == 2 {
|
||||
prefix = args[0]
|
||||
root = args[1]
|
||||
}
|
||||
|
||||
// A non wildcard path must start with a '/'
|
||||
if prefix != "*" && len(prefix) > 0 && prefix[0] != '/' {
|
||||
prefix = "/" + prefix
|
||||
}
|
||||
// Prepend group prefix
|
||||
if len(grpPrefix) > 0 {
|
||||
// `/v1`+`/` => `/v1`+``
|
||||
if prefix == "/" {
|
||||
prefix = grpPrefix
|
||||
} else {
|
||||
prefix = grpPrefix + prefix
|
||||
}
|
||||
path = strings.Replace(path, "//", "/", -1)
|
||||
path = filepath.Clean(path)
|
||||
// Remove duplicate slashes `//`
|
||||
prefix = strings.Replace(prefix, "//", "/", -1)
|
||||
}
|
||||
// Empty or '/*' path equals "match anything"
|
||||
// TODO fix * for paths with grpprefix
|
||||
if prefix == "/*" {
|
||||
prefix = "*"
|
||||
}
|
||||
// Lets get all files from root
|
||||
files, _, err := getFiles(root)
|
||||
if err != nil {
|
||||
log.Fatal("Static: ", err)
|
||||
}
|
||||
// ./static/compiled => static/compiled
|
||||
mount := filepath.Clean(root)
|
||||
|
||||
if !app.Settings.CaseSensitive {
|
||||
prefix = strings.ToLower(prefix)
|
||||
}
|
||||
if !app.Settings.StrictRouting && len(prefix) > 1 {
|
||||
prefix = strings.TrimRight(prefix, "/")
|
||||
}
|
||||
|
||||
// Loop over all files
|
||||
for _, file := range files {
|
||||
// Ignore the .gzipped files by fasthttp
|
||||
if strings.Contains(file, ".fasthttp.gz") {
|
||||
continue
|
||||
}
|
||||
// Time to create a fake path for the route match
|
||||
// static/index.html => /index.html
|
||||
path := filepath.Join(prefix, strings.Replace(file, mount, "", 1))
|
||||
// for windows: static\index.html => /index.html
|
||||
path = filepath.ToSlash(path)
|
||||
// Store file path to use in ctx handler
|
||||
filePath := file
|
||||
|
||||
if len(prefix) > 1 && strings.Contains(prefix, "*") {
|
||||
app.routes = append(app.routes, &Route{
|
||||
Method: "GET",
|
||||
Path: path,
|
||||
Prefix: strings.Split(prefix, "*")[0],
|
||||
HandlerCtx: func(c *Ctx) {
|
||||
c.SendFile(filePath, gzip)
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
// If the file is an index.html, bind the prefix to index.html directly
|
||||
if filepath.Base(filePath) == "index.html" || filepath.Base(filePath) == "index.htm" {
|
||||
app.routes = append(app.routes, &Route{
|
||||
Method: "GET",
|
||||
Path: prefix,
|
||||
HandlerCtx: func(c *Ctx) {
|
||||
c.SendFile(filePath, gzip)
|
||||
},
|
||||
})
|
||||
}
|
||||
if !app.Settings.CaseSensitive {
|
||||
path = strings.ToLower(path)
|
||||
}
|
||||
if !app.Settings.StrictRouting && len(prefix) > 1 {
|
||||
path = strings.TrimRight(path, "/")
|
||||
}
|
||||
// Add the route + SendFile(filepath) to routes
|
||||
app.routes = append(app.routes, &Route{
|
||||
Method: "GET",
|
||||
Path: path,
|
||||
HandlerCtx: func(c *Ctx) {
|
||||
c.SendFile(filePath, gzip)
|
||||
},
|
||||
})
|
||||
}
|
||||
grp.app.register(method, path, handler)
|
||||
}
|
||||
|
||||
// Function to add a route correctly
|
||||
func (app *Application) register(method string, args ...interface{}) {
|
||||
// Set if method is Use() midware
|
||||
var midware = method == "USE"
|
||||
|
||||
// Match any method
|
||||
if method == "ALL" || midware {
|
||||
method = "*"
|
||||
}
|
||||
|
||||
// Prepare possible variables
|
||||
var path string // We could have a path/prefix
|
||||
var handler func(*Ctx) // We could have a ctx handler
|
||||
|
||||
// Only 1 argument, so no path/prefix
|
||||
if len(args) == 1 {
|
||||
handler = args[0].(func(*Ctx))
|
||||
} else if len(args) > 1 {
|
||||
path = args[0].(string)
|
||||
handler = args[1].(func(*Ctx))
|
||||
if path == "" || path[0] != '/' && path[0] != '*' {
|
||||
path = "/" + path
|
||||
func (app *App) register(method, grpPrefix string, args ...interface{}) {
|
||||
// Set variables
|
||||
var path = "*"
|
||||
var prefix string
|
||||
var middleware = method == "USE"
|
||||
var handlersCtx []func(*Ctx)
|
||||
var handlersConn []func(*Conn)
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch arg := args[i].(type) {
|
||||
case string:
|
||||
path = arg
|
||||
case func(*Ctx):
|
||||
handlersCtx = append(handlersCtx, arg)
|
||||
case func(*Conn):
|
||||
handlersConn = append(handlersConn, arg)
|
||||
default:
|
||||
log.Fatalf("Invalid argument type: %v", reflect.TypeOf(arg))
|
||||
}
|
||||
}
|
||||
|
||||
if midware && strings.Contains(path, "/:") {
|
||||
log.Fatal("Router: You cannot use :params in Use()")
|
||||
// A non wildcard path must start with a '/'
|
||||
if path != "*" && len(path) > 0 && path[0] != '/' {
|
||||
path = "/" + path
|
||||
}
|
||||
|
||||
// If Use() path == "/", match anything aka *
|
||||
if midware && path == "/" {
|
||||
// Prepend group prefix
|
||||
if len(grpPrefix) > 0 {
|
||||
// `/v1`+`/` => `/v1`+``
|
||||
if path == "/" {
|
||||
path = grpPrefix
|
||||
} else {
|
||||
path = grpPrefix + path
|
||||
}
|
||||
// Remove duplicate slashes `//`
|
||||
path = strings.Replace(path, "//", "/", -1)
|
||||
}
|
||||
// Empty or '/*' path equals "match anything"
|
||||
// TODO fix * for paths with grpprefix
|
||||
if path == "" || path == "/*" {
|
||||
path = "*"
|
||||
}
|
||||
|
||||
// If the route needs to match any path
|
||||
if path == "" || path == "*" || path == "/*" {
|
||||
app.routes = append(app.routes, &Route{method, path, midware, true, nil, nil, handler})
|
||||
if method == "ALL" || middleware {
|
||||
method = "*"
|
||||
}
|
||||
// Routes are case insensitive by default
|
||||
if !app.Settings.CaseSensitive {
|
||||
path = strings.ToLower(path)
|
||||
}
|
||||
if !app.Settings.StrictRouting && len(path) > 1 {
|
||||
path = strings.TrimRight(path, "/")
|
||||
}
|
||||
// If the route can match anything
|
||||
if path == "*" {
|
||||
for i := range handlersCtx {
|
||||
app.routes = append(app.routes, &Route{
|
||||
Method: method, Path: path, HandlerCtx: handlersCtx[i],
|
||||
})
|
||||
}
|
||||
for i := range handlersConn {
|
||||
app.routes = append(app.routes, &Route{
|
||||
Method: method, Path: path, HandlerConn: handlersConn[i],
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get params from path
|
||||
// Get ':param' & ':optional?' & '*' from path
|
||||
params := getParams(path)
|
||||
// Enable prefix for midware
|
||||
if len(params) == 0 && middleware {
|
||||
prefix = path
|
||||
}
|
||||
|
||||
// If path has no params (simple path), we don't need regex (also for use())
|
||||
if midware || len(params) == 0 {
|
||||
app.routes = append(app.routes, &Route{method, path, midware, false, nil, nil, handler})
|
||||
// If path has no params (simple path)
|
||||
if len(params) == 0 {
|
||||
for i := range handlersCtx {
|
||||
app.routes = append(app.routes, &Route{
|
||||
Method: method, Path: path, Prefix: prefix, HandlerCtx: handlersCtx[i],
|
||||
})
|
||||
}
|
||||
for i := range handlersConn {
|
||||
app.routes = append(app.routes, &Route{
|
||||
Method: method, Path: path, Prefix: prefix, HandlerConn: handlersConn[i],
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// We have parametes, so we need to compile regex from the path
|
||||
// If path only contains 1 wildcard, we can create a prefix
|
||||
// If its a middleware, we also create a prefix
|
||||
if len(params) == 1 && params[0] == "*" {
|
||||
prefix = strings.Split(path, "*")[0]
|
||||
for i := range handlersCtx {
|
||||
app.routes = append(app.routes, &Route{
|
||||
Method: method, Path: path, Prefix: prefix,
|
||||
Params: params, HandlerCtx: handlersCtx[i],
|
||||
})
|
||||
}
|
||||
for i := range handlersConn {
|
||||
app.routes = append(app.routes, &Route{
|
||||
Method: method, Path: path, Prefix: prefix,
|
||||
Params: params, HandlerConn: handlersConn[i],
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
// We have an :param or :optional? and need to compile a regex struct
|
||||
regex, err := getRegex(path)
|
||||
if err != nil {
|
||||
log.Fatal("Router: Invalid url pattern: " + path)
|
||||
log.Fatal("Router: Invalid path pattern: " + path)
|
||||
}
|
||||
// Add route with regex
|
||||
for i := range handlersCtx {
|
||||
app.routes = append(app.routes, &Route{
|
||||
Method: method, Path: path, Regex: regex,
|
||||
Params: params, HandlerCtx: handlersCtx[i],
|
||||
})
|
||||
}
|
||||
for i := range handlersConn {
|
||||
app.routes = append(app.routes, &Route{
|
||||
Method: method, Path: path, Regex: regex,
|
||||
Params: params, HandlerConn: handlersConn[i],
|
||||
})
|
||||
}
|
||||
|
||||
// Add regex + params to route
|
||||
app.routes = append(app.routes, &Route{method, path, midware, false, regex, params, handler})
|
||||
}
|
||||
|
||||
// then try to match a route as efficient as possible.
|
||||
func (app *Application) handler(fctx *fasthttp.RequestCtx) {
|
||||
found := false
|
||||
|
||||
func (app *App) handler(fctx *fasthttp.RequestCtx) {
|
||||
// Use this boolean to perform 404 not found at the end
|
||||
var match = false
|
||||
// get custom context from sync pool
|
||||
ctx := acquireCtx(fctx)
|
||||
|
||||
// get path and method from main context
|
||||
if ctx.app == nil {
|
||||
ctx.app = app
|
||||
}
|
||||
// get path and method
|
||||
path := ctx.Path()
|
||||
if !app.Settings.CaseSensitive {
|
||||
path = strings.ToLower(path)
|
||||
}
|
||||
if !app.Settings.StrictRouting && len(path) > 1 {
|
||||
path = strings.TrimRight(path, "/")
|
||||
}
|
||||
method := ctx.Method()
|
||||
|
||||
// enable recovery
|
||||
if app.recover != nil {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@ -167,48 +279,101 @@ func (app *Application) handler(fctx *fasthttp.RequestCtx) {
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// loop trough routes
|
||||
for _, route := range app.routes {
|
||||
// Skip route if method is not allowed
|
||||
// Skip route if method does not match
|
||||
if route.Method != "*" && route.Method != method {
|
||||
continue
|
||||
}
|
||||
|
||||
// First check if we match a wildcard or static path
|
||||
if route.Wildcard || route.Path == path {
|
||||
// if route.wildcard || (route.path == path && route.params == nil) {
|
||||
// If * always set the path to the wildcard parameter
|
||||
if route.Wildcard {
|
||||
// Set route pointer if user wants to call .Route()
|
||||
ctx.route = route
|
||||
// wilcard or exact same path
|
||||
// TODO v2: enable or disable case insensitive match
|
||||
if route.Path == "*" || route.Path == path {
|
||||
// if * always set the path to the wildcard parameter
|
||||
if route.Path == "*" {
|
||||
ctx.params = &[]string{"*"}
|
||||
ctx.values = make([]string, 1)
|
||||
ctx.values[0] = path
|
||||
ctx.values = []string{path}
|
||||
}
|
||||
found = true
|
||||
// Set route pointer if user wants to call .Route()
|
||||
ctx.route = route
|
||||
// Execute handler with context
|
||||
route.Handler(ctx)
|
||||
// ctx.Fasthttp.Request.Header.ConnectionUpgrade()
|
||||
// Websocket request
|
||||
if route.HandlerConn != nil && websocket.FastHTTPIsWebSocketUpgrade(fctx) {
|
||||
// Try to upgrade
|
||||
err := socketUpgrade.Upgrade(ctx.Fasthttp, func(fconn *websocket.Conn) {
|
||||
conn := acquireConn(fconn)
|
||||
defer releaseConn(conn)
|
||||
conn.params = ctx.params
|
||||
conn.values = ctx.values
|
||||
releaseCtx(ctx)
|
||||
route.HandlerConn(conn)
|
||||
})
|
||||
// Upgrading failed
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
// No handler for HTTP nor websocket
|
||||
if route.HandlerCtx == nil {
|
||||
continue
|
||||
}
|
||||
// Match found, 404 not needed
|
||||
match = true
|
||||
route.HandlerCtx(ctx)
|
||||
// if next is not set, leave loop and release ctx
|
||||
if !ctx.next {
|
||||
break
|
||||
} else {
|
||||
// reset match to false
|
||||
match = false
|
||||
}
|
||||
// set next to false for next iteration
|
||||
ctx.next = false
|
||||
// continue to go to the next route
|
||||
continue
|
||||
}
|
||||
|
||||
// If route is Use() and path starts with route.path
|
||||
// aka strings.HasPrefix(route.path, path)
|
||||
if route.Midware && strings.HasPrefix(path, route.Path) {
|
||||
found = true
|
||||
if route.Prefix != "" && strings.HasPrefix(path, route.Prefix) {
|
||||
ctx.route = route
|
||||
route.Handler(ctx)
|
||||
if strings.Contains(route.Path, "*") {
|
||||
ctx.params = &[]string{"*"}
|
||||
// ctx.values = matches[0][1:len(matches[0])]
|
||||
// parse query source
|
||||
ctx.values = []string{strings.Replace(path, route.Prefix, "", 1)}
|
||||
}
|
||||
// Websocket request
|
||||
if route.HandlerConn != nil {
|
||||
// Try to upgrade
|
||||
err := socketUpgrade.Upgrade(ctx.Fasthttp, func(fconn *websocket.Conn) {
|
||||
conn := acquireConn(fconn)
|
||||
defer releaseConn(conn)
|
||||
conn.params = ctx.params
|
||||
conn.values = ctx.values
|
||||
releaseCtx(ctx)
|
||||
route.HandlerConn(conn)
|
||||
})
|
||||
// Upgrading failed
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
// No handler for HTTP nor websocket
|
||||
if route.HandlerCtx == nil {
|
||||
continue
|
||||
}
|
||||
// Match found, 404 not needed
|
||||
match = true
|
||||
route.HandlerCtx(ctx)
|
||||
// if next is not set, leave loop and release ctx
|
||||
if !ctx.next {
|
||||
break
|
||||
} else {
|
||||
// reset match to false
|
||||
match = false
|
||||
}
|
||||
// set next to false for next iteration
|
||||
ctx.next = false
|
||||
// continue to go to the next route
|
||||
continue
|
||||
}
|
||||
|
||||
@ -228,34 +393,46 @@ func (app *Application) handler(fctx *fasthttp.RequestCtx) {
|
||||
// If we have matches, add params and values to context
|
||||
if len(matches) > 0 && len(matches[0]) > 1 {
|
||||
ctx.params = &route.Params
|
||||
// ctx.values = make([]string, len(*ctx.params))
|
||||
ctx.values = matches[0][1:len(matches[0])]
|
||||
}
|
||||
}
|
||||
|
||||
found = true
|
||||
|
||||
// Set route pointer if user wants to call .Route()
|
||||
ctx.route = route
|
||||
|
||||
// Execute handler with context
|
||||
route.Handler(ctx)
|
||||
|
||||
// Websocket route
|
||||
if route.HandlerConn != nil {
|
||||
// Try to upgrade
|
||||
err := socketUpgrade.Upgrade(ctx.Fasthttp, func(fconn *websocket.Conn) {
|
||||
conn := acquireConn(fconn)
|
||||
conn.params = ctx.params
|
||||
conn.values = ctx.values
|
||||
releaseCtx(ctx)
|
||||
defer releaseConn(conn)
|
||||
route.HandlerConn(conn)
|
||||
})
|
||||
// Upgrading failed
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
// No handler for HTTP nor websocket
|
||||
if route.HandlerCtx == nil {
|
||||
continue
|
||||
}
|
||||
// Match found, 404 not needed
|
||||
match = true
|
||||
route.HandlerCtx(ctx)
|
||||
// if next is not set, leave loop and release ctx
|
||||
if !ctx.next {
|
||||
break
|
||||
} else {
|
||||
// reset match to false
|
||||
match = false
|
||||
}
|
||||
|
||||
// set next to false for next iteration
|
||||
ctx.next = false
|
||||
}
|
||||
|
||||
// No routes found
|
||||
if !found {
|
||||
// Custom 404 handler?
|
||||
// No match, send default 404
|
||||
if !match {
|
||||
ctx.SendStatus(404)
|
||||
}
|
||||
|
||||
// release context back into sync pool
|
||||
releaseCtx(ctx)
|
||||
}
|
||||
|
@ -1,10 +0,0 @@
|
||||
package fiber
|
||||
|
||||
import "testing"
|
||||
|
||||
func BenchmarkFib10(b *testing.B) {
|
||||
// run the Fib function b.N times
|
||||
// for n := 0; n < b.N; n++ {
|
||||
// Fib(10)
|
||||
// }
|
||||
}
|
113
utils.go
113
utils.go
@ -1,10 +1,3 @@
|
||||
// 🚀 Fiber is an Express.js inspired web framework written in Go with 💖
|
||||
// 📌 Please open an issue if you got suggestions or found a bug!
|
||||
// 🖥 Links: https://github.com/gofiber/fiber, https://fiber.wiki
|
||||
|
||||
// 🦸 Not all heroes wear capes, thank you to some amazing people
|
||||
// 💖 @valyala, @erikdubbelboer, @savsgio, @julienschmidt, @koddr
|
||||
|
||||
package fiber
|
||||
|
||||
import (
|
||||
@ -13,18 +6,44 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
websocket "github.com/fasthttp/websocket"
|
||||
schema "github.com/gorilla/schema"
|
||||
fasthttp "github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
var schemaDecoder = schema.NewDecoder()
|
||||
var socketUpgrade = websocket.FastHTTPUpgrader{
|
||||
ReadBufferSize: 1024,
|
||||
WriteBufferSize: 1024,
|
||||
CheckOrigin: func(fctx *fasthttp.RequestCtx) bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
// MIME types
|
||||
const (
|
||||
MIMEApplicationJSON = "application/json"
|
||||
MIMEApplicationJavaScript = "application/javascript"
|
||||
MIMEApplicationXML = "application/xml"
|
||||
MIMETextXML = "text/xml"
|
||||
MIMEApplicationForm = "application/x-www-form-urlencoded"
|
||||
MIMEApplicationProtobuf = "application/protobuf"
|
||||
MIMEApplicationMsgpack = "application/msgpack"
|
||||
MIMETextHTML = "text/html"
|
||||
MIMETextPlain = "text/plain"
|
||||
MIMEMultipartForm = "multipart/form-data"
|
||||
MIMEOctetStream = "application/octet-stream"
|
||||
)
|
||||
|
||||
func getParams(path string) (params []string) {
|
||||
if len(path) < 1 {
|
||||
return
|
||||
}
|
||||
segments := strings.Split(path, "/")
|
||||
replacer := strings.NewReplacer(":", "", "?", "")
|
||||
for _, s := range segments {
|
||||
@ -32,11 +51,12 @@ func getParams(path string) (params []string) {
|
||||
continue
|
||||
} else if s[0] == ':' {
|
||||
params = append(params, replacer.Replace(s))
|
||||
} else if s[0] == '*' {
|
||||
}
|
||||
if strings.Contains(s, "*") {
|
||||
params = append(params, "*")
|
||||
}
|
||||
}
|
||||
return params
|
||||
return
|
||||
}
|
||||
|
||||
func getRegex(path string) (*regexp.Regexp, error) {
|
||||
@ -63,21 +83,20 @@ func getRegex(path string) (*regexp.Regexp, error) {
|
||||
return regex, err
|
||||
}
|
||||
|
||||
func getFiles(root string) (files []string, isDir bool, err error) {
|
||||
func getFiles(root string) (files []string, dir bool, err error) {
|
||||
root = filepath.Clean(root)
|
||||
// Check if dir/file exists
|
||||
if _, err := os.Lstat(root); err != nil {
|
||||
return files, isDir, fmt.Errorf("%s", err)
|
||||
return files, dir, fmt.Errorf("%s", err)
|
||||
}
|
||||
err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() {
|
||||
files = append(files, path)
|
||||
} else {
|
||||
isDir = true
|
||||
dir = true
|
||||
}
|
||||
return err
|
||||
})
|
||||
return files, isDir, err
|
||||
return
|
||||
}
|
||||
|
||||
func getType(ext string) (mime string) {
|
||||
@ -85,17 +104,18 @@ func getType(ext string) (mime string) {
|
||||
return mime
|
||||
}
|
||||
if ext[0] == '.' {
|
||||
ext = ext[1:]
|
||||
mime = extensionMIME[ext[1:]]
|
||||
} else {
|
||||
mime = extensionMIME[ext]
|
||||
}
|
||||
mime = mimeTypes[ext]
|
||||
if mime == "" {
|
||||
return mimeApplicationOctetStream
|
||||
return MIMEOctetStream
|
||||
}
|
||||
return mime
|
||||
}
|
||||
|
||||
func getStatus(status int) (msg string) {
|
||||
return statusMessages[status]
|
||||
return statusMessage[status]
|
||||
}
|
||||
|
||||
// #nosec G103
|
||||
@ -109,47 +129,32 @@ func getString(b []byte) string {
|
||||
// getBytes converts string to a byte slice without memory allocation.
|
||||
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
|
||||
func getBytes(s string) (b []byte) {
|
||||
// return *(*[]byte)(unsafe.Pointer(&s))
|
||||
sh := *(*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
||||
bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
|
||||
return b
|
||||
return *(*[]byte)(unsafe.Pointer(&s))
|
||||
}
|
||||
|
||||
// Check for error and format
|
||||
// func checkErr(err error, title ...string) {
|
||||
// if err != nil {
|
||||
// t := "Error"
|
||||
// if len(title) > 0 {
|
||||
// t = title[0]
|
||||
// }
|
||||
// fmt.Printf("\n%s%s: %v%s\n\n", "\x1b[1;30m", t, err, "\x1b[0m")
|
||||
// os.Exit(1)
|
||||
// }
|
||||
// }
|
||||
|
||||
// https://golang.org/src/net/net.go#L113
|
||||
// Helper methods for Testing
|
||||
type conn struct {
|
||||
// Helper methods for application#test
|
||||
type testConn struct {
|
||||
net.Conn
|
||||
r bytes.Buffer
|
||||
w bytes.Buffer
|
||||
}
|
||||
|
||||
func (c *conn) RemoteAddr() net.Addr {
|
||||
func (c *testConn) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{
|
||||
IP: net.IPv4(0, 0, 0, 0),
|
||||
}
|
||||
}
|
||||
func (c *conn) LocalAddr() net.Addr { return c.LocalAddr() }
|
||||
func (c *conn) Read(b []byte) (int, error) { return c.r.Read(b) }
|
||||
func (c *conn) Write(b []byte) (int, error) { return c.w.Write(b) }
|
||||
func (c *conn) Close() error { return nil }
|
||||
func (c *conn) SetDeadline(t time.Time) error { return nil }
|
||||
func (c *conn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (c *conn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
func (c *testConn) LocalAddr() net.Addr { return c.RemoteAddr() }
|
||||
func (c *testConn) Read(b []byte) (int, error) { return c.r.Read(b) }
|
||||
func (c *testConn) Write(b []byte) (int, error) { return c.w.Write(b) }
|
||||
func (c *testConn) Close() error { return nil }
|
||||
func (c *testConn) SetDeadline(t time.Time) error { return nil }
|
||||
func (c *testConn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (c *testConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
||||
var statusMessages = map[int]string{
|
||||
// HTTP status codes
|
||||
var statusMessage = map[int]string{
|
||||
100: "Continue",
|
||||
101: "Switching Protocols",
|
||||
102: "Processing",
|
||||
@ -212,18 +217,8 @@ var statusMessages = map[int]string{
|
||||
511: "Network Authentication Required",
|
||||
}
|
||||
|
||||
const (
|
||||
mimeApplicationJSON = "application/json"
|
||||
mimeApplicationJavascript = "application/javascript"
|
||||
mimeApplicationXML = "application/xml"
|
||||
mimeTextXML = "text/xml"
|
||||
mimeApplicationOctetStream = "application/octet-stream"
|
||||
mimeApplicationForm = "application/x-www-form-urlencoded"
|
||||
mimeMultipartForm = "multipart/form-data"
|
||||
)
|
||||
|
||||
// https://github.com/nginx/nginx/blob/master/conf/mime.types
|
||||
var mimeTypes = map[string]string{
|
||||
// MIME types for file extensions
|
||||
var extensionMIME = map[string]string{
|
||||
"html": "text/html",
|
||||
"htm": "text/html",
|
||||
"shtml": "text/html",
|
||||
|
Loading…
x
Reference in New Issue
Block a user