-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp.go
136 lines (118 loc) · 4.08 KB
/
http.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
package web
import (
"fmt"
"net/http"
"runtime/debug"
"strconv"
"time"
"github.com/ecnepsnai/web/router"
)
// HTTP describes a HTTP server. HTTP handles are exposed to the raw http request and response writers.
type HTTP struct {
server *Server
}
// GET register a new HTTP GET request handle
func (h HTTP) GET(path string, handle HTTPHandle, options HandleOptions) {
h.registerHTTPEndpoint("GET", path, handle, options)
}
// HEAD register a new HTTP HEAD request handle
func (h HTTP) HEAD(path string, handle HTTPHandle, options HandleOptions) {
h.registerHTTPEndpoint("HEAD", path, handle, options)
}
// OPTIONS register a new HTTP OPTIONS request handle
func (h HTTP) OPTIONS(path string, handle HTTPHandle, options HandleOptions) {
h.registerHTTPEndpoint("OPTIONS", path, handle, options)
}
// POST register a new HTTP POST request handle
func (h HTTP) POST(path string, handle HTTPHandle, options HandleOptions) {
h.registerHTTPEndpoint("POST", path, handle, options)
}
// PUT register a new HTTP PUT request handle
func (h HTTP) PUT(path string, handle HTTPHandle, options HandleOptions) {
h.registerHTTPEndpoint("PUT", path, handle, options)
}
// PATCH register a new HTTP PATCH request handle
func (h HTTP) PATCH(path string, handle HTTPHandle, options HandleOptions) {
h.registerHTTPEndpoint("PATCH", path, handle, options)
}
// DELETE register a new HTTP DELETE request handle
func (h HTTP) DELETE(path string, handle HTTPHandle, options HandleOptions) {
h.registerHTTPEndpoint("DELETE", path, handle, options)
}
func (h HTTP) registerHTTPEndpoint(method string, path string, handle HTTPHandle, options HandleOptions) {
log.PDebug("Register HTTP endpoint", map[string]interface{}{
"method": method,
"path": path,
})
h.server.router.Handle(method, path, h.httpPreHandle(handle, options))
}
func (h HTTP) httpPreHandle(endpointHandle HTTPHandle, options HandleOptions) router.Handle {
return func(w http.ResponseWriter, request router.Request) {
if options.PreHandle != nil {
if err := options.PreHandle(w, request.HTTP); err != nil {
return
}
}
if h.server.isRateLimited(w, request.HTTP) {
return
}
if options.MaxBodyLength > 0 {
// We don't need to worry about this not being a number. Go's own HTTP server
// won't respond to requests like these
length, _ := strconv.ParseUint(request.HTTP.Header.Get("Content-Length"), 10, 64)
if length > options.MaxBodyLength {
log.PError("Rejecting HTTP request with oversized body", map[string]interface{}{
"body_length": length,
"max_length": options.MaxBodyLength,
})
w.WriteHeader(413)
return
}
}
var userData interface{}
if options.AuthenticateMethod != nil {
userData = options.AuthenticateMethod(request.HTTP)
if isUserdataNil(userData) {
if options.UnauthorizedMethod == nil {
log.PWarn("Rejected request to authenticated HTTP endpoint", map[string]interface{}{
"url": request.HTTP.URL,
"method": request.HTTP.Method,
"remote_addr": RealRemoteAddr(request.HTTP),
})
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("<html><head><title>Unauthorized</title></head><body><h1>Unauthorized</h1></body></html>"))
return
}
options.UnauthorizedMethod(w, request.HTTP)
return
}
}
start := time.Now()
defer func() {
if p := recover(); p != nil {
log.PError("Recovered from panic during HTTP handle", map[string]interface{}{
"error": fmt.Sprintf("%v", p),
"route": request.HTTP.URL.Path,
"method": request.HTTP.Method,
"stack": string(debug.Stack()),
})
w.WriteHeader(500)
}
}()
endpointHandle(w, Request{
HTTP: request.HTTP,
Parameters: request.Parameters,
UserData: userData,
})
elapsed := time.Since(start)
if !options.DontLogRequests {
log.PWrite(h.server.Options.RequestLogLevel, "HTTP Request", map[string]interface{}{
"remote_addr": RealRemoteAddr(request.HTTP),
"method": request.HTTP.Method,
"url": request.HTTP.URL,
"elapsed": elapsed.String(),
})
}
}
}