|
3
|
1 // Copyright 2012 Junqing Tan <ivan@mysqlab.net> and The Go Authors
|
|
|
2 // Use of this source code is governed by a BSD-style
|
|
|
3 // Part of source code is from Go fcgi package
|
|
|
4
|
|
0
|
5 package fcgi
|
|
|
6
|
|
|
7 import (
|
|
|
8 "bufio"
|
|
3
|
9 "bytes"
|
|
0
|
10 "encoding/binary"
|
|
3
|
11 "errors"
|
|
|
12 "fmt"
|
|
0
|
13 "io"
|
|
3
|
14 "io/ioutil"
|
|
|
15 "mime/multipart"
|
|
|
16 "net"
|
|
0
|
17 "net/http"
|
|
3
|
18 "net/http/httputil"
|
|
0
|
19 "net/textproto"
|
|
3
|
20 "net/url"
|
|
|
21 "os"
|
|
|
22 "path/filepath"
|
|
|
23 "strconv"
|
|
0
|
24 "strings"
|
|
3
|
25 "sync"
|
|
|
26 "time"
|
|
0
|
27 )
|
|
|
28
|
|
3
|
29 const FCGI_LISTENSOCK_FILENO uint8 = 0
|
|
|
30 const FCGI_HEADER_LEN uint8 = 8
|
|
|
31 const VERSION_1 uint8 = 1
|
|
|
32 const FCGI_NULL_REQUEST_ID uint8 = 0
|
|
|
33 const FCGI_KEEP_CONN uint8 = 1
|
|
|
34 const doubleCRLF = "\r\n\r\n"
|
|
|
35
|
|
|
36 const (
|
|
|
37 FCGI_BEGIN_REQUEST uint8 = iota + 1
|
|
|
38 FCGI_ABORT_REQUEST
|
|
|
39 FCGI_END_REQUEST
|
|
|
40 FCGI_PARAMS
|
|
|
41 FCGI_STDIN
|
|
|
42 FCGI_STDOUT
|
|
|
43 FCGI_STDERR
|
|
|
44 FCGI_DATA
|
|
|
45 FCGI_GET_VALUES
|
|
|
46 FCGI_GET_VALUES_RESULT
|
|
|
47 FCGI_UNKNOWN_TYPE
|
|
|
48 FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
|
|
|
49 )
|
|
|
50
|
|
|
51 const (
|
|
|
52 FCGI_RESPONDER uint8 = iota + 1
|
|
|
53 FCGI_AUTHORIZER
|
|
|
54 FCGI_FILTER
|
|
|
55 )
|
|
|
56
|
|
|
57 const (
|
|
|
58 FCGI_REQUEST_COMPLETE uint8 = iota
|
|
|
59 FCGI_CANT_MPX_CONN
|
|
|
60 FCGI_OVERLOADED
|
|
|
61 FCGI_UNKNOWN_ROLE
|
|
|
62 )
|
|
|
63
|
|
|
64 const (
|
|
|
65 FCGI_MAX_CONNS string = "MAX_CONNS"
|
|
|
66 FCGI_MAX_REQS string = "MAX_REQS"
|
|
|
67 FCGI_MPXS_CONNS string = "MPXS_CONNS"
|
|
|
68 )
|
|
0
|
69
|
|
|
70 const (
|
|
3
|
71 maxWrite = 65500 // 65530 may work, but for compatibility
|
|
|
72 maxPad = 255
|
|
0
|
73 )
|
|
|
74
|
|
3
|
75 type header struct {
|
|
|
76 Version uint8
|
|
|
77 Type uint8
|
|
|
78 Id uint16
|
|
|
79 ContentLength uint16
|
|
|
80 PaddingLength uint8
|
|
|
81 Reserved uint8
|
|
|
82 }
|
|
|
83
|
|
|
84 // for padding so we don't have to allocate all the time
|
|
|
85 // not synchronized because we don't care what the contents are
|
|
|
86 var pad [maxPad]byte
|
|
|
87
|
|
|
88 func (h *header) init(recType uint8, reqId uint16, contentLength int) {
|
|
|
89 h.Version = 1
|
|
|
90 h.Type = recType
|
|
|
91 h.Id = reqId
|
|
|
92 h.ContentLength = uint16(contentLength)
|
|
|
93 h.PaddingLength = uint8(-contentLength & 7)
|
|
|
94 }
|
|
|
95
|
|
|
96 type record struct {
|
|
|
97 h header
|
|
|
98 rbuf []byte
|
|
|
99 }
|
|
0
|
100
|
|
3
|
101 func (rec *record) read(r io.Reader) (buf []byte, err error) {
|
|
|
102 if err = binary.Read(r, binary.BigEndian, &rec.h); err != nil {
|
|
|
103 return
|
|
|
104 }
|
|
|
105 if rec.h.Version != 1 {
|
|
|
106 err = errors.New("fcgi: invalid header version")
|
|
|
107 return
|
|
|
108 }
|
|
|
109 if rec.h.Type == FCGI_END_REQUEST {
|
|
|
110 err = io.EOF
|
|
|
111 return
|
|
|
112 }
|
|
|
113 n := int(rec.h.ContentLength) + int(rec.h.PaddingLength)
|
|
|
114 if len(rec.rbuf) < n {
|
|
|
115 rec.rbuf = make([]byte, n)
|
|
|
116 }
|
|
|
117 if n, err = io.ReadFull(r, rec.rbuf[:n]); err != nil {
|
|
|
118 return
|
|
|
119 }
|
|
|
120 buf = rec.rbuf[:int(rec.h.ContentLength)]
|
|
|
121
|
|
|
122 return
|
|
|
123 }
|
|
|
124
|
|
|
125 type FCGIClient struct {
|
|
|
126 mutex sync.Mutex
|
|
|
127 rwc io.ReadWriteCloser
|
|
|
128 h header
|
|
|
129 buf bytes.Buffer
|
|
|
130 keepAlive bool
|
|
|
131 reqId uint16
|
|
|
132 }
|
|
|
133
|
|
|
134 // Connects to the fcgi responder at the specified network address.
|
|
|
135 // See func net.Dial for a description of the network and address parameters.
|
|
|
136 func Dial(network, address string) (fcgi *FCGIClient, err error) {
|
|
|
137 var conn net.Conn
|
|
|
138
|
|
|
139 conn, err = net.Dial(network, address)
|
|
|
140 if err != nil {
|
|
|
141 return
|
|
0
|
142 }
|
|
|
143
|
|
3
|
144 fcgi = &FCGIClient{
|
|
|
145 rwc: conn,
|
|
|
146 keepAlive: false,
|
|
|
147 reqId: 1,
|
|
0
|
148 }
|
|
3
|
149
|
|
|
150 return
|
|
|
151 }
|
|
|
152
|
|
|
153 // Connects to the fcgi responder at the specified network address with timeout
|
|
|
154 // See func net.DialTimeout for a description of the network, address and timeout parameters.
|
|
|
155 func DialTimeout(network, address string, timeout time.Duration) (fcgi *FCGIClient, err error) {
|
|
|
156
|
|
|
157 var conn net.Conn
|
|
|
158
|
|
|
159 conn, err = net.DialTimeout(network, address, timeout)
|
|
|
160 if err != nil {
|
|
|
161 return
|
|
|
162 }
|
|
|
163
|
|
|
164 fcgi = &FCGIClient{
|
|
|
165 rwc: conn,
|
|
|
166 keepAlive: false,
|
|
|
167 reqId: 1,
|
|
0
|
168 }
|
|
|
169
|
|
3
|
170 return
|
|
|
171 }
|
|
|
172
|
|
|
173 // Close fcgi connnection
|
|
|
174 func (this *FCGIClient) Close() {
|
|
|
175 this.rwc.Close()
|
|
|
176 }
|
|
|
177
|
|
|
178 func (this *FCGIClient) writeRecord(recType uint8, content []byte) (err error) {
|
|
|
179 this.mutex.Lock()
|
|
|
180 defer this.mutex.Unlock()
|
|
|
181 this.buf.Reset()
|
|
|
182 this.h.init(recType, this.reqId, len(content))
|
|
|
183 if err := binary.Write(&this.buf, binary.BigEndian, this.h); err != nil {
|
|
|
184 return err
|
|
0
|
185 }
|
|
3
|
186 if _, err := this.buf.Write(content); err != nil {
|
|
|
187 return err
|
|
|
188 }
|
|
|
189 if _, err := this.buf.Write(pad[:this.h.PaddingLength]); err != nil {
|
|
0
|
190 return err
|
|
|
191 }
|
|
3
|
192 _, err = this.rwc.Write(this.buf.Bytes())
|
|
|
193 return err
|
|
|
194 }
|
|
|
195
|
|
|
196 func (this *FCGIClient) writeBeginRequest(role uint16, flags uint8) error {
|
|
|
197 b := [8]byte{byte(role >> 8), byte(role), flags}
|
|
|
198 return this.writeRecord(FCGI_BEGIN_REQUEST, b[:])
|
|
|
199 }
|
|
|
200
|
|
|
201 func (this *FCGIClient) writeEndRequest(appStatus int, protocolStatus uint8) error {
|
|
|
202 b := make([]byte, 8)
|
|
|
203 binary.BigEndian.PutUint32(b, uint32(appStatus))
|
|
|
204 b[4] = protocolStatus
|
|
|
205 return this.writeRecord(FCGI_END_REQUEST, b)
|
|
|
206 }
|
|
|
207
|
|
|
208 func (this *FCGIClient) writePairs(recType uint8, pairs map[string]string) error {
|
|
|
209 w := newWriter(this, recType)
|
|
|
210 b := make([]byte, 8)
|
|
|
211 nn := 0
|
|
|
212 for k, v := range pairs {
|
|
|
213 m := 8 + len(k) + len(v)
|
|
|
214 if m > maxWrite {
|
|
|
215 // param data size exceed 65535 bytes"
|
|
|
216 vl := maxWrite - 8 - len(k)
|
|
|
217 v = v[:vl]
|
|
|
218 }
|
|
|
219 n := encodeSize(b, uint32(len(k)))
|
|
|
220 n += encodeSize(b[n:], uint32(len(v)))
|
|
|
221 m = n + len(k) + len(v)
|
|
|
222 if (nn + m) > maxWrite {
|
|
|
223 w.Flush()
|
|
|
224 nn = 0
|
|
|
225 }
|
|
|
226 nn += m
|
|
|
227 if _, err := w.Write(b[:n]); err != nil {
|
|
|
228 return err
|
|
|
229 }
|
|
|
230 if _, err := w.WriteString(k); err != nil {
|
|
|
231 return err
|
|
|
232 }
|
|
|
233 if _, err := w.WriteString(v); err != nil {
|
|
|
234 return err
|
|
|
235 }
|
|
|
236 }
|
|
|
237 w.Close()
|
|
|
238 return nil
|
|
|
239 }
|
|
|
240
|
|
|
241 func readSize(s []byte) (uint32, int) {
|
|
|
242 if len(s) == 0 {
|
|
|
243 return 0, 0
|
|
|
244 }
|
|
|
245 size, n := uint32(s[0]), 1
|
|
|
246 if size&(1<<7) != 0 {
|
|
|
247 if len(s) < 4 {
|
|
|
248 return 0, 0
|
|
|
249 }
|
|
|
250 n = 4
|
|
|
251 size = binary.BigEndian.Uint32(s)
|
|
|
252 size &^= 1 << 31
|
|
|
253 }
|
|
|
254 return size, n
|
|
|
255 }
|
|
|
256
|
|
|
257 func readString(s []byte, size uint32) string {
|
|
|
258 if size > uint32(len(s)) {
|
|
|
259 return ""
|
|
|
260 }
|
|
|
261 return string(s[:size])
|
|
|
262 }
|
|
|
263
|
|
|
264 func encodeSize(b []byte, size uint32) int {
|
|
|
265 if size > 127 {
|
|
|
266 size |= 1 << 31
|
|
|
267 binary.BigEndian.PutUint32(b, size)
|
|
|
268 return 4
|
|
|
269 }
|
|
|
270 b[0] = byte(size)
|
|
|
271 return 1
|
|
|
272 }
|
|
|
273
|
|
|
274 // bufWriter encapsulates bufio.Writer but also closes the underlying stream when
|
|
|
275 // Closed.
|
|
|
276 type bufWriter struct {
|
|
|
277 closer io.Closer
|
|
|
278 *bufio.Writer
|
|
|
279 }
|
|
|
280
|
|
|
281 func (w *bufWriter) Close() error {
|
|
|
282 if err := w.Writer.Flush(); err != nil {
|
|
|
283 w.closer.Close()
|
|
0
|
284 return err
|
|
|
285 }
|
|
3
|
286 return w.closer.Close()
|
|
|
287 }
|
|
0
|
288
|
|
3
|
289 func newWriter(c *FCGIClient, recType uint8) *bufWriter {
|
|
|
290 s := &streamWriter{c: c, recType: recType}
|
|
|
291 w := bufio.NewWriterSize(s, maxWrite)
|
|
|
292 return &bufWriter{s, w}
|
|
|
293 }
|
|
0
|
294
|
|
3
|
295 // streamWriter abstracts out the separation of a stream into discrete records.
|
|
|
296 // It only writes maxWrite bytes at a time.
|
|
|
297 type streamWriter struct {
|
|
|
298 c *FCGIClient
|
|
|
299 recType uint8
|
|
|
300 }
|
|
|
301
|
|
|
302 func (w *streamWriter) Write(p []byte) (int, error) {
|
|
|
303 nn := 0
|
|
|
304 for len(p) > 0 {
|
|
|
305 n := len(p)
|
|
|
306 if n > maxWrite {
|
|
|
307 n = maxWrite
|
|
0
|
308 }
|
|
3
|
309 if err := w.c.writeRecord(w.recType, p[:n]); err != nil {
|
|
|
310 return nn, err
|
|
|
311 }
|
|
|
312 nn += n
|
|
|
313 p = p[n:]
|
|
|
314 }
|
|
|
315 return nn, nil
|
|
|
316 }
|
|
|
317
|
|
|
318 func (w *streamWriter) Close() error {
|
|
|
319 // send empty record to close the stream
|
|
|
320 return w.c.writeRecord(w.recType, nil)
|
|
|
321 }
|
|
0
|
322
|
|
3
|
323 type streamReader struct {
|
|
|
324 c *FCGIClient
|
|
|
325 buf []byte
|
|
|
326 }
|
|
|
327
|
|
|
328 func (w *streamReader) Read(p []byte) (n int, err error) {
|
|
|
329
|
|
|
330 if len(p) > 0 {
|
|
|
331 if len(w.buf) == 0 {
|
|
|
332 rec := &record{}
|
|
|
333 w.buf, err = rec.read(w.c.rwc)
|
|
|
334 if err != nil {
|
|
|
335 return
|
|
0
|
336 }
|
|
|
337 }
|
|
|
338
|
|
3
|
339 n = len(p)
|
|
|
340 if n > len(w.buf) {
|
|
|
341 n = len(w.buf)
|
|
0
|
342 }
|
|
3
|
343 copy(p, w.buf[:n])
|
|
|
344 w.buf = w.buf[n:]
|
|
0
|
345 }
|
|
3
|
346
|
|
|
347 return
|
|
0
|
348 }
|
|
|
349
|
|
3
|
350 // Do made the request and returns a io.Reader that translates the data read
|
|
|
351 // from fcgi responder out of fcgi packet before returning it.
|
|
|
352 func (this *FCGIClient) Do(p map[string]string, req io.Reader) (r io.Reader, err error) {
|
|
|
353 err = this.writeBeginRequest(uint16(FCGI_RESPONDER), 0)
|
|
|
354 if err != nil {
|
|
|
355 return
|
|
0
|
356 }
|
|
|
357
|
|
3
|
358 err = this.writePairs(FCGI_PARAMS, p)
|
|
|
359 if err != nil {
|
|
|
360 return
|
|
0
|
361 }
|
|
|
362
|
|
3
|
363 body := newWriter(this, FCGI_STDIN)
|
|
|
364 if req != nil {
|
|
|
365 io.Copy(body, req)
|
|
0
|
366 }
|
|
3
|
367 body.Close()
|
|
|
368
|
|
|
369 r = &streamReader{c: this}
|
|
|
370 return
|
|
|
371 }
|
|
|
372
|
|
|
373 type badStringError struct {
|
|
|
374 what string
|
|
|
375 str string
|
|
0
|
376 }
|
|
|
377
|
|
3
|
378 func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) }
|
|
|
379
|
|
|
380 // Request returns a HTTP Response with Header and Body
|
|
|
381 // from fcgi responder
|
|
|
382 func (this *FCGIClient) Request(p map[string]string, req io.Reader) (resp *http.Response, err error) {
|
|
|
383
|
|
|
384 r, err := this.Do(p, req)
|
|
|
385 if err != nil {
|
|
|
386 return
|
|
0
|
387 }
|
|
3
|
388
|
|
|
389 rb := bufio.NewReader(r)
|
|
|
390 tp := textproto.NewReader(rb)
|
|
|
391 resp = new(http.Response)
|
|
|
392 // Parse the first line of the response.
|
|
|
393 line, err := tp.ReadLine()
|
|
|
394 if err != nil {
|
|
|
395 if err == io.EOF {
|
|
|
396 err = io.ErrUnexpectedEOF
|
|
|
397 }
|
|
|
398 return nil, err
|
|
|
399 }
|
|
|
400 if i := strings.IndexByte(line, ' '); i == -1 {
|
|
|
401 err = &badStringError{"malformed HTTP response", line}
|
|
|
402 } else {
|
|
|
403 resp.Proto = line[:i]
|
|
|
404 resp.Status = strings.TrimLeft(line[i+1:], " ")
|
|
0
|
405 }
|
|
3
|
406 statusCode := resp.Status
|
|
|
407 if i := strings.IndexByte(resp.Status, ' '); i != -1 {
|
|
|
408 statusCode = resp.Status[:i]
|
|
|
409 }
|
|
|
410 if len(statusCode) != 3 {
|
|
|
411 err = &badStringError{"malformed HTTP status code", statusCode}
|
|
|
412 }
|
|
|
413 resp.StatusCode, err = strconv.Atoi(statusCode)
|
|
|
414 if err != nil || resp.StatusCode < 0 {
|
|
|
415 err = &badStringError{"malformed HTTP status code", statusCode}
|
|
|
416 }
|
|
|
417 var ok bool
|
|
|
418 if resp.ProtoMajor, resp.ProtoMinor, ok = http.ParseHTTPVersion(resp.Proto); !ok {
|
|
|
419 err = &badStringError{"malformed HTTP version", resp.Proto}
|
|
0
|
420 }
|
|
3
|
421 // Parse the response headers.
|
|
|
422 mimeHeader, err := tp.ReadMIMEHeader()
|
|
|
423 if err != nil {
|
|
|
424 if err == io.EOF {
|
|
|
425 err = io.ErrUnexpectedEOF
|
|
0
|
426 }
|
|
3
|
427 return nil, err
|
|
0
|
428 }
|
|
3
|
429 resp.Header = http.Header(mimeHeader)
|
|
|
430 // TODO: fixTransferEncoding ?
|
|
|
431 resp.TransferEncoding = resp.Header["Transfer-Encoding"]
|
|
|
432 resp.ContentLength, _ = strconv.ParseInt(resp.Header.Get("Content-Length"), 10, 64)
|
|
|
433
|
|
|
434 if chunked(resp.TransferEncoding) {
|
|
|
435 resp.Body = ioutil.NopCloser(httputil.NewChunkedReader(rb))
|
|
|
436 } else {
|
|
|
437 resp.Body = ioutil.NopCloser(rb)
|
|
|
438 }
|
|
|
439 return
|
|
0
|
440 }
|
|
|
441
|
|
3
|
442 // Get issues a GET request to the fcgi responder.
|
|
|
443 func (this *FCGIClient) Get(p map[string]string) (resp *http.Response, err error) {
|
|
|
444
|
|
|
445 p["REQUEST_METHOD"] = "GET"
|
|
|
446 p["CONTENT_LENGTH"] = "0"
|
|
|
447
|
|
|
448 return this.Request(p, nil)
|
|
|
449 }
|
|
|
450
|
|
|
451 // Get issues a Post request to the fcgi responder. with request body
|
|
|
452 // in the format that bodyType specified
|
|
|
453 func (this *FCGIClient) Post(p map[string]string, bodyType string, body io.Reader, l int) (resp *http.Response, err error) {
|
|
|
454
|
|
|
455 if len(p["REQUEST_METHOD"]) == 0 || p["REQUEST_METHOD"] == "GET" {
|
|
|
456 p["REQUEST_METHOD"] = "POST"
|
|
0
|
457 }
|
|
3
|
458 p["CONTENT_LENGTH"] = strconv.Itoa(l)
|
|
|
459 if len(bodyType) > 0 {
|
|
|
460 p["CONTENT_TYPE"] = bodyType
|
|
|
461 } else {
|
|
|
462 p["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
|
|
463 }
|
|
|
464
|
|
|
465 return this.Request(p, body)
|
|
|
466 }
|
|
|
467
|
|
|
468 // PostForm issues a POST to the fcgi responder, with form
|
|
|
469 // as a string key to a list values (url.Values)
|
|
|
470 func (this *FCGIClient) PostForm(p map[string]string, data url.Values) (resp *http.Response, err error) {
|
|
|
471 body := bytes.NewReader([]byte(data.Encode()))
|
|
|
472 return this.Post(p, "application/x-www-form-urlencoded", body, body.Len())
|
|
0
|
473 }
|
|
|
474
|
|
3
|
475 // PostFile issues a POST to the fcgi responder in multipart(RFC 2046) standard,
|
|
|
476 // with form as a string key to a list values (url.Values),
|
|
|
477 // and/or with file as a string key to a list file path.
|
|
|
478 func (this *FCGIClient) PostFile(p map[string]string, data url.Values, file map[string]string) (resp *http.Response, err error) {
|
|
|
479 buf := &bytes.Buffer{}
|
|
|
480 writer := multipart.NewWriter(buf)
|
|
|
481 bodyType := writer.FormDataContentType()
|
|
|
482
|
|
|
483 for key, val := range data {
|
|
|
484 for _, v0 := range val {
|
|
|
485 err = writer.WriteField(key, v0)
|
|
|
486 if err != nil {
|
|
|
487 return
|
|
|
488 }
|
|
|
489 }
|
|
0
|
490 }
|
|
3
|
491
|
|
|
492 for key, val := range file {
|
|
|
493 fd, e := os.Open(val)
|
|
|
494 if e != nil {
|
|
|
495 return nil, e
|
|
|
496 }
|
|
|
497 defer fd.Close()
|
|
|
498
|
|
|
499 part, e := writer.CreateFormFile(key, filepath.Base(val))
|
|
|
500 if e != nil {
|
|
|
501 return nil, e
|
|
|
502 }
|
|
|
503 _, err = io.Copy(part, fd)
|
|
|
504 }
|
|
|
505
|
|
|
506 err = writer.Close()
|
|
|
507 if err != nil {
|
|
|
508 return
|
|
|
509 }
|
|
|
510
|
|
|
511 return this.Post(p, bodyType, buf, buf.Len())
|
|
0
|
512 }
|
|
|
513
|
|
3
|
514 // Checks whether chunked is part of the encodings stack
|
|
|
515 func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" } |