Clean up repository

pull/85/head
Fenny 2020-02-10 01:24:59 +01:00
parent 401b21a0d4
commit 1e7df547ba
17 changed files with 570 additions and 734 deletions

View File

@ -1,7 +1,7 @@
# 🚀 Fiber <a href="https://github.com/gofiber/fiber/blob/master/.github/README_RU.md"><img width="20px" src="https://github.com/gofiber/docs/blob/master/static/flags/ru.svg" alt="ru"/></a> <a href="https://github.com/gofiber/fiber/blob/master/.github/README_CH.md"><img width="20px" src="https://github.com/gofiber/docs/blob/master/static/flags/ch.svg" alt="ch"/></a>
[![](https://img.shields.io/github/release/gofiber/fiber)](https://github.com/gofiber/fiber/releases) ![](https://img.shields.io/github/languages/top/gofiber/fiber) [![](https://godoc.org/github.com/gofiber/fiber?status.svg)](https://godoc.org/github.com/gofiber/fiber) ![](https://goreportcard.com/badge/github.com/gofiber/fiber) [![GitHub license](https://img.shields.io/github/license/gofiber/fiber.svg)](https://github.com/gofiber/fiber/blob/master/LICENSE) [![Join the chat at https://gitter.im/gofiber/community](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gofiber/community) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/gofiber/fiber)
[![](https://img.shields.io/github/release/gofiber/fiber)](https://github.com/gofiber/fiber/releases) ![](https://img.shields.io/github/languages/top/gofiber/fiber) [![](https://godoc.org/github.com/gofiber/fiber?status.svg)](https://godoc.org/github.com/gofiber/fiber) ![](https://goreportcard.com/badge/github.com/gofiber/fiber) [![GitHub license](https://img.shields.io/github/license/gofiber/fiber.svg)](https://github.com/gofiber/fiber/blob/master/LICENSE) [![Join the chat at https://gitter.im/gofiber/community](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gofiber/community) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/gofiber/fiber)
<img align="right" height="180px" src="https://github.com/gofiber/docs/blob/master/static/logo_320px_trans.png" alt="Fiber logo" />

View File

@ -9,9 +9,19 @@ package fiber
import (
"flag"
"fmt"
"log"
"net"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/reuseport"
)
const (
@ -102,3 +112,262 @@ func New() *Fiber {
},
}
}
// Connect establishes a tunnel to the server
// identified by the target resource.
func (r *Fiber) Connect(args ...interface{}) {
r.register("CONNECT", args...)
}
// Put replaces all current representations
// of the target resource with the request payload.
func (r *Fiber) Put(args ...interface{}) {
r.register("PUT", args...)
}
// Post is used to submit an entity to the specified resource,
// often causing a change in state or side effects on the server.
func (r *Fiber) Post(args ...interface{}) {
r.register("POST", args...)
}
// Delete deletes the specified resource.
func (r *Fiber) Delete(args ...interface{}) {
r.register("DELETE", args...)
}
// Head asks for a response identical to that of a GET request,
// but without the response body.
func (r *Fiber) Head(args ...interface{}) {
r.register("HEAD", args...)
}
// Patch is used to apply partial modifications to a resource.
func (r *Fiber) Patch(args ...interface{}) {
r.register("PATCH", args...)
}
// Options is used to describe the communication options
// for the target resource.
func (r *Fiber) Options(args ...interface{}) {
r.register("OPTIONS", args...)
}
// Trace performs a message loop-back test
// along the path to the target resource.
func (r *Fiber) Trace(args ...interface{}) {
r.register("TRACE", args...)
}
// Get requests a representation of the specified resource.
// Requests using GET should only retrieve data.
func (r *Fiber) Get(args ...interface{}) {
r.register("GET", args...)
}
// All matches any HTTP method
func (r *Fiber) All(args ...interface{}) {
r.register("ALL", args...)
}
// Use only matches the starting path
func (r *Fiber) Use(args ...interface{}) {
r.register("MIDWARE", args...)
}
// Static https://fiber.wiki/application#static
func (r *Fiber) Static(args ...string) {
prefix := "/"
root := "./"
wildcard := 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
}
// Check if root exists
if _, err := os.Lstat(root); err != nil {
log.Fatal("Static: ", err)
}
// 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))
// Store original 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" {
r.routes = append(r.routes, &Route{"GET", prefix, wildcard, false, nil, nil, func(c *Ctx) {
c.SendFile(filePath, gzip)
}})
}
// Add the route + SendFile(filepath) to routes
r.routes = append(r.routes, &Route{"GET", path, wildcard, false, nil, nil, func(c *Ctx) {
c.SendFile(filePath, gzip)
}})
}
}
// Listen : https://fiber.wiki/application#listen
func (r *Fiber) 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
r.httpServer = r.setupServer()
// Prefork enabled
if r.Prefork && runtime.NumCPU() > 1 {
if r.Banner && !r.child {
cores := fmt.Sprintf("%s\x1b[1;30m %v cores", host, runtime.NumCPU())
fmt.Printf(banner, Version, " prefork", "Express on steroids", cores)
}
r.prefork(host, tls...)
}
// Prefork disabled
if r.Banner {
fmt.Printf(banner, Version, "", "Express on steroids", host)
}
ln, err := net.Listen("tcp4", host)
if err != nil {
log.Fatal("Listen: ", err)
}
// enable TLS/HTTPS
if len(tls) > 1 {
if err := r.httpServer.ServeTLS(ln, tls[0], tls[1]); err != nil {
log.Fatal("Listen: ", err)
}
}
if err := r.httpServer.Serve(ln); err != nil {
log.Fatal("Listen: ", err)
}
}
// Shutdown server gracefully
func (r *Fiber) Shutdown() error {
if r.httpServer == nil {
return fmt.Errorf("Server is not running")
}
return r.httpServer.Shutdown()
}
// https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/
func (r *Fiber) prefork(host string, tls ...string) {
// Master proc
if !r.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 := r.httpServer.ServeTLS(ln, tls[0], tls[1]); err != nil {
log.Fatal("Listen-prefork: ", err)
}
}
if err := r.httpServer.Serve(ln); err != nil {
log.Fatal("Listen-prefork: ", err)
}
}
func (r *Fiber) setupServer() *fasthttp.Server {
return &fasthttp.Server{
Handler: r.handler,
Name: r.Server,
Concurrency: r.Engine.Concurrency,
DisableKeepalive: r.Engine.DisableKeepAlive,
ReadBufferSize: r.Engine.ReadBufferSize,
WriteBufferSize: r.Engine.WriteBufferSize,
ReadTimeout: r.Engine.ReadTimeout,
WriteTimeout: r.Engine.WriteTimeout,
IdleTimeout: r.Engine.IdleTimeout,
MaxConnsPerIP: r.Engine.MaxConnsPerIP,
MaxRequestsPerConn: r.Engine.MaxRequestsPerConn,
TCPKeepalive: r.Engine.TCPKeepalive,
TCPKeepalivePeriod: r.Engine.TCPKeepalivePeriod,
MaxRequestBodySize: r.Engine.MaxRequestBodySize,
ReduceMemoryUsage: r.Engine.ReduceMemoryUsage,
GetOnly: r.Engine.GetOnly,
DisableHeaderNamesNormalizing: r.Engine.DisableHeaderNamesNormalizing,
SleepWhenConcurrencyLimitsExceeded: r.Engine.SleepWhenConcurrencyLimitsExceeded,
NoDefaultServerHeader: r.Server == "",
NoDefaultContentType: r.Engine.NoDefaultContentType,
KeepHijackedConns: r.Engine.KeepHijackedConns,
}
}

