1
2
3
4
5 package fcgi
6
7
8
9 import (
10 "context"
11 "errors"
12 "fmt"
13 "io"
14 "net"
15 "net/http"
16 "net/http/cgi"
17 "os"
18 "strings"
19 "time"
20 )
21
22
23
24 type request struct {
25 pw *io.PipeWriter
26 reqId uint16
27 params map[string]string
28 buf [1024]byte
29 rawParams []byte
30 keepConn bool
31 }
32
33
34
35 type envVarsContextKey struct{}
36
37 func newRequest(reqId uint16, flags uint8) *request {
38 r := &request{
39 reqId: reqId,
40 params: map[string]string{},
41 keepConn: flags&flagKeepConn != 0,
42 }
43 r.rawParams = r.buf[:0]
44 return r
45 }
46
47
48 func (r *request) parseParams() {
49 text := r.rawParams
50 r.rawParams = nil
51 for len(text) > 0 {
52 keyLen, n := readSize(text)
53 if n == 0 {
54 return
55 }
56 text = text[n:]
57 valLen, n := readSize(text)
58 if n == 0 {
59 return
60 }
61 text = text[n:]
62 if int(keyLen)+int(valLen) > len(text) {
63 return
64 }
65 key := readString(text, keyLen)
66 text = text[keyLen:]
67 val := readString(text, valLen)
68 text = text[valLen:]
69 r.params[key] = val
70 }
71 }
72
73
74 type response struct {
75 req *request
76 header http.Header
77 code int
78 wroteHeader bool
79 wroteCGIHeader bool
80 w *bufWriter
81 }
82
83 func newResponse(c *child, req *request) *response {
84 return &response{
85 req: req,
86 header: http.Header{},
87 w: newWriter(c.conn, typeStdout, req.reqId),
88 }
89 }
90
91 func (r *response) Header() http.Header {
92 return r.header
93 }
94
95 func (r *response) Write(p []byte) (n int, err error) {
96 if !r.wroteHeader {
97 r.WriteHeader(http.StatusOK)
98 }
99 if !r.wroteCGIHeader {
100 r.writeCGIHeader(p)
101 }
102 return r.w.Write(p)
103 }
104
105 func (r *response) WriteHeader(code int) {
106 if r.wroteHeader {
107 return
108 }
109 r.wroteHeader = true
110 r.code = code
111 if code == http.StatusNotModified {
112
113 r.header.Del("Content-Type")
114 r.header.Del("Content-Length")
115 r.header.Del("Transfer-Encoding")
116 }
117 if r.header.Get("Date") == "" {
118 r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
119 }
120 }
121
122
123
124
125
126 func (r *response) writeCGIHeader(p []byte) {
127 if r.wroteCGIHeader {
128 return
129 }
130 r.wroteCGIHeader = true
131 fmt.Fprintf(r.w, "Status: %d %s\r\n", r.code, http.StatusText(r.code))
132 if _, hasType := r.header["Content-Type"]; r.code != http.StatusNotModified && !hasType {
133 r.header.Set("Content-Type", http.DetectContentType(p))
134 }
135 r.header.Write(r.w)
136 r.w.WriteString("\r\n")
137 r.w.Flush()
138 }
139
140 func (r *response) Flush() {
141 if !r.wroteHeader {
142 r.WriteHeader(http.StatusOK)
143 }
144 r.w.Flush()
145 }
146
147 func (r *response) Close() error {
148 r.Flush()
149 return r.w.Close()
150 }
151
152 type child struct {
153 conn *conn
154 handler http.Handler
155
156 requests map[uint16]*request
157 }
158
159 func newChild(rwc io.ReadWriteCloser, handler http.Handler) *child {
160 return &child{
161 conn: newConn(rwc),
162 handler: handler,
163 requests: make(map[uint16]*request),
164 }
165 }
166
167 func (c *child) serve() {
168 defer c.conn.Close()
169 defer c.cleanUp()
170 var rec record
171 for {
172 if err := rec.read(c.conn.rwc); err != nil {
173 return
174 }
175 if err := c.handleRecord(&rec); err != nil {
176 return
177 }
178 }
179 }
180
181 var errCloseConn = errors.New("fcgi: connection should be closed")
182
183 var emptyBody = io.NopCloser(strings.NewReader(""))
184
185
186
187 var ErrRequestAborted = errors.New("fcgi: request aborted by web server")
188
189
190
191 var ErrConnClosed = errors.New("fcgi: connection to web server closed")
192
193 func (c *child) handleRecord(rec *record) error {
194 req, ok := c.requests[rec.h.Id]
195 if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
196
197 return nil
198 }
199
200 switch rec.h.Type {
201 case typeBeginRequest:
202 if req != nil {
203
204
205 return errors.New("fcgi: received ID that is already in-flight")
206 }
207
208 var br beginRequest
209 if err := br.read(rec.content()); err != nil {
210 return err
211 }
212 if br.role != roleResponder {
213 c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
214 return nil
215 }
216 req = newRequest(rec.h.Id, br.flags)
217 c.requests[rec.h.Id] = req
218 return nil
219 case typeParams:
220
221
222 if len(rec.content()) > 0 {
223 req.rawParams = append(req.rawParams, rec.content()...)
224 return nil
225 }
226 req.parseParams()
227 return nil
228 case typeStdin:
229 content := rec.content()
230 if req.pw == nil {
231 var body io.ReadCloser
232 if len(content) > 0 {
233
234
235 body, req.pw = io.Pipe()
236 } else {
237 body = emptyBody
238 }
239 go c.serveRequest(req, body)
240 }
241 if len(content) > 0 {
242
243
244 req.pw.Write(content)
245 } else {
246 delete(c.requests, req.reqId)
247 if req.pw != nil {
248 req.pw.Close()
249 }
250 }
251 return nil
252 case typeGetValues:
253 values := map[string]string{"FCGI_MPXS_CONNS": "1"}
254 c.conn.writePairs(typeGetValuesResult, 0, values)
255 return nil
256 case typeData:
257
258 return nil
259 case typeAbortRequest:
260 delete(c.requests, rec.h.Id)
261 c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
262 if req.pw != nil {
263 req.pw.CloseWithError(ErrRequestAborted)
264 }
265 if !req.keepConn {
266
267 return errCloseConn
268 }
269 return nil
270 default:
271 b := make([]byte, 8)
272 b[0] = byte(rec.h.Type)
273 c.conn.writeRecord(typeUnknownType, 0, b)
274 return nil
275 }
276 }
277
278
279
280 func filterOutUsedEnvVars(envVars map[string]string) map[string]string {
281 withoutUsedEnvVars := make(map[string]string)
282 for k, v := range envVars {
283 if addFastCGIEnvToContext(k) {
284 withoutUsedEnvVars[k] = v
285 }
286 }
287 return withoutUsedEnvVars
288 }
289
290 func (c *child) serveRequest(req *request, body io.ReadCloser) {
291 r := newResponse(c, req)
292 httpReq, err := cgi.RequestFromMap(req.params)
293 if err != nil {
294
295 r.WriteHeader(http.StatusInternalServerError)
296 c.conn.writeRecord(typeStderr, req.reqId, []byte(err.Error()))
297 } else {
298 httpReq.Body = body
299 withoutUsedEnvVars := filterOutUsedEnvVars(req.params)
300 envVarCtx := context.WithValue(httpReq.Context(), envVarsContextKey{}, withoutUsedEnvVars)
301 httpReq = httpReq.WithContext(envVarCtx)
302 c.handler.ServeHTTP(r, httpReq)
303 }
304
305 r.Write(nil)
306 r.Close()
307 c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete)
308
309
310
311
312
313
314
315
316 io.CopyN(io.Discard, body, 100<<20)
317 body.Close()
318
319 if !req.keepConn {
320 c.conn.Close()
321 }
322 }
323
324 func (c *child) cleanUp() {
325 for _, req := range c.requests {
326 if req.pw != nil {
327
328
329 req.pw.CloseWithError(ErrConnClosed)
330 }
331 }
332 }
333
334
335
336
337
338
339 func Serve(l net.Listener, handler http.Handler) error {
340 if l == nil {
341 var err error
342 l, err = net.FileListener(os.Stdin)
343 if err != nil {
344 return err
345 }
346 defer l.Close()
347 }
348 if handler == nil {
349 handler = http.DefaultServeMux
350 }
351 for {
352 rw, err := l.Accept()
353 if err != nil {
354 return err
355 }
356 c := newChild(rw, handler)
357 go c.serve()
358 }
359 }
360
361
362
363
364
365
366 func ProcessEnv(r *http.Request) map[string]string {
367 env, _ := r.Context().Value(envVarsContextKey{}).(map[string]string)
368 return env
369 }
370
371
372
373 func addFastCGIEnvToContext(s string) bool {
374
375 switch s {
376 case "CONTENT_LENGTH", "CONTENT_TYPE", "HTTPS",
377 "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR",
378 "REMOTE_HOST", "REMOTE_PORT", "REQUEST_METHOD",
379 "REQUEST_URI", "SCRIPT_NAME", "SERVER_PROTOCOL":
380 return false
381 }
382 if strings.HasPrefix(s, "HTTP_") {
383 return false
384 }
385
386
387
388
389 switch s {
390 case "REMOTE_USER":
391 return true
392 }
393
394 return true
395 }
396
View as plain text