-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbase.go
More file actions
133 lines (103 loc) · 2.8 KB
/
base.go
File metadata and controls
133 lines (103 loc) · 2.8 KB
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
package stackable
import (
"io"
"log/slog"
"net/http"
"time"
"github.com/saryginrodion/stackable/utils"
)
type ILocalState interface {
Default
}
type ISharedState any
type Context[S ISharedState, L ILocalState] struct {
Shared *S
Local *L
Response Response
Request *http.Request
}
type Handler[S ISharedState, L ILocalState] interface {
Run(context *Context[S, L], next func() error) error
}
type Stackable[S ISharedState, L ILocalState] struct {
Handlers []Handler[S, L]
Shared *S
logger *slog.Logger
}
func NewStackable[S ISharedState, L ILocalState](s *S) Stackable[S, L] {
stack := Stackable[S, L]{
Shared: s,
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
}
return stack
}
func (s *Stackable[S, L]) SetLogger(logger *slog.Logger) {
s.logger = logger
}
func (s *Stackable[S, L]) AddHandler(handler Handler[S, L]) *Stackable[S, L] {
s.Handlers = append(s.Handlers, handler)
return s
}
func (s Stackable[S, L]) AddUniqueHandler(handler Handler[S, L]) Stackable[S, L] {
newStackable := Stackable[S, L]{
Shared: s.Shared,
logger: s.logger,
}
newStackable.Handlers = make([]Handler[S, L], len(s.Handlers))
copy(newStackable.Handlers, s.Handlers)
newStackable.Handlers = append(newStackable.Handlers, handler)
return newStackable
}
func (s *Stackable[S, L]) HttpHandler() http.HandlerFunc {
return func(response http.ResponseWriter, request *http.Request) {
local := DefaultValue[L]()
var context Context[S, L] = Context[S, L]{
Shared: s.Shared,
Local: &local,
Response: NewHttpResponse(
500,
"text/html",
"Override this response from your handlers!",
),
Request: request,
}
handlerIndex := 0
var next func() error
next = func() error {
if handlerIndex >= len(s.Handlers) {
return nil
}
layer := s.Handlers[handlerIndex]
handlerIndex += 1
return layer.Run(&context, next)
}
err := next()
if err != nil {
s.logger.Error("Stackable: finished with error. Error: "+err.Error(), "err", err)
}
// Writing response to http.ResponseWriter
headers := context.Response.Headers()
for key, values := range (&headers).Entries() {
response.Header().Del(key)
for _, value := range values {
response.Header().Add(key, value)
}
}
response.WriteHeader(context.Response.Status())
_, err = io.Copy(response, context.Response.Body())
if err != nil {
s.logger.Error("Stackable: failed to write response. Error: "+err.Error(), "err", err)
}
}
}
func (s Stackable[S, L]) ServeHTTP(response http.ResponseWriter, request *http.Request) {
startTime := time.Now()
s.HttpHandler()(response, request)
stopTime := time.Now()
elapsedTime := stopTime.Sub(startTime)
s.logger.Debug("Handled request",
"method", request.Method,
"url", request.URL.Path,
"elapsedTime", utils.FormatDuration(elapsedTime),
)
}