-
Notifications
You must be signed in to change notification settings - Fork 1
/
handlers.go
112 lines (90 loc) · 3.33 KB
/
handlers.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
package shuttle
import (
"net/http"
"sync"
)
func NewHandler(options ...option) http.Handler {
config := newConfig(options)
if config.LongLivedPoolCapacity == 0 {
return newSemiPersistentHandler(options)
}
return newPersistentHandler(config)
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
type persistentHandler struct {
buffer chan http.Handler
}
func newPersistentHandler(config configuration) http.Handler {
buffer := make(chan http.Handler, config.LongLivedPoolCapacity)
for i := 0; i < config.LongLivedPoolCapacity; i++ {
buffer <- newTransientHandlerFromConfig(config)
}
return &persistentHandler{buffer: buffer}
}
func (this *persistentHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
handler := <-this.buffer
defer func() { this.buffer <- handler }()
handler.ServeHTTP(response, request)
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
type semiPersistentHandler struct {
buffer *sync.Pool
}
func newSemiPersistentHandler(options []option) http.Handler {
buffer := &sync.Pool{New: func() any {
// The config is a "shared nothing" style wherein each handler gets its own configuration values which include
// callbacks to stateful error writers and stateful serializers.
config := newConfig(options)
return newTransientHandlerFromConfig(config)
}}
return &semiPersistentHandler{buffer: buffer}
}
func (this *semiPersistentHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
handler := this.buffer.Get().(http.Handler)
defer this.buffer.Put(handler)
handler.ServeHTTP(response, request)
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
type transientHandler struct {
input InputModel
readers []Reader
processor Processor
writer Writer
monitor Monitor
}
func newTransientHandlerFromConfig(config configuration) http.Handler {
readers := make([]Reader, 0, len(config.Readers))
for _, readerFactory := range config.Readers {
readers = append(readers, readerFactory())
}
return newTransientHandler(config.InputModel(), readers, config.Processor(), config.Writer(), config.Monitor)
}
func newTransientHandler(input InputModel, readers []Reader, processor Processor, writer Writer, monitor Monitor) http.Handler {
monitor.HandlerCreated()
return &transientHandler{
input: input,
readers: readers,
processor: processor,
writer: writer,
monitor: monitor,
}
}
func (this *transientHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
this.monitor.RequestReceived()
result := this.process(request)
this.writer.Write(response, request, result)
}
func (this *transientHandler) process(request *http.Request) any {
this.input.Reset()
for _, reader := range this.readers {
if result := reader.Read(this.input, request); result != nil {
return result
}
}
// FUTURE: if the context is cancelled, don't bother rendering a response
return this.processor.Process(request.Context(), this.input)
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TODO:
// 404 Not Found (JSON/XML/etc. output)
// 405 Method Not Allowed (JSON/XML/etc. output)