Repository navigation
Expand file tree
/
Copy pathstdlib.go
More file actions
138 lines (125 loc) · 4.2 KB
/
Copy pathstdlib.go
File metadata and controls
138 lines (125 loc) · 4.2 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
134
135
136
137
138
package celeris
import (
"io"
"net/http"
"strconv"
"time"
"github.com/goceleris/celeris/internal/engine/std"
"github.com/goceleris/celeris/internal/protocol/h2/stream"
)
// maxToHandlerBodySize bounds the request body ToHandler buffers. It is not
// Config.MaxRequestBodySize: ToHandler runs under a net/http server, which has
// no celeris Config to read it from, and the cap has no setting of its own, so
// it stays at 100 MB when that default drops to 4 MiB (v1.6.0).
const maxToHandlerBodySize = maxBodySize
// ToHandler wraps a celeris HandlerFunc as an http.Handler for use with
// net/http routers, middleware, or test infrastructure. The returned handler
// converts the *http.Request into a stream.Stream, invokes the celeris
// handler, and writes the response back via http.ResponseWriter.
//
// The handler's [Context.Context] ends with the request's context
// (r.Context()), on HTTP/1 and HTTP/2: a client that goes away, a stream the
// client resets, or an http.TimeoutHandler above ToHandler cancels it.
//
// This is the reverse of [Adapt] / [AdaptFunc] which wrap net/http handlers
// for use inside celeris.
func ToHandler(h HandlerFunc) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
s := stream.NewStream(1)
defer s.Release()
// c.Context() ends with r.Context(): the client leaving, a stream
// the client reset, an http.TimeoutHandler above this handler, or a
// Server.Close that closes the connection (celeris#949). The binding
// is made when the handler first asks for c.Context(), and Release
// ends it (celeris#889).
s.BindParentContext(r.Context())
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
hdrs := make([][2]string, 0, len(r.Header)+4)
hdrs = append(hdrs,
[2]string{":method", r.Method},
[2]string{":path", r.RequestURI},
[2]string{":scheme", scheme},
[2]string{":authority", r.Host},
)
hdrs = std.AppendRequestHeaders(hdrs, r.Header)
s.Headers = hdrs
if r.Body != nil && r.Body != http.NoBody {
// A declared Content-Length over the cap is refused before a byte
// is read, as the std bridge does for MaxRequestBodySize (the
// client may never send it). On HTTP/1 the unread body makes the
// answer say Connection: close, or net/http reads up to 256 KiB
// of it first; not on HTTP/2, where it would end the connection.
if r.ContentLength > int64(maxToHandlerBodySize) {
if r.ProtoMajor == 1 {
w.Header().Set("Connection", "close")
}
http.Error(w, "request body too large", http.StatusRequestEntityTooLarge)
return
}
body, err := io.ReadAll(io.LimitReader(r.Body, int64(maxToHandlerBodySize)+1))
_ = r.Body.Close()
if err != nil {
http.Error(w, "failed to read body", http.StatusBadRequest)
return
}
if len(body) > maxToHandlerBodySize {
http.Error(w, "request body too large", http.StatusRequestEntityTooLarge)
return
}
if len(body) > 0 {
s.SetRawBody(body)
}
}
s.RemoteAddr = r.RemoteAddr
s.SetProtoMajor(uint8(r.ProtoMajor))
s.EndStream = true
s.SetState(stream.StateHalfClosedRemote)
rw := &toStdlibResponseWriter{w: w}
s.ResponseWriter = rw
c := acquireContext(s)
c.startTime = time.Now()
defer func() {
if rv := recover(); rv != nil {
if !rw.flushed {
c.statusCode = 500
w.WriteHeader(500)
_, _ = w.Write([]byte("Internal Server Error"))
}
}
releaseContext(c)
}()
c.handlers = []HandlerFunc{h}
if err := c.Next(); err != nil {
if !rw.flushed {
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}
})
}
// toStdlibResponseWriter adapts http.ResponseWriter to stream.ResponseWriter
// for use in ToHandler.
type toStdlibResponseWriter struct {
w http.ResponseWriter
flushed bool
}
func (rw *toStdlibResponseWriter) WriteResponse(_ *stream.Stream, status int, headers [][2]string, body []byte) error {
h := rw.w.Header()
for _, hdr := range headers {
h.Add(hdr[0], hdr[1])
}
if len(body) > 0 && h.Get("Content-Length") == "" {
h.Set("Content-Length", strconv.Itoa(len(body)))
}
rw.w.WriteHeader(status)
rw.flushed = true
if len(body) > 0 {
_, _ = rw.w.Write(body)
}
if f, ok := rw.w.(http.Flusher); ok {
f.Flush()
}
return nil
}