This repository has been archived by the owner on Nov 26, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 36
/
application.go
129 lines (110 loc) · 2.68 KB
/
application.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
package main
import (
"log"
"github.com/valyala/fasthttp"
)
// Application is an abstraction of radix-tree, timeline, balancer, and configurations...
type Application struct {
// redirect if tsr is true?
TSRRedirect bool
balancer Balancer
root *node
fallbackType string
FallbackContent []byte
}
// NewApp return a brand new Application
func NewApp(b Balancer, tsr bool) *Application {
return &Application{tsr, b, &node{}, "", []byte("")}
}
func convertMethod(methods ...string) HTTPMethod {
httpMethods := NONE
if len(methods) == 0 {
log.Panicf("at least one method is required")
}
for _, m := range methods {
switch m {
case "GET":
httpMethods |= GET
case "POST":
httpMethods |= POST
case "PUT":
httpMethods |= PUT
case "DELETE":
httpMethods |= DELETE
case "HEAD":
httpMethods |= HEAD
case "OPTIONS":
httpMethods |= OPTIONS
case "CONNECT":
httpMethods |= CONNECT
case "TRACE":
httpMethods |= TRACE
case "PATCH":
httpMethods |= PATCH
default:
log.Panicf("bad http method: %s", m)
}
}
return httpMethods
}
// AddRoute add a route to itself
func (a *Application) AddRoute(path string, methods ...string) {
a.root.addRoute([]byte(path), convertMethod(methods...))
}
func (a *Application) ServeHTTP(ctx *fasthttp.RequestCtx) {
if a.root == nil {
log.Panic("application should bind a URL-tree")
}
if a.balancer == nil {
log.Panic("application should bind a load balancer")
}
path := ctx.Path()
n, tsr, found := a.root.byPath(path)
// redirect?
if tsr && a.TSRRedirect {
code := fasthttp.StatusMovedPermanently
if string(ctx.Method()) != "GET" {
code = fasthttp.StatusTemporaryRedirect
}
var redirectTo []byte
if len(path) > 1 && path[len(path)-1] == '/' {
redirectTo = path[:len(path)-1]
} else {
redirectTo = append(path, '/')
}
log.Printf("redirect to %s", redirectTo)
ctx.RedirectBytes(redirectTo, code)
return
}
// not found
if !found {
ctx.NotFound()
return
}
// method allowed?
if !n.hasMethod(convertMethod(string(ctx.Method()))) {
ctx.SetStatusCode(fasthttp.StatusMethodNotAllowed)
return
}
// circuit breaker is open?
_, _, _, _, ratio := n.query()
if ratio > 0.3 {
// fallback
log.Printf("too many requests, ratio is %f", ratio)
switch a.fallbackType {
case fallbackJSON:
ctx.SetContentType("application/json")
case fallbackHTML, fallbackHTMLFile:
ctx.SetContentType("text/html")
case fallbackTEXT:
ctx.SetContentType("text/plain")
default:
ctx.SetContentType("text/plain")
}
ctx.SetStatusCode(fasthttp.StatusTooManyRequests)
ctx.Write(a.FallbackContent)
return
}
// proxy! and then feedback the result
n.incr(Proxy(a.balancer, ctx))
}