2020-02-08 01:05:13 +01:00
|
|
|
// 🚀 Fiber is an Express.js inspired web framework written in Go with 💖
|
2020-01-22 05:42:37 +01:00
|
|
|
// 📌 Please open an issue if you got suggestions or found a bug!
|
2020-02-08 01:05:13 +01:00
|
|
|
// 🖥 Links: https://github.com/gofiber/fiber, https://fiber.wiki
|
2020-01-15 03:07:49 +01:00
|
|
|
|
2020-01-16 00:24:58 +01:00
|
|
|
// 🦸 Not all heroes wear capes, thank you to some amazing people
|
2020-02-08 01:05:13 +01:00
|
|
|
// 💖 @valyala, @erikdubbelboer, @savsgio, @julienschmidt, @koddr
|
2020-01-15 03:07:49 +01:00
|
|
|
|
2019-12-30 07:29:42 -05:00
|
|
|
package fiber
|
|
|
|
|
|
|
|
import (
|
2020-02-06 21:34:36 +01:00
|
|
|
"bufio"
|
2020-02-05 17:37:04 +01:00
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net"
|
2020-02-06 02:43:13 +01:00
|
|
|
"net/http"
|
|
|
|
"net/http/httputil"
|
2020-01-06 18:38:39 -05:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2020-02-03 13:40:50 +01:00
|
|
|
"reflect"
|
2019-12-30 07:29:42 -05:00
|
|
|
"regexp"
|
|
|
|
"strings"
|
2020-02-05 17:37:04 +01:00
|
|
|
"time"
|
2019-12-30 07:29:42 -05:00
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
func getParams(path string) (params []string) {
|
|
|
|
segments := strings.Split(path, "/")
|
2020-01-30 23:17:25 -05:00
|
|
|
replacer := strings.NewReplacer(":", "", "?", "")
|
2019-12-30 07:29:42 -05:00
|
|
|
for _, s := range segments {
|
|
|
|
if s == "" {
|
|
|
|
continue
|
2020-01-30 23:17:25 -05:00
|
|
|
} else if s[0] == ':' {
|
|
|
|
params = append(params, replacer.Replace(s))
|
|
|
|
} else if s[0] == '*' {
|
2019-12-30 07:29:42 -05:00
|
|
|
params = append(params, "*")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return params
|
|
|
|
}
|
2020-02-01 19:42:40 +03:00
|
|
|
|
2019-12-30 07:29:42 -05:00
|
|
|
func getRegex(path string) (*regexp.Regexp, error) {
|
|
|
|
pattern := "^"
|
|
|
|
segments := strings.Split(path, "/")
|
|
|
|
for _, s := range segments {
|
2020-02-04 13:15:24 +02:00
|
|
|
if s == "" {
|
|
|
|
continue
|
|
|
|
}
|
2020-01-30 23:17:25 -05:00
|
|
|
if s[0] == ':' {
|
2019-12-30 07:29:42 -05:00
|
|
|
if strings.Contains(s, "?") {
|
|
|
|
pattern += "(?:/([^/]+?))?"
|
|
|
|
} else {
|
|
|
|
pattern += "/(?:([^/]+?))"
|
|
|
|
}
|
2020-01-30 23:17:25 -05:00
|
|
|
} else if s[0] == '*' {
|
2019-12-30 07:29:42 -05:00
|
|
|
pattern += "/(.*)"
|
|
|
|
} else {
|
|
|
|
pattern += "/" + s
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pattern += "/?$"
|
|
|
|
regex, err := regexp.Compile(pattern)
|
|
|
|
return regex, err
|
|
|
|
}
|
2020-02-01 19:42:40 +03:00
|
|
|
|
2020-01-30 23:17:25 -05:00
|
|
|
func getFiles(root string) (files []string, isDir bool, err error) {
|
2020-01-06 18:38:39 -05:00
|
|
|
err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
|
|
|
if !info.IsDir() {
|
2020-01-11 04:59:51 +01:00
|
|
|
files = append(files, path)
|
2020-01-06 18:38:39 -05:00
|
|
|
} else {
|
2020-01-11 04:59:51 +01:00
|
|
|
isDir = true
|
2020-01-06 18:38:39 -05:00
|
|
|
}
|
2020-01-11 04:59:51 +01:00
|
|
|
return err
|
2020-01-06 18:38:39 -05:00
|
|
|
})
|
2020-01-11 04:59:51 +01:00
|
|
|
return files, isDir, err
|
2020-01-06 18:38:39 -05:00
|
|
|
}
|
2020-02-01 19:42:40 +03:00
|
|
|
|
2020-01-30 23:17:25 -05:00
|
|
|
func getType(ext string) (mime string) {
|
2020-02-06 21:34:36 +01:00
|
|
|
if ext == "" {
|
|
|
|
return mime
|
|
|
|
}
|
2020-01-30 23:17:25 -05:00
|
|
|
if ext[0] == '.' {
|
|
|
|
ext = ext[1:]
|
|
|
|
}
|
|
|
|
mime = contentTypes[ext]
|
|
|
|
if mime == "" {
|
|
|
|
return contentTypeOctetStream
|
|
|
|
}
|
|
|
|
return mime
|
2019-12-30 07:29:42 -05:00
|
|
|
}
|
2020-02-01 19:42:40 +03:00
|
|
|
|
2020-01-30 23:17:25 -05:00
|
|
|
func getStatus(status int) (msg string) {
|
2020-01-31 15:19:57 -05:00
|
|
|
return statusMessages[status]
|
2019-12-30 07:29:42 -05:00
|
|
|
}
|
2020-02-01 19:42:40 +03:00
|
|
|
|
2020-02-03 13:40:50 +01:00
|
|
|
// #nosec G103
|
|
|
|
// getString converts byte slice to a string without memory allocation.
|
|
|
|
// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
|
2020-01-30 23:17:25 -05:00
|
|
|
func getString(b []byte) string {
|
|
|
|
return *(*string)(unsafe.Pointer(&b))
|
2020-01-11 04:59:51 +01:00
|
|
|
}
|
2020-02-01 19:42:40 +03:00
|
|
|
|
2020-02-03 13:40:50 +01:00
|
|
|
// #nosec G103
|
|
|
|
// 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))
|
2020-02-05 11:54:10 +01:00
|
|
|
bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
|
|
|
|
bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len
|
2020-02-03 13:40:50 +01:00
|
|
|
return b
|
2020-01-11 04:59:51 +01:00
|
|
|
}
|
2020-02-05 17:37:04 +01:00
|
|
|
|
2020-02-06 21:34:36 +01:00
|
|
|
// Test takes a http.Request and execute a fake connection to the application
|
|
|
|
// It returns a http.Response when the connection was successfull
|
|
|
|
func (r *Fiber) Test(req *http.Request) (*http.Response, error) {
|
|
|
|
// Get raw http request
|
|
|
|
reqRaw, err := httputil.DumpRequest(req, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-02-05 17:37:04 +01:00
|
|
|
}
|
2020-02-06 21:34:36 +01:00
|
|
|
// Setup a fiber server struct
|
2020-02-07 00:27:50 +01:00
|
|
|
r.httpServer = r.setupServer()
|
2020-02-06 21:34:36 +01:00
|
|
|
// Create fake connection
|
|
|
|
conn := &conn{}
|
|
|
|
// Pass HTTP request to conn
|
2020-02-07 22:28:18 +03:00
|
|
|
_, err = conn.r.Write(reqRaw)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-02-06 21:34:36 +01:00
|
|
|
// Serve conn to server
|
|
|
|
channel := make(chan error)
|
2020-02-05 17:37:04 +01:00
|
|
|
go func() {
|
2020-02-07 00:27:50 +01:00
|
|
|
channel <- r.httpServer.ServeConn(conn)
|
2020-02-05 17:37:04 +01:00
|
|
|
}()
|
2020-02-06 21:34:36 +01:00
|
|
|
// Wait for callback
|
2020-02-05 17:37:04 +01:00
|
|
|
select {
|
2020-02-06 21:34:36 +01:00
|
|
|
case err := <-channel:
|
2020-02-05 17:37:04 +01:00
|
|
|
if err != nil {
|
2020-02-06 21:34:36 +01:00
|
|
|
return nil, err
|
2020-02-05 17:37:04 +01:00
|
|
|
}
|
2020-02-06 21:34:36 +01:00
|
|
|
// Throw timeout error after 200ms
|
2020-02-07 00:27:50 +01:00
|
|
|
case <-time.After(500 * time.Millisecond):
|
2020-02-06 21:34:36 +01:00
|
|
|
return nil, fmt.Errorf("Timeout")
|
2020-02-05 17:37:04 +01:00
|
|
|
}
|
2020-02-06 21:34:36 +01:00
|
|
|
// Get raw HTTP response
|
|
|
|
respRaw, err := ioutil.ReadAll(&conn.w)
|
2020-02-05 17:37:04 +01:00
|
|
|
if err != nil {
|
2020-02-06 21:34:36 +01:00
|
|
|
return nil, err
|
2020-02-05 17:37:04 +01:00
|
|
|
}
|
2020-02-06 21:34:36 +01:00
|
|
|
// Create buffer
|
|
|
|
reader := strings.NewReader(getString(respRaw))
|
|
|
|
buffer := bufio.NewReader(reader)
|
|
|
|
// Convert raw HTTP response to http.Response
|
|
|
|
resp, err := http.ReadResponse(buffer, req)
|
2020-02-05 17:37:04 +01:00
|
|
|
if err != nil {
|
2020-02-06 21:34:36 +01:00
|
|
|
return nil, err
|
2020-02-05 17:37:04 +01:00
|
|
|
}
|
2020-02-06 21:34:36 +01:00
|
|
|
// Return *http.Response
|
|
|
|
return resp, nil
|
2020-02-05 17:37:04 +01:00
|
|
|
}
|
|
|
|
|
2020-02-06 21:34:36 +01:00
|
|
|
// https://golang.org/src/net/net.go#L113
|
|
|
|
type conn struct {
|
2020-02-05 17:37:04 +01:00
|
|
|
net.Conn
|
|
|
|
r bytes.Buffer
|
|
|
|
w bytes.Buffer
|
|
|
|
}
|
|
|
|
|
2020-02-06 21:34:36 +01:00
|
|
|
func (c *conn) RemoteAddr() net.Addr {
|
2020-02-05 17:37:04 +01:00
|
|
|
return &net.TCPAddr{
|
2020-02-06 21:34:36 +01:00
|
|
|
IP: net.IPv4(0, 0, 0, 0),
|
2020-02-05 17:37:04 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-06 21:34:36 +01:00
|
|
|
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 }
|