72
application_test.go Normal file
View File

@ -0,0 +1,72 @@
package fiber
import (
"net/http"
"testing"
)
func Test_Methods(t *testing.T) {
app := New()
handler := func(c *Ctx) {}
methods := []string{"CONNECT", "PUT", "POST", "DELETE", "HEAD", "PATCH", "OPTIONS", "TRACE", "GET", "ALL", "USE"}
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)
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()
app.Static("./.github")
app.Static("/john", "./.github")
app.Static("*", "./.github/stale.yml")
req, _ := http.NewRequest("GET", "/stale.yml", 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-Length") == "" {
t.Fatalf(`%s: Missing Content-Length`, t.Name())
}
req, _ = http.NewRequest("GET", "/john/stale.yml", 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-Length") == "" {
t.Fatalf(`%s: Missing Content-Length`, t.Name())
}
}

View File

@ -1,59 +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 (
"sync"
"github.com/valyala/fasthttp"
)
// Ctx : struct
type Ctx struct {
route *Route
next bool
params *[]string
values []string
Fasthttp *fasthttp.RequestCtx
}
// Cookie : struct
type Cookie struct {
Expire int // time.Unix(1578981376, 0)
MaxAge int
Domain string
Path string
HTTPOnly bool
Secure bool
SameSite string
}
// Ctx pool
var poolCtx = sync.Pool{
New: func() interface{} {
return new(Ctx)
},
}
// 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.params = nil
ctx.values = nil
ctx.Fasthttp = nil
poolCtx.Put(ctx)
}

151
listen.go
View File

@ -1,151 +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 (
"fmt"
"log"
"net"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/reuseport"
)
// Shutdown server gracefully
func (r *Fiber) Shutdown() error {
if r.httpServer == nil {
return fmt.Errorf("Server is not running")
}
return r.httpServer.Shutdown()
}
// Listen : https://fiber.wiki/application#listen
func (r *Fiber) 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
r.httpServer = r.setupServer()
// Prefork enabled
if r.Prefork && runtime.NumCPU() > 1 {
if r.Banner && !r.child {
cores := fmt.Sprintf("%s\x1b[1;30m %v cores", host, runtime.NumCPU())
fmt.Printf(banner, Version, " prefork", "Express on steroids", cores)
}
r.prefork(host, tls...)
}
// Prefork disabled
if r.Banner {
fmt.Printf(banner, Version, "", "Express on steroids", host)
}
ln, err := net.Listen("tcp4", host)
if err != nil {
log.Fatal("Listen: ", err)
}
// enable TLS/HTTPS
if len(tls) > 1 {
if err := r.httpServer.ServeTLS(ln, tls[0], tls[1]); err != nil {
log.Fatal("Listen: ", err)
}
}
if err := r.httpServer.Serve(ln); err != nil {
log.Fatal("Listen: ", err)
}
}
// https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/
func (r *Fiber) prefork(host string, tls ...string) {
// Master proc
if !r.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 := r.httpServer.ServeTLS(ln, tls[0], tls[1]); err != nil {
log.Fatal("Listen-prefork: ", err)
}
}
if err := r.httpServer.Serve(ln); err != nil {
log.Fatal("Listen-prefork: ", err)
}
}
func (r *Fiber) setupServer() *fasthttp.Server {
return &fasthttp.Server{
Handler: r.handler,
Name: r.Server,
Concurrency: r.Engine.Concurrency,
DisableKeepalive: r.Engine.DisableKeepAlive,
ReadBufferSize: r.Engine.ReadBufferSize,
WriteBufferSize: r.Engine.WriteBufferSize,
ReadTimeout: r.Engine.ReadTimeout,
WriteTimeout: r.Engine.WriteTimeout,
IdleTimeout: r.Engine.IdleTimeout,
MaxConnsPerIP: r.Engine.MaxConnsPerIP,
MaxRequestsPerConn: r.Engine.MaxRequestsPerConn,
TCPKeepalive: r.Engine.TCPKeepalive,
TCPKeepalivePeriod: r.Engine.TCPKeepalivePeriod,
MaxRequestBodySize: r.Engine.MaxRequestBodySize,
ReduceMemoryUsage: r.Engine.ReduceMemoryUsage,
GetOnly: r.Engine.GetOnly,
DisableHeaderNamesNormalizing: r.Engine.DisableHeaderNamesNormalizing,
SleepWhenConcurrencyLimitsExceeded: r.Engine.SleepWhenConcurrencyLimitsExceeded,
NoDefaultServerHeader: r.Server == "",
NoDefaultContentType: r.Engine.NoDefaultContentType,
KeepHijackedConns: r.Engine.KeepHijackedConns,
}
}

View File

@ -1,30 +0,0 @@
package fiber
import (
"testing"
)
// var wg sync.WaitGroup
func Test_Connect(t *testing.T) {
// app := New()
// app.Banner = false
//
// wg.Add(1)
//
// go func() {
// app.Listen("8080")
// }()
//
// time.Sleep(time.Millisecond * 100)
//
// go func() {
// err := app.Shutdown()
// if err != nil {
// t.Fatalf(`%s: Failed to shutdown server %v`, t.Name(), err)
// }
// wg.Done()
// }()
// wg.Wait()
//app.Listen(":8085")
}

View File

@ -1,70 +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
// Connect establishes a tunnel to the server
// identified by the target resource.
func (r *Fiber) Connect(args ...interface{}) {
r.register("CONNECT", args...)
}
// Put replaces all current representations
// of the target resource with the request payload.
func (r *Fiber) Put(args ...interface{}) {
r.register("PUT", args...)
}
// Post is used to submit an entity to the specified resource,
// often causing a change in state or side effects on the server.
func (r *Fiber) Post(args ...interface{}) {
r.register("POST", args...)
}
// Delete deletes the specified resource.
func (r *Fiber) Delete(args ...interface{}) {
r.register("DELETE", args...)
}
// Head asks for a response identical to that of a GET request,
// but without the response body.
func (r *Fiber) Head(args ...interface{}) {
r.register("HEAD", args...)
}
// Patch is used to apply partial modifications to a resource.
func (r *Fiber) Patch(args ...interface{}) {
r.register("PATCH", args...)
}
// Options is used to describe the communication options
// for the target resource.
func (r *Fiber) Options(args ...interface{}) {
r.register("OPTIONS", args...)
}
// Trace performs a message loop-back test
// along the path to the target resource.
func (r *Fiber) Trace(args ...interface{}) {
r.register("TRACE", args...)
}
// Get requests a representation of the specified resource.
// Requests using GET should only retrieve data.
func (r *Fiber) Get(args ...interface{}) {
r.register("GET", args...)
}
// All matches any HTTP method
func (r *Fiber) All(args ...interface{}) {
r.register("ALL", args...)
}
// Use only matches the starting path
func (r *Fiber) Use(args ...interface{}) {
r.register("MIDWARE", args...)
}

View File

@ -1,109 +0,0 @@
package fiber
import (
"net/http"
"testing"
)
func Test_Methods(t *testing.T) {
app := New()
app.Connect(func(c *Ctx) {})
app.Put(func(c *Ctx) {})
app.Post(func(c *Ctx) {})
app.Delete(func(c *Ctx) {})
app.Head(func(c *Ctx) {})
app.Patch(func(c *Ctx) {})
app.Options(func(c *Ctx) {})
app.Trace(func(c *Ctx) {})
app.Get(func(c *Ctx) {})
app.All("/special", func(c *Ctx) {})
app.Use("/special/john", func(c *Ctx) {})
req, _ := http.NewRequest("CONNECT", "/", 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("PUT", "/", 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("POST", "/", 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("DELETE", "/", 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("HEAD", "/", 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("PATCH", "/", 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("OPTIONS", "/", 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("TRACE", "/", 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", "/", 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", "/special", 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", "/special/john", 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)
}
}

View File

@ -19,6 +19,18 @@ import (
"github.com/valyala/fasthttp"
)
// 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 {
@ -224,7 +236,7 @@ func (ctx *Ctx) JsonString(raw string) {
ctx.JSONString(raw)
}
// JSONString : https://fiber.wiki/context#jsonstring
// JSONString : https://fiber.wiki/context#json
func (ctx *Ctx) JSONString(raw string) {
ctx.Fasthttp.Response.Header.SetContentType(contentTypeJSON)
ctx.Fasthttp.Response.SetBodyString(raw)

View File

@ -11,10 +11,20 @@ import (
"log"
"regexp"
"strings"
"sync"
"github.com/valyala/fasthttp"
)
// Ctx : struct
type Ctx struct {
route *Route
next bool
params *[]string
values []string
Fasthttp *fasthttp.RequestCtx
}
// Route : struct
type Route struct {
// HTTP method in uppercase, can be a * for Use() & All()
@ -33,6 +43,30 @@ type Route struct {
Handler func(*Ctx)
}
// Ctx pool
var poolCtx = sync.Pool{
New: func() interface{} {
return new(Ctx)
},
}
// 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.params = nil
ctx.values = nil
ctx.Fasthttp = nil
poolCtx.Put(ctx)
}
// Function to add a route correctly
func (r *Fiber) register(method string, args ...interface{}) {
// Set if method is Use() midware

1
router_test.go Normal file
View File

@ -0,0 +1 @@
package fiber

View File

@ -1,83 +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 (
"log"
"os"
"path/filepath"
"strings"
)
// Static https://fiber.wiki/application#static
func (r *Fiber) Static(args ...string) {
prefix := "/"
root := "./"
wildcard := 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
}
// Check if root exists
if _, err := os.Lstat(root); err != nil {
log.Fatal("Static: ", err)
}
// 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))
// Store original 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" {
r.routes = append(r.routes, &Route{"GET", prefix, wildcard, false, nil, nil, func(c *Ctx) {
c.SendFile(filePath, gzip)
}})
}
// Add the route + SendFile(filepath) to routes
r.routes = append(r.routes, &Route{"GET", path, wildcard, false, nil, nil, func(c *Ctx) {
c.SendFile(filePath, gzip)
}})
}
}

View File

@ -1,35 +0,0 @@
package fiber
import (
"net/http"
"testing"
)
func Test_Static(t *testing.T) {
app := New()
app.Static("./.github")
app.Static("/john", "./.github")
app.Static("*", "./.github/stale.yml")
req, _ := http.NewRequest("GET", "/stale.yml", 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-Length") == "" {
t.Fatalf(`%s: Missing Content-Length`, t.Name())
}
req, _ = http.NewRequest("GET", "/john/stale.yml", 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-Length") == "" {
t.Fatalf(`%s: Missing Content-Length`, t.Name())
}
}

View File

@ -1,71 +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
var statusMessages = map[int]string{
100: "Continue",
101: "Switching Protocols",
102: "Processing",
200: "OK",
201: "Created",
202: "Accepted",
203: "Non-Authoritative Information",
204: "No Content",
205: "Reset Content",
206: "Partial Content",
207: "Multi-Status",
208: "Already Reported",
226: "IM Used",
300: "Multiple Choices",
301: "Moved Permanently",
302: "Found",
303: "See Other",
304: "Not Modified",
305: "Use Proxy",
306: "Switch Proxy",
307: "Temporary Redirect",
308: "Permanent Redirect",
400: "Bad Request",
401: "Unauthorized",
402: "Payment Required",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
406: "Not Acceptable",
407: "Proxy Authentication Required",
408: "Request Timeout",
409: "Conflict",
410: "Gone",
411: "Length Required",
412: "Precondition Failed",
413: "Request Entity Too Large",
414: "Request URI Too Long",
415: "Unsupported Media Type",
416: "Requested Range Not Satisfiable",
417: "Expectation Failed",
418: "I'm a teapot",
422: "Unprocessable Entity",
423: "Locked",
424: "Failed Dependency",
426: "Upgrade Required",
428: "Precondition Required",
429: "Too Many Requests",
431: "Request Header Fields Too Large",
451: "Unavailable For Legal Reasons",
500: "Internal Server Error",
501: "Not Implemented",
502: "Bad Gateway",
503: "Service Unavailable",
504: "Gateway Timeout",
505: "HTTP Version Not Supported",
506: "Variant Also Negotiates",
507: "Insufficient Storage",
508: "Loop Detected",
510: "Not Extended",
511: "Network Authentication Required",
}

124
types.go
View File

@ -1,124 +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
const (
contentTypeJSON = "application/json"
contentTypeJs = "application/javascript"
contentTypeXML = "application/xml"
contentTypeOctetStream = "application/octet-stream"
)
// https://github.com/nginx/nginx/blob/master/conf/mime.types
var contentTypes = map[string]string{
"html": "text/html",
"htm": "text/html",
"shtml": "text/html",
"css": "text/css",
"xml": "text/xml",
"gif": "image/gif",
"jpeg": "image/jpeg",
"jpg": "image/jpeg",
"js": "application/javascript",
"atom": "application/atom+xml",
"rss": "application/rss+xml",
"mml": "text/mathml",
"txt": "text/plain",
"jad": "text/vnd.sun.j2me.app-descriptor",
"wml": "text/vnd.wap.wml",
"htc": "text/x-component",
"png": "image/png",
"svg": "image/svg+xml",
"svgz": "image/svg+xml",
"tif": "image/tiff",
"tiff": "image/tiff",
"wbmp": "image/vnd.wap.wbmp",
"webp": "image/webp",
"ico": "image/x-icon",
"jng": "image/x-jng",
"bmp": "image/x-ms-bmp",
"woff": "font/woff",
"woff2": "font/woff2",
"jar": "application/java-archive",
"war": "application/java-archive",
"ear": "application/java-archive",
"json": "application/json",
"hqx": "application/mac-binhex40",
"doc": "application/msword",
"pdf": "application/pdf",
"ps": "application/postscript",
"eps": "application/postscript",
"ai": "application/postscript",
"rtf": "application/rtf",
"m3u8": "application/vnd.apple.mpegurl",
"kml": "application/vnd.google-earth.kml+xml",
"kmz": "application/vnd.google-earth.kmz",
"xls": "application/vnd.ms-excel",
"eot": "application/vnd.ms-fontobject",
"ppt": "application/vnd.ms-powerpoint",
"odg": "application/vnd.oasis.opendocument.graphics",
"odp": "application/vnd.oasis.opendocument.presentation",
"ods": "application/vnd.oasis.opendocument.spreadsheet",
"odt": "application/vnd.oasis.opendocument.text",
"wmlc": "application/vnd.wap.wmlc",
"7z": "application/x-7z-compressed",
"cco": "application/x-cocoa",
"jardiff": "application/x-java-archive-diff",
"jnlp": "application/x-java-jnlp-file",
"run": "application/x-makeself",
"pl": "application/x-perl",
"pm": "application/x-perl",
"prc": "application/x-pilot",
"pdb": "application/x-pilot",
"rar": "application/x-rar-compressed",
"rpm": "application/x-redhat-package-manager",
"sea": "application/x-sea",
"swf": "application/x-shockwave-flash",
"sit": "application/x-stuffit",
"tcl": "application/x-tcl",
"tk": "application/x-tcl",
"der": "application/x-x509-ca-cert",
"pem": "application/x-x509-ca-cert",
"crt": "application/x-x509-ca-cert",
"xpi": "application/x-xpinstall",
"xhtml": "application/xhtml+xml",
"xspf": "application/xspf+xml",
"zip": "application/zip",
"bin": "application/octet-stream",
"exe": "application/octet-stream",
"dll": "application/octet-stream",
"deb": "application/octet-stream",
"dmg": "application/octet-stream",
"iso": "application/octet-stream",
"img": "application/octet-stream",
"msi": "application/octet-stream",
"msp": "application/octet-stream",
"msm": "application/octet-stream",
"mid": "audio/midi",
"midi": "audio/midi",
"kar": "audio/midi",
"mp3": "audio/mpeg",
"ogg": "audio/ogg",
"m4a": "audio/x-m4a",
"ra": "audio/x-realaudio",
"3gpp": "video/3gpp",
"3gp": "video/3gpp",
"ts": "video/mp2t",
"mp4": "video/mp4",
"mpeg": "video/mpeg",
"mpg": "video/mpeg",
"mov": "video/quicktime",
"webm": "video/webm",
"flv": "video/x-flv",
"m4v": "video/x-m4v",
"mng": "video/x-mng",
"asx": "video/x-ms-asf",
"asf": "video/x-ms-asf",
"wmv": "video/x-ms-wmv",
"avi": "video/x-msvideo",
}

179
utils.go
View File

@ -179,3 +179,182 @@ 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 }
var statusMessages = map[int]string{
100: "Continue",
101: "Switching Protocols",
102: "Processing",
200: "OK",
201: "Created",
202: "Accepted",
203: "Non-Authoritative Information",
204: "No Content",
205: "Reset Content",
206: "Partial Content",
207: "Multi-Status",
208: "Already Reported",
226: "IM Used",
300: "Multiple Choices",
301: "Moved Permanently",
302: "Found",
303: "See Other",
304: "Not Modified",
305: "Use Proxy",
306: "Switch Proxy",
307: "Temporary Redirect",
308: "Permanent Redirect",
400: "Bad Request",
401: "Unauthorized",
402: "Payment Required",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
406: "Not Acceptable",
407: "Proxy Authentication Required",
408: "Request Timeout",
409: "Conflict",
410: "Gone",
411: "Length Required",
412: "Precondition Failed",
413: "Request Entity Too Large",
414: "Request URI Too Long",
415: "Unsupported Media Type",
416: "Requested Range Not Satisfiable",
417: "Expectation Failed",
418: "I'm a teapot",
422: "Unprocessable Entity",
423: "Locked",
424: "Failed Dependency",
426: "Upgrade Required",
428: "Precondition Required",
429: "Too Many Requests",
431: "Request Header Fields Too Large",
451: "Unavailable For Legal Reasons",
500: "Internal Server Error",
501: "Not Implemented",
502: "Bad Gateway",
503: "Service Unavailable",
504: "Gateway Timeout",
505: "HTTP Version Not Supported",
506: "Variant Also Negotiates",
507: "Insufficient Storage",
508: "Loop Detected",
510: "Not Extended",
511: "Network Authentication Required",
}
const (
contentTypeJSON = "application/json"
contentTypeJs = "application/javascript"
contentTypeXML = "application/xml"
contentTypeOctetStream = "application/octet-stream"
)
// https://github.com/nginx/nginx/blob/master/conf/mime.types
var contentTypes = map[string]string{
"html": "text/html",
"htm": "text/html",
"shtml": "text/html",
"css": "text/css",
"xml": "text/xml",
"gif": "image/gif",
"jpeg": "image/jpeg",
"jpg": "image/jpeg",
"js": "application/javascript",
"atom": "application/atom+xml",
"rss": "application/rss+xml",
"mml": "text/mathml",
"txt": "text/plain",
"jad": "text/vnd.sun.j2me.app-descriptor",
"wml": "text/vnd.wap.wml",
"htc": "text/x-component",
"png": "image/png",
"svg": "image/svg+xml",
"svgz": "image/svg+xml",
"tif": "image/tiff",
"tiff": "image/tiff",
"wbmp": "image/vnd.wap.wbmp",
"webp": "image/webp",
"ico": "image/x-icon",
"jng": "image/x-jng",
"bmp": "image/x-ms-bmp",
"woff": "font/woff",
"woff2": "font/woff2",
"jar": "application/java-archive",
"war": "application/java-archive",
"ear": "application/java-archive",
"json": "application/json",
"hqx": "application/mac-binhex40",
"doc": "application/msword",
"pdf": "application/pdf",
"ps": "application/postscript",
"eps": "application/postscript",
"ai": "application/postscript",
"rtf": "application/rtf",
"m3u8": "application/vnd.apple.mpegurl",
"kml": "application/vnd.google-earth.kml+xml",
"kmz": "application/vnd.google-earth.kmz",
"xls": "application/vnd.ms-excel",
"eot": "application/vnd.ms-fontobject",
"ppt": "application/vnd.ms-powerpoint",
"odg": "application/vnd.oasis.opendocument.graphics",
"odp": "application/vnd.oasis.opendocument.presentation",
"ods": "application/vnd.oasis.opendocument.spreadsheet",
"odt": "application/vnd.oasis.opendocument.text",
"wmlc": "application/vnd.wap.wmlc",
"7z": "application/x-7z-compressed",
"cco": "application/x-cocoa",
"jardiff": "application/x-java-archive-diff",
"jnlp": "application/x-java-jnlp-file",
"run": "application/x-makeself",
"pl": "application/x-perl",
"pm": "application/x-perl",
"prc": "application/x-pilot",
"pdb": "application/x-pilot",
"rar": "application/x-rar-compressed",
"rpm": "application/x-redhat-package-manager",
"sea": "application/x-sea",
"swf": "application/x-shockwave-flash",
"sit": "application/x-stuffit",
"tcl": "application/x-tcl",
"tk": "application/x-tcl",
"der": "application/x-x509-ca-cert",
"pem": "application/x-x509-ca-cert",
"crt": "application/x-x509-ca-cert",
"xpi": "application/x-xpinstall",
"xhtml": "application/xhtml+xml",
"xspf": "application/xspf+xml",
"zip": "application/zip",
"bin": "application/octet-stream",
"exe": "application/octet-stream",
"dll": "application/octet-stream",
"deb": "application/octet-stream",
"dmg": "application/octet-stream",
"iso": "application/octet-stream",
"img": "application/octet-stream",
"msi": "application/octet-stream",
"msp": "application/octet-stream",
"msm": "application/octet-stream",
"mid": "audio/midi",
"midi": "audio/midi",
"kar": "audio/midi",
"mp3": "audio/mpeg",
"ogg": "audio/ogg",
"m4a": "audio/x-m4a",
"ra": "audio/x-realaudio",
"3gpp": "video/3gpp",
"3gp": "video/3gpp",
"ts": "video/mp2t",
"mp4": "video/mp4",
"mpeg": "video/mpeg",
"mpg": "video/mpeg",
"mov": "video/quicktime",
"webm": "video/webm",
"flv": "video/x-flv",
"m4v": "video/x-m4v",
"mng": "video/x-mng",
"asx": "video/x-ms-asf",
"asf": "video/x-ms-asf",
"wmv": "video/x-ms-wmv",
"avi": "video/x-msvideo",
}

1
utils_test.go Normal file
View File

@ -0,0 +1 @@
package fiber