-
Notifications
You must be signed in to change notification settings - Fork 0
/
fiber.go
205 lines (179 loc) · 5.88 KB
/
fiber.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package httputil
import (
"bufio"
"io"
"net"
"net/http"
"strings"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/utils"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttpadaptor"
)
func StripPrefix(prefix string, hh fiber.Handler) fiber.Handler {
return func(ctx *fiber.Ctx) error {
ctx.Request().Header.Set("Path-Prefix", prefix)
ctx.Request().SetRequestURI(strings.TrimPrefix(string(ctx.Request().RequestURI()), prefix))
return hh(ctx)
}
}
func FastHandler(h fasthttp.RequestHandler) http.Handler {
return handlerFunc(h)
}
func HTTPHandlerFunc(h http.HandlerFunc) fiber.Handler { return HTTPHandler(h) }
func HTTPHandler(h http.Handler) fiber.Handler {
return func(c *fiber.Ctx) error {
handler := NewFastHTTPHandler(h)
handler(c.Context())
return nil
}
}
// NewFastHTTPHandlerFunc wraps net/http handler func to fasthttp
// request handler, so it can be passed to fasthttp server.
//
// While this function may be used for easy switching from net/http to fasthttp,
// it has the following drawbacks comparing to using manually written fasthttp
// request handler:
//
// - A lot of useful functionality provided by fasthttp is missing
// from net/http handler.
// - net/http -> fasthttp handler conversion has some overhead,
// so the returned handler will be always slower than manually written
// fasthttp handler.
//
// So it is advisable using this function only for quick net/http -> fasthttp
// switching. Then manually convert net/http handlers to fasthttp handlers
// according to https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp .
func NewFastHTTPHandlerFunc(h http.HandlerFunc) fasthttp.RequestHandler {
return NewFastHTTPHandler(h)
}
// NewFastHTTPHandler wraps net/http handler to fasthttp request handler,
// so it can be passed to fasthttp server.
//
// While this function may be used for easy switching from net/http to fasthttp,
// it has the following drawbacks comparing to using manually written fasthttp
// request handler:
//
// - A lot of useful functionality provided by fasthttp is missing
// from net/http handler.
// - net/http -> fasthttp handler conversion has some overhead,
// so the returned handler will be always slower than manually written
// fasthttp handler.
//
// So it is advisable using this function only for quick net/http -> fasthttp
// switching. Then manually convert net/http handlers to fasthttp handlers
// according to https://github.com/valyala/fasthttp#switching-from-nethttp-to-fasthttp .
func NewFastHTTPHandler(h http.Handler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
var r http.Request
if err := fasthttpadaptor.ConvertRequest(ctx, &r, true); err != nil {
ctx.Logger().Printf("cannot parse requestURI %q: %v", r.RequestURI, err)
ctx.Error("Internal Server Error", fasthttp.StatusInternalServerError)
return
}
w := netHTTPResponseWriter{
h: r.Header,
w: ctx.Response.BodyWriter(),
r: ctx.RequestBodyStream(),
conn: ctx.Conn(),
}
h.ServeHTTP(&w, r.WithContext(ctx))
ctx.SetStatusCode(w.StatusCode())
haveContentType := false
for k, vv := range w.Header() {
if k == fasthttp.HeaderContentType {
haveContentType = true
}
for _, v := range vv {
ctx.Response.Header.Add(k, v)
}
}
if !haveContentType {
// From net/http.ResponseWriter.Write:
// If the Header does not contain a Content-Type line, Write adds a Content-Type set
// to the result of passing the initial 512 bytes of written data to DetectContentType.
l := 512
b := ctx.Response.Body()
if len(b) < 512 {
l = len(b)
}
ctx.Response.Header.Set(fasthttp.HeaderContentType, http.DetectContentType(b[:l]))
}
}
}
type netHTTPResponseWriter struct {
statusCode int
h http.Header
w io.Writer
r io.Reader
conn net.Conn
}
func (w *netHTTPResponseWriter) StatusCode() int {
if w.statusCode == 0 {
return http.StatusOK
}
return w.statusCode
}
func (w *netHTTPResponseWriter) Header() http.Header {
if w.h == nil {
w.h = make(http.Header)
}
return w.h
}
func (w *netHTTPResponseWriter) WriteHeader(statusCode int) {
w.statusCode = statusCode
}
func (w *netHTTPResponseWriter) Write(p []byte) (int, error) {
return w.w.Write(p)
}
func (w *netHTTPResponseWriter) Flush() {
ff, ok := w.w.(http.Flusher)
if ok {
ff.Flush()
}
}
func (w *netHTTPResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return w.conn, &bufio.ReadWriter{Reader: bufio.NewReader(w.r), Writer: bufio.NewWriter(w.w)}, nil
}
func handlerFunc(h fasthttp.RequestHandler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// New fasthttp request
req := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(req)
// Convert net/http -> fasthttp request
if r.Body != nil {
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError)
return
}
req.Header.SetContentLength(len(body))
_, _ = req.BodyWriter().Write(body)
}
req.Header.SetMethod(r.Method)
req.SetRequestURI(r.RequestURI)
req.SetHost(r.Host)
for key, val := range r.Header {
for _, v := range val {
req.Header.Set(key, v)
}
}
if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil && err.(*net.AddrError).Err == "missing port in address" {
r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80")
}
remoteAddr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr)
if err != nil {
http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError)
return
}
var ctx fasthttp.RequestCtx
ctx.Init(req, remoteAddr, nil)
h(&ctx)
// Convert fasthttp Ctx > net/http
ctx.Response.Header.VisitAll(func(k, v []byte) {
w.Header().Add(string(k), string(v))
})
w.WriteHeader(ctx.Response.StatusCode())
_, _ = w.Write(ctx.Response.Body())
}
}