2020-05-07 19:28:21 +02:00
|
|
|
// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️
|
2020-05-07 20:22:26 +02:00
|
|
|
// 🤖 Github Repository: https://github.com/gofiber/fiber
|
2020-05-07 19:28:21 +02:00
|
|
|
// 📌 API Documentation: https://docs.gofiber.io
|
2020-02-21 18:07:43 +01:00
|
|
|
|
|
|
|
package fiber
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2020-03-04 12:30:29 +01:00
|
|
|
"crypto/tls"
|
2020-02-21 18:07:43 +01:00
|
|
|
"fmt"
|
2020-02-27 04:10:26 -05:00
|
|
|
"log"
|
2020-02-21 18:07:43 +01:00
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httputil"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
2020-02-27 04:10:26 -05:00
|
|
|
"reflect"
|
2020-02-21 18:07:43 +01:00
|
|
|
"runtime"
|
2020-06-06 07:30:22 +02:00
|
|
|
"sort"
|
2020-02-21 18:07:43 +01:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2020-05-09 15:15:34 +02:00
|
|
|
"sync"
|
2020-02-21 18:07:43 +01:00
|
|
|
"time"
|
|
|
|
|
2020-05-23 09:25:18 +02:00
|
|
|
utils "github.com/gofiber/utils"
|
2020-02-21 18:07:43 +01:00
|
|
|
fasthttp "github.com/valyala/fasthttp"
|
|
|
|
)
|
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// Version of current package
|
2020-06-06 07:30:22 +02:00
|
|
|
const Version = "1.11.0"
|
2020-02-21 18:07:43 +01:00
|
|
|
|
2020-05-30 17:39:33 +02:00
|
|
|
// Map is a shortcut for map[string]interface{}, useful for JSON returns
|
2020-03-24 05:31:51 +01:00
|
|
|
type Map map[string]interface{}
|
|
|
|
|
2020-06-01 11:19:29 +02:00
|
|
|
// Handler defines a function to serve HTTP requests.
|
2020-05-24 10:02:21 -04:00
|
|
|
type Handler = func(*Ctx)
|
|
|
|
|
2020-06-07 20:57:55 +02:00
|
|
|
// default settings
|
|
|
|
var (
|
|
|
|
defaultBodyLimit = 4 * 1024 * 1024
|
|
|
|
defaultConcurrency = 256 * 1024
|
|
|
|
defaultErrorHandler = func(ctx *Ctx, err error) {
|
|
|
|
code := StatusInternalServerError
|
|
|
|
if e, ok := err.(*Error); ok {
|
|
|
|
code = e.Code
|
|
|
|
}
|
|
|
|
ctx.Status(code).SendString(err.Error())
|
|
|
|
}
|
|
|
|
defaultCompressedFileSuffix = ".fiber.gz"
|
|
|
|
)
|
|
|
|
|
2020-03-31 10:03:39 +02:00
|
|
|
// App denotes the Fiber application.
|
|
|
|
type App struct {
|
2020-05-23 09:25:18 +02:00
|
|
|
mutex sync.Mutex
|
|
|
|
// Route stack
|
|
|
|
stack [][]*Route
|
2020-06-06 07:30:22 +02:00
|
|
|
// Amount of registered routes
|
|
|
|
routes int
|
2020-05-23 09:25:18 +02:00
|
|
|
// Ctx pool
|
|
|
|
pool sync.Pool
|
2020-05-11 04:30:31 +02:00
|
|
|
// Fasthttp server
|
2020-05-16 05:11:25 +02:00
|
|
|
server *fasthttp.Server
|
|
|
|
// App settings
|
|
|
|
Settings *Settings
|
2020-03-24 05:31:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Settings holds is a struct holding the server settings
|
|
|
|
type Settings struct {
|
2020-06-06 07:30:22 +02:00
|
|
|
// ErrorHandler is executed when you pass an error in the Next(err) method
|
|
|
|
// This function is also executed when middleware.Recover() catches a panic
|
2020-06-06 20:49:02 +02:00
|
|
|
// Default: func(ctx *Ctx, err error) {
|
2020-06-06 20:42:08 +02:00
|
|
|
// code := StatusInternalServerError
|
2020-06-06 20:49:02 +02:00
|
|
|
// if e, ok := err.(*Error); ok {
|
2020-06-06 20:42:08 +02:00
|
|
|
// code = e.Code
|
|
|
|
// }
|
2020-06-06 20:49:02 +02:00
|
|
|
// ctx.Status(code).SendString(err.Error())
|
2020-06-06 07:30:22 +02:00
|
|
|
// }
|
2020-06-06 20:49:02 +02:00
|
|
|
ErrorHandler func(*Ctx, error)
|
2020-06-03 17:20:20 +02:00
|
|
|
|
2020-06-03 00:01:55 +02:00
|
|
|
// Enables the "Server: value" HTTP header.
|
|
|
|
// Default: ""
|
|
|
|
ServerHeader string
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// Enable strict routing. When enabled, the router treats "/foo" and "/foo/" as different.
|
2020-05-23 09:25:18 +02:00
|
|
|
// By default this is disabled and both "/foo" and "/foo/" will execute the same handler.
|
|
|
|
StrictRouting bool
|
|
|
|
|
|
|
|
// Enable case sensitive routing. When enabled, "/FoO" and "/foo" are different routes.
|
|
|
|
// By default this is disabled and both "/FoO" and "/foo" will execute the same handler.
|
|
|
|
CaseSensitive bool
|
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// Enables handler values to be immutable even if you return from handler
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: false
|
|
|
|
Immutable bool
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-04-28 21:34:34 +02:00
|
|
|
// Enable or disable ETag header generation, since both weak and strong etags are generated
|
|
|
|
// using the same hashing method (CRC-32). Weak ETags are the default when enabled.
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default value false
|
2020-04-28 21:34:34 +02:00
|
|
|
ETag bool
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-06-03 00:01:55 +02:00
|
|
|
// This will spawn multiple Go processes listening on the same port
|
|
|
|
// Default: false
|
|
|
|
Prefork bool
|
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// Max body size that the server accepts
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: 4 * 1024 * 1024
|
|
|
|
BodyLimit int
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-04-19 16:10:19 +02:00
|
|
|
// Maximum number of concurrent connections.
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: 256 * 1024
|
|
|
|
Concurrency int
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-04-19 16:10:19 +02:00
|
|
|
// Disable keep-alive connections, the server will close incoming connections after sending the first response to client
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: false
|
|
|
|
DisableKeepalive bool
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-04-19 16:10:19 +02:00
|
|
|
// When set to true causes the default date header to be excluded from the response.
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: false
|
|
|
|
DisableDefaultDate bool
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-04-19 16:10:19 +02:00
|
|
|
// When set to true, causes the default Content-Type header to be excluded from the Response.
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: false
|
|
|
|
DisableDefaultContentType bool
|
2020-05-23 09:25:18 +02:00
|
|
|
|
|
|
|
// By default all header names are normalized: conteNT-tYPE -> Content-Type
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: false
|
|
|
|
DisableHeaderNormalizing bool
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-06-01 20:29:52 +02:00
|
|
|
// When set to true, it will not print out the «Fiber» ASCII art and listening address
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: false
|
|
|
|
DisableStartupMessage bool
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-05-25 07:31:20 -04:00
|
|
|
// Templates is the interface that wraps the Render function.
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: nil
|
2020-05-25 07:31:20 -04:00
|
|
|
Templates Templates
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// The amount of time allowed to read the full request including body.
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: unlimited
|
|
|
|
ReadTimeout time.Duration
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// The maximum duration before timing out writes of the response.
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: unlimited
|
|
|
|
WriteTimeout time.Duration
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// The maximum amount of time to wait for the next request when keep-alive is enabled.
|
2020-06-03 00:01:55 +02:00
|
|
|
// Default: unlimited
|
|
|
|
IdleTimeout time.Duration
|
2020-02-21 18:07:43 +01:00
|
|
|
|
2020-06-07 20:35:41 +02:00
|
|
|
// CompressedFileSuffix adds suffix to the original file name and
|
|
|
|
// tries saving the resulting compressed file under the new file name.
|
|
|
|
// Default: ".fiber.gz"
|
|
|
|
CompressedFileSuffix string
|
|
|
|
|
|
|
|
// FEATURE: v1.12
|
2020-05-23 09:25:18 +02:00
|
|
|
// The router executes the same handler by default if StrictRouting or CaseSensitive is disabled.
|
|
|
|
// Enabling RedirectFixedPath will change this behaviour into a client redirect to the original route path.
|
|
|
|
// Using the status code 301 for GET requests and 308 for all other request methods.
|
|
|
|
// RedirectFixedPath bool
|
2020-03-22 01:51:53 +01:00
|
|
|
}
|
|
|
|
|
2020-04-28 21:34:34 +02:00
|
|
|
// Static struct
|
|
|
|
type Static struct {
|
|
|
|
// This works differently than the github.com/gofiber/compression middleware
|
|
|
|
// The server tries minimizing CPU usage by caching compressed files.
|
|
|
|
// Optional. Default value false
|
|
|
|
Compress bool
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-04-28 21:34:34 +02:00
|
|
|
// Enables byte range requests if set to true.
|
|
|
|
// Optional. Default value false
|
|
|
|
ByteRange bool
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-04-28 21:34:34 +02:00
|
|
|
// Enable directory browsing.
|
|
|
|
// Optional. Default value false.
|
|
|
|
Browse bool
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-04-28 21:34:34 +02:00
|
|
|
// Index file for serving a directory.
|
|
|
|
// Optional. Default value "index.html".
|
|
|
|
Index string
|
|
|
|
}
|
|
|
|
|
2020-06-06 18:54:02 +02:00
|
|
|
// Error represents an error that occurred while handling a request.
|
|
|
|
type Error struct {
|
|
|
|
Code int
|
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error makes it compatible with `error` interface.
|
|
|
|
func (e *Error) Error() string {
|
|
|
|
return e.Message
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewError creates a new HTTPError instance.
|
|
|
|
func NewError(code int, message ...string) *Error {
|
|
|
|
e := &Error{code, utils.StatusMessage(code)}
|
|
|
|
if len(message) > 0 {
|
|
|
|
e.Message = message[0]
|
|
|
|
}
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
2020-06-06 07:30:22 +02:00
|
|
|
// Routes returns all registered routes
|
|
|
|
//
|
|
|
|
// for _, r := range app.Routes() {
|
|
|
|
// fmt.Printf("%s\t%s\n", r.Method, r.Path)
|
2020-05-23 09:25:18 +02:00
|
|
|
// }
|
2020-06-06 07:30:22 +02:00
|
|
|
func (app *App) Routes() []*Route {
|
|
|
|
routes := make([]*Route, 0)
|
|
|
|
for m := range app.stack {
|
|
|
|
for r := range app.stack[m] {
|
|
|
|
// Ignore HEAD routes handling GET routes
|
|
|
|
if m == 1 && app.stack[m][r].Method == MethodGet {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
routes = append(routes, app.stack[m][r])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Sort routes by stack position
|
|
|
|
sort.Slice(routes, func(i, k int) bool {
|
|
|
|
return routes[i].pos < routes[k].pos
|
|
|
|
})
|
|
|
|
return routes
|
|
|
|
}
|
2020-05-23 09:25:18 +02:00
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// New creates a new Fiber named instance.
|
2020-03-16 17:18:25 +01:00
|
|
|
// You can pass optional settings when creating a new instance.
|
2020-03-31 10:03:39 +02:00
|
|
|
func New(settings ...*Settings) *App {
|
2020-05-23 09:25:18 +02:00
|
|
|
// Create a new app
|
|
|
|
app := &App{
|
|
|
|
// Create router stack
|
|
|
|
stack: make([][]*Route, len(methodINT)),
|
|
|
|
// Create Ctx pool
|
|
|
|
pool: sync.Pool{
|
|
|
|
New: func() interface{} {
|
|
|
|
return new(Ctx)
|
|
|
|
},
|
|
|
|
},
|
2020-06-07 20:57:55 +02:00
|
|
|
// Set settings
|
|
|
|
Settings: &Settings{},
|
2020-05-23 09:25:18 +02:00
|
|
|
}
|
2020-06-06 20:42:08 +02:00
|
|
|
|
2020-05-23 09:25:18 +02:00
|
|
|
// Overwrite settings if provided
|
2020-02-21 18:07:43 +01:00
|
|
|
if len(settings) > 0 {
|
2020-05-23 09:25:18 +02:00
|
|
|
app.Settings = settings[0]
|
2020-03-14 12:30:21 +01:00
|
|
|
}
|
2020-06-07 20:57:55 +02:00
|
|
|
|
|
|
|
if app.Settings.BodyLimit <= 0 {
|
|
|
|
app.Settings.BodyLimit = defaultBodyLimit
|
|
|
|
}
|
|
|
|
if app.Settings.Concurrency <= 0 {
|
|
|
|
app.Settings.Concurrency = defaultConcurrency
|
|
|
|
}
|
|
|
|
// Set default compressed file suffix
|
|
|
|
if app.Settings.CompressedFileSuffix == "" {
|
|
|
|
app.Settings.CompressedFileSuffix = defaultCompressedFileSuffix
|
|
|
|
}
|
|
|
|
// Set default error
|
|
|
|
if app.Settings.ErrorHandler == nil {
|
|
|
|
app.Settings.ErrorHandler = defaultErrorHandler
|
|
|
|
}
|
|
|
|
|
|
|
|
if !app.Settings.Prefork { // Default to -prefork flag if false
|
|
|
|
app.Settings.Prefork = utils.GetArgument("-prefork")
|
|
|
|
}
|
|
|
|
// Replace unsafe conversion functions
|
|
|
|
if app.Settings.Immutable {
|
2020-06-07 21:19:58 +02:00
|
|
|
getBytes, getString = getBytesImmutable, getStringImmutable
|
2020-06-07 20:57:55 +02:00
|
|
|
}
|
|
|
|
|
2020-05-23 09:25:18 +02:00
|
|
|
// Initialize app
|
|
|
|
return app.init()
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// Use registers a middleware route.
|
|
|
|
// Middleware matches requests beginning with the provided prefix.
|
2020-05-16 05:11:25 +02:00
|
|
|
// Providing a prefix is optional, it defaults to "/".
|
|
|
|
//
|
|
|
|
// - app.Use(handler)
|
|
|
|
// - app.Use("/api", handler)
|
|
|
|
// - app.Use("/api", handler, handler)
|
2020-05-23 09:25:18 +02:00
|
|
|
func (app *App) Use(args ...interface{}) *Route {
|
2020-05-16 05:11:25 +02:00
|
|
|
var prefix string
|
2020-05-24 10:02:21 -04:00
|
|
|
var handlers []Handler
|
2020-05-16 05:11:25 +02:00
|
|
|
|
2020-02-27 04:10:26 -05:00
|
|
|
for i := 0; i < len(args); i++ {
|
|
|
|
switch arg := args[i].(type) {
|
|
|
|
case string:
|
2020-05-16 05:11:25 +02:00
|
|
|
prefix = arg
|
2020-05-24 10:02:21 -04:00
|
|
|
case Handler:
|
2020-02-27 04:10:26 -05:00
|
|
|
handlers = append(handlers, arg)
|
|
|
|
default:
|
2020-05-24 10:02:21 -04:00
|
|
|
log.Fatalf("Use: Invalid Handler %v", reflect.TypeOf(arg))
|
2020-02-27 04:10:26 -05:00
|
|
|
}
|
|
|
|
}
|
2020-05-16 05:11:25 +02:00
|
|
|
return app.register("USE", prefix, handlers...)
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 05:11:25 +02:00
|
|
|
// Get ...
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) Get(path string, handlers ...Handler) *Route {
|
2020-05-23 09:25:18 +02:00
|
|
|
return app.Add(MethodGet, path, handlers...)
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 05:11:25 +02:00
|
|
|
// Head ...
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) Head(path string, handlers ...Handler) *Route {
|
2020-05-23 09:25:18 +02:00
|
|
|
return app.Add(MethodHead, path, handlers...)
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 05:11:25 +02:00
|
|
|
// Post ...
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) Post(path string, handlers ...Handler) *Route {
|
2020-05-23 09:25:18 +02:00
|
|
|
return app.Add(MethodPost, path, handlers...)
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 05:11:25 +02:00
|
|
|
// Put ...
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) Put(path string, handlers ...Handler) *Route {
|
2020-05-23 09:25:18 +02:00
|
|
|
return app.Add(MethodPut, path, handlers...)
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 05:11:25 +02:00
|
|
|
// Delete ...
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) Delete(path string, handlers ...Handler) *Route {
|
2020-05-23 09:25:18 +02:00
|
|
|
return app.Add(MethodDelete, path, handlers...)
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 05:11:25 +02:00
|
|
|
// Connect ...
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) Connect(path string, handlers ...Handler) *Route {
|
2020-05-23 09:25:18 +02:00
|
|
|
return app.Add(MethodConnect, path, handlers...)
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 05:11:25 +02:00
|
|
|
// Options ...
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) Options(path string, handlers ...Handler) *Route {
|
2020-05-23 09:25:18 +02:00
|
|
|
return app.Add(MethodOptions, path, handlers...)
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 05:11:25 +02:00
|
|
|
// Trace ...
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) Trace(path string, handlers ...Handler) *Route {
|
2020-05-23 09:25:18 +02:00
|
|
|
return app.Add(MethodTrace, path, handlers...)
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 05:11:25 +02:00
|
|
|
// Patch ...
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) Patch(path string, handlers ...Handler) *Route {
|
2020-05-23 09:25:18 +02:00
|
|
|
return app.Add(MethodPatch, path, handlers...)
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 05:11:25 +02:00
|
|
|
// Add ...
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) Add(method, path string, handlers ...Handler) *Route {
|
2020-05-16 05:11:25 +02:00
|
|
|
return app.register(method, path, handlers...)
|
|
|
|
}
|
|
|
|
|
2020-05-23 09:25:18 +02:00
|
|
|
// Static ...
|
|
|
|
func (app *App) Static(prefix, root string, config ...Static) *Route {
|
|
|
|
return app.registerStatic(prefix, root, config...)
|
2020-05-16 05:11:25 +02:00
|
|
|
}
|
|
|
|
|
2020-05-23 09:25:18 +02:00
|
|
|
// All ...
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) All(path string, handlers ...Handler) []*Route {
|
2020-05-23 09:25:18 +02:00
|
|
|
routes := make([]*Route, len(methodINT))
|
|
|
|
for method, i := range methodINT {
|
|
|
|
routes[i] = app.Add(method, path, handlers...)
|
|
|
|
}
|
|
|
|
return routes
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// Group is used for Routes with common prefix to define a new sub-router with optional middleware.
|
2020-05-24 10:02:21 -04:00
|
|
|
func (app *App) Group(prefix string, handlers ...Handler) *Group {
|
2020-03-22 01:51:53 +01:00
|
|
|
if len(handlers) > 0 {
|
2020-05-23 09:25:18 +02:00
|
|
|
app.register("USE", prefix, handlers...)
|
2020-05-16 05:11:25 +02:00
|
|
|
}
|
2020-05-23 09:25:18 +02:00
|
|
|
return &Group{prefix: prefix, app: app}
|
2020-03-22 01:51:53 +01:00
|
|
|
}
|
|
|
|
|
2020-04-12 14:58:05 +02:00
|
|
|
// Serve can be used to pass a custom listener
|
|
|
|
// This method does not support the Prefork feature
|
2020-04-23 00:33:36 +02:00
|
|
|
// Preforkin is not available using app.Serve(ln net.Listener)
|
2020-04-12 14:58:05 +02:00
|
|
|
// You can pass an optional *tls.Config to enable TLS.
|
|
|
|
func (app *App) Serve(ln net.Listener, tlsconfig ...*tls.Config) error {
|
2020-05-12 19:24:04 +02:00
|
|
|
// Update fiber server settings
|
2020-05-16 05:11:25 +02:00
|
|
|
app.init()
|
2020-04-12 14:58:05 +02:00
|
|
|
// TLS config
|
|
|
|
if len(tlsconfig) > 0 {
|
|
|
|
ln = tls.NewListener(ln, tlsconfig[0])
|
|
|
|
}
|
2020-05-29 23:41:03 +02:00
|
|
|
// Print startup message
|
2020-04-27 23:34:35 +02:00
|
|
|
if !app.Settings.DisableStartupMessage {
|
2020-06-07 21:10:38 +02:00
|
|
|
startupMessage(ln)
|
2020-04-27 23:34:35 +02:00
|
|
|
}
|
2020-06-01 20:29:52 +02:00
|
|
|
|
2020-04-12 14:58:05 +02:00
|
|
|
return app.server.Serve(ln)
|
|
|
|
}
|
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// Listen serves HTTP requests from the given addr or port.
|
|
|
|
// You can pass an optional *tls.Config to enable TLS.
|
2020-03-31 10:03:39 +02:00
|
|
|
func (app *App) Listen(address interface{}, tlsconfig ...*tls.Config) error {
|
2020-02-21 18:07:43 +01:00
|
|
|
addr, ok := address.(string)
|
|
|
|
if !ok {
|
|
|
|
port, ok := address.(int)
|
|
|
|
if !ok {
|
2020-06-08 02:55:19 +02:00
|
|
|
return fmt.Errorf("listen: host must be an `int` port or `string` address")
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
addr = strconv.Itoa(port)
|
|
|
|
}
|
|
|
|
if !strings.Contains(addr, ":") {
|
|
|
|
addr = ":" + addr
|
|
|
|
}
|
2020-05-12 19:24:04 +02:00
|
|
|
// Update fiber server settings
|
2020-05-16 05:11:25 +02:00
|
|
|
app.init()
|
2020-05-09 15:15:34 +02:00
|
|
|
// Setup listener
|
2020-02-21 18:07:43 +01:00
|
|
|
var ln net.Listener
|
|
|
|
var err error
|
2020-05-09 15:15:34 +02:00
|
|
|
// Prefork enabled, not available on windows
|
2020-04-12 14:58:05 +02:00
|
|
|
if app.Settings.Prefork && runtime.NumCPU() > 1 && runtime.GOOS != "windows" {
|
2020-02-21 18:07:43 +01:00
|
|
|
if ln, err = app.prefork(addr); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ln, err = net.Listen("tcp4", addr); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2020-03-04 12:30:29 +01:00
|
|
|
// TLS config
|
|
|
|
if len(tlsconfig) > 0 {
|
|
|
|
ln = tls.NewListener(ln, tlsconfig[0])
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
2020-05-29 23:41:03 +02:00
|
|
|
// Print startup message
|
2020-05-23 09:25:18 +02:00
|
|
|
if !app.Settings.DisableStartupMessage && !utils.GetArgument("-child") {
|
2020-06-07 21:10:38 +02:00
|
|
|
startupMessage(ln)
|
2020-04-12 14:58:05 +02:00
|
|
|
}
|
2020-05-30 17:39:33 +02:00
|
|
|
|
2020-02-21 18:07:43 +01:00
|
|
|
return app.server.Serve(ln)
|
|
|
|
}
|
|
|
|
|
2020-03-24 05:31:51 +01:00
|
|
|
// Shutdown gracefully shuts down the server without interrupting any active connections.
|
|
|
|
// Shutdown works by first closing all open listeners and then waiting indefinitely for all connections to return to idle and then shut down.
|
|
|
|
//
|
|
|
|
// When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS immediately return nil.
|
|
|
|
// Make sure the program doesn't exit and waits instead for Shutdown to return.
|
|
|
|
//
|
|
|
|
// Shutdown does not close keepalive connections so its recommended to set ReadTimeout to something else than 0.
|
2020-03-31 10:03:39 +02:00
|
|
|
func (app *App) Shutdown() error {
|
2020-05-09 15:15:34 +02:00
|
|
|
app.mutex.Lock()
|
|
|
|
defer app.mutex.Unlock()
|
2020-02-21 18:07:43 +01:00
|
|
|
if app.server == nil {
|
2020-06-08 02:55:19 +02:00
|
|
|
return fmt.Errorf("shutdown: server is not running")
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
|
|
|
return app.server.Shutdown()
|
|
|
|
}
|
|
|
|
|
2020-04-12 14:58:05 +02:00
|
|
|
// Test is used for internal debugging by passing a *http.Request
|
2020-04-27 23:34:35 +02:00
|
|
|
// Timeout is optional and defaults to 1s, -1 will disable it completely.
|
2020-03-31 10:03:39 +02:00
|
|
|
func (app *App) Test(request *http.Request, msTimeout ...int) (*http.Response, error) {
|
2020-04-27 23:34:35 +02:00
|
|
|
timeout := 1000 // 1 second default
|
2020-03-22 17:35:12 +01:00
|
|
|
if len(msTimeout) > 0 {
|
|
|
|
timeout = msTimeout[0]
|
|
|
|
}
|
2020-05-16 08:12:43 +02:00
|
|
|
// Add Content-Length if not provided with body
|
|
|
|
if request.Body != http.NoBody && request.Header.Get("Content-Length") == "" {
|
|
|
|
request.Header.Add("Content-Length", strconv.FormatInt(request.ContentLength, 10))
|
|
|
|
}
|
2020-03-20 16:43:28 +01:00
|
|
|
// Dump raw http request
|
|
|
|
dump, err := httputil.DumpRequest(request, true)
|
2020-02-21 18:07:43 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-05-12 19:24:04 +02:00
|
|
|
// Update server settings
|
2020-05-16 05:11:25 +02:00
|
|
|
app.init()
|
2020-05-12 19:24:04 +02:00
|
|
|
// Create test connection
|
|
|
|
conn := new(testConn)
|
2020-03-20 16:43:28 +01:00
|
|
|
// Write raw http request
|
2020-05-12 19:24:04 +02:00
|
|
|
if _, err = conn.r.Write(dump); err != nil {
|
2020-02-21 18:07:43 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Serve conn to server
|
|
|
|
channel := make(chan error)
|
|
|
|
go func() {
|
2020-05-12 19:24:04 +02:00
|
|
|
channel <- app.server.ServeConn(conn)
|
2020-02-21 18:07:43 +01:00
|
|
|
}()
|
2020-03-23 21:52:37 +01:00
|
|
|
// Wait for callback
|
2020-04-27 23:34:35 +02:00
|
|
|
if timeout >= 0 {
|
|
|
|
// With timeout
|
|
|
|
select {
|
|
|
|
case err = <-channel:
|
|
|
|
case <-time.After(time.Duration(timeout) * time.Millisecond):
|
2020-06-08 02:55:19 +02:00
|
|
|
return nil, fmt.Errorf("test: timeout error %vms", timeout)
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
2020-04-27 23:34:35 +02:00
|
|
|
} else {
|
|
|
|
// Without timeout
|
2020-05-12 23:24:04 +02:00
|
|
|
err = <-channel
|
2020-04-27 23:34:35 +02:00
|
|
|
}
|
|
|
|
// Check for errors
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-03-24 03:43:13 +01:00
|
|
|
}
|
2020-03-20 16:43:28 +01:00
|
|
|
// Read response
|
2020-05-12 19:24:04 +02:00
|
|
|
buffer := bufio.NewReader(&conn.w)
|
2020-03-20 16:43:28 +01:00
|
|
|
// Convert raw http response to *http.Response
|
2020-02-26 19:31:43 -05:00
|
|
|
resp, err := http.ReadResponse(buffer, request)
|
2020-02-21 18:07:43 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// Return *http.Response
|
|
|
|
return resp, nil
|
|
|
|
}
|
|
|
|
|
2020-03-27 06:56:58 +01:00
|
|
|
// Sharding: https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/
|
2020-03-31 10:03:39 +02:00
|
|
|
func (app *App) prefork(address string) (ln net.Listener, err error) {
|
2020-02-21 18:07:43 +01:00
|
|
|
// Master proc
|
2020-05-23 09:25:18 +02:00
|
|
|
if !utils.GetArgument("-child") {
|
2020-03-01 07:31:14 +01:00
|
|
|
addr, err := net.ResolveTCPAddr("tcp", address)
|
2020-02-21 18:07:43 +01:00
|
|
|
if err != nil {
|
|
|
|
return ln, err
|
|
|
|
}
|
2020-03-01 07:31:14 +01:00
|
|
|
tcplistener, err := net.ListenTCP("tcp", addr)
|
2020-02-21 18:07:43 +01:00
|
|
|
if err != nil {
|
|
|
|
return ln, err
|
|
|
|
}
|
|
|
|
fl, err := tcplistener.File()
|
|
|
|
if err != nil {
|
|
|
|
return ln, err
|
|
|
|
}
|
2020-02-26 19:31:43 -05:00
|
|
|
files := []*os.File{fl}
|
2020-02-21 18:07:43 +01:00
|
|
|
childs := make([]*exec.Cmd, runtime.NumCPU()/2)
|
|
|
|
// #nosec G204
|
|
|
|
for i := range childs {
|
2020-02-26 19:31:43 -05:00
|
|
|
childs[i] = exec.Command(os.Args[0], append(os.Args[1:], "-prefork", "-child")...)
|
2020-02-21 18:07:43 +01:00
|
|
|
childs[i].Stdout = os.Stdout
|
|
|
|
childs[i].Stderr = os.Stderr
|
2020-02-26 19:31:43 -05:00
|
|
|
childs[i].ExtraFiles = files
|
2020-02-21 18:07:43 +01:00
|
|
|
if err := childs[i].Start(); err != nil {
|
|
|
|
return ln, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-29 04:55:34 +02:00
|
|
|
for k := range childs {
|
|
|
|
if err := childs[k].Wait(); err != nil {
|
2020-02-21 18:07:43 +01:00
|
|
|
return ln, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
os.Exit(0)
|
|
|
|
} else {
|
2020-03-27 06:56:58 +01:00
|
|
|
// 1 core per child
|
2020-02-26 19:31:43 -05:00
|
|
|
runtime.GOMAXPROCS(1)
|
2020-02-21 18:07:43 +01:00
|
|
|
ln, err = net.FileListener(os.NewFile(3, ""))
|
|
|
|
}
|
|
|
|
return ln, err
|
|
|
|
}
|
|
|
|
|
2020-03-29 14:13:02 +02:00
|
|
|
type disableLogger struct{}
|
|
|
|
|
|
|
|
func (dl *disableLogger) Printf(format string, args ...interface{}) {
|
|
|
|
// fmt.Println(fmt.Sprintf(format, args...))
|
|
|
|
}
|
|
|
|
|
2020-05-23 09:25:18 +02:00
|
|
|
func (app *App) init() *App {
|
2020-05-12 19:24:04 +02:00
|
|
|
app.mutex.Lock()
|
2020-05-16 05:11:25 +02:00
|
|
|
if app.server == nil {
|
|
|
|
app.server = &fasthttp.Server{
|
|
|
|
Logger: &disableLogger{},
|
|
|
|
LogAllErrors: false,
|
|
|
|
ErrorHandler: func(fctx *fasthttp.RequestCtx, err error) {
|
2020-06-06 18:54:02 +02:00
|
|
|
ctx := app.AcquireCtx(fctx)
|
2020-05-16 05:11:25 +02:00
|
|
|
if _, ok := err.(*fasthttp.ErrSmallBuffer); ok {
|
2020-06-06 19:02:28 +02:00
|
|
|
ctx.err = ErrRequestHeaderFieldsTooLarge
|
2020-05-16 05:11:25 +02:00
|
|
|
} else if netErr, ok := err.(*net.OpError); ok && netErr.Timeout() {
|
2020-06-06 19:02:28 +02:00
|
|
|
ctx.err = ErrRequestTimeout
|
2020-05-16 05:11:25 +02:00
|
|
|
} else if len(err.Error()) == 33 && err.Error() == "body size exceeds the given limit" {
|
2020-06-06 19:02:28 +02:00
|
|
|
ctx.err = ErrRequestEntityTooLarge
|
2020-05-16 05:11:25 +02:00
|
|
|
} else {
|
2020-06-06 19:02:28 +02:00
|
|
|
ctx.err = ErrBadRequest
|
2020-05-16 05:11:25 +02:00
|
|
|
}
|
2020-06-07 10:13:50 +02:00
|
|
|
app.Settings.ErrorHandler(ctx, ctx.err)
|
2020-06-06 19:02:28 +02:00
|
|
|
app.ReleaseCtx(ctx)
|
2020-05-16 05:11:25 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if app.server.Handler == nil {
|
|
|
|
app.server.Handler = app.handler
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
2020-05-16 05:11:25 +02:00
|
|
|
app.server.Name = app.Settings.ServerHeader
|
|
|
|
app.server.Concurrency = app.Settings.Concurrency
|
|
|
|
app.server.NoDefaultDate = app.Settings.DisableDefaultDate
|
|
|
|
app.server.NoDefaultContentType = app.Settings.DisableDefaultContentType
|
|
|
|
app.server.DisableHeaderNamesNormalizing = app.Settings.DisableHeaderNormalizing
|
|
|
|
app.server.DisableKeepalive = app.Settings.DisableKeepalive
|
|
|
|
app.server.MaxRequestBodySize = app.Settings.BodyLimit
|
|
|
|
app.server.NoDefaultServerHeader = app.Settings.ServerHeader == ""
|
|
|
|
app.server.ReadTimeout = app.Settings.ReadTimeout
|
|
|
|
app.server.WriteTimeout = app.Settings.WriteTimeout
|
|
|
|
app.server.IdleTimeout = app.Settings.IdleTimeout
|
2020-05-12 19:24:04 +02:00
|
|
|
app.mutex.Unlock()
|
2020-05-23 09:25:18 +02:00
|
|
|
return app
|
2020-02-21 18:07:43 +01:00
|
|
|
}
|
2020-06-07 21:10:38 +02:00
|
|
|
|
|
|
|
func startupMessage(ln net.Listener) {
|
|
|
|
fmt.Printf(" _______ __\n ____ / ____(_) /_ ___ _____\n_____ / /_ / / __ \\/ _ \\/ ___/\n __ / __/ / / /_/ / __/ /\n /_/ /_/_.___/\\___/_/ v%s\n", Version)
|
|
|
|
fmt.Printf("Started listening on %s\n", ln.Addr().String())
|
|
|
|
}
|