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
+
5
+ // Fix bug: Can't recive more than 1 record untill FCGI_END_REQUEST 2012-09-15
6
+ // By: wofeiwo
7
+
8
+ package fcgiclient
9
+
10
+ import (
11
+ "bufio"
12
+ "bytes"
13
+ "encoding/binary"
14
+ "errors"
15
+ "io"
16
+ "net"
17
+ "strconv"
18
+ "sync"
19
+ )
20
+
21
+ const FCGI_LISTENSOCK_FILENO uint8 = 0
22
+ const FCGI_HEADER_LEN uint8 = 8
23
+ const VERSION_1 uint8 = 1
24
+ const FCGI_NULL_REQUEST_ID uint8 = 0
25
+ const FCGI_KEEP_CONN uint8 = 1
26
+
27
+ const (
28
+ FCGI_BEGIN_REQUEST uint8 = iota + 1
29
+ FCGI_ABORT_REQUEST
30
+ FCGI_END_REQUEST
31
+ FCGI_PARAMS
32
+ FCGI_STDIN
33
+ FCGI_STDOUT
34
+ FCGI_STDERR
35
+ FCGI_DATA
36
+ FCGI_GET_VALUES
37
+ FCGI_GET_VALUES_RESULT
38
+ FCGI_UNKNOWN_TYPE
39
+ FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
40
+ )
41
+
42
+ const (
43
+ FCGI_RESPONDER uint8 = iota + 1
44
+ FCGI_AUTHORIZER
45
+ FCGI_FILTER
46
+ )
47
+
48
+ const (
49
+ FCGI_REQUEST_COMPLETE uint8 = iota
50
+ FCGI_CANT_MPX_CONN
51
+ FCGI_OVERLOADED
52
+ FCGI_UNKNOWN_ROLE
53
+ )
54
+
55
+ const (
56
+ FCGI_MAX_CONNS string = "MAX_CONNS"
57
+ FCGI_MAX_REQS string = "MAX_REQS"
58
+ FCGI_MPXS_CONNS string = "MPXS_CONNS"
59
+ )
60
+
61
+ const (
62
+ maxWrite = 6553500 // maximum record body
63
+ maxPad = 255
64
+ )
65
+
66
+ type header struct {
67
+ Version uint8
68
+ Type uint8
69
+ Id uint16
70
+ ContentLength uint16
71
+ PaddingLength uint8
72
+ Reserved uint8
73
+ }
74
+
75
+ // for padding so we don't have to allocate all the time
76
+ // not synchronized because we don't care what the contents are
77
+ var pad [maxPad ]byte
78
+
79
+ func (h * header ) init (recType uint8 , reqId uint16 , contentLength int ) {
80
+ h .Version = 1
81
+ h .Type = recType
82
+ h .Id = reqId
83
+ h .ContentLength = uint16 (contentLength )
84
+ h .PaddingLength = uint8 (- contentLength & 7 )
85
+ }
86
+
87
+ type record struct {
88
+ h header
89
+ buf [maxWrite + maxPad ]byte
90
+ }
91
+
92
+ func (rec * record ) read (r io.Reader ) (err error ) {
93
+ if err = binary .Read (r , binary .BigEndian , & rec .h ); err != nil {
94
+ return err
95
+ }
96
+ if rec .h .Version != 1 {
97
+ return errors .New ("fcgi: invalid header version" )
98
+ }
99
+ n := int (rec .h .ContentLength ) + int (rec .h .PaddingLength )
100
+ if _ , err = io .ReadFull (r , rec .buf [:n ]); err != nil {
101
+ return err
102
+ }
103
+ return nil
104
+ }
105
+
106
+ func (r * record ) content () []byte {
107
+ return r .buf [:r .h .ContentLength ]
108
+ }
109
+
110
+ type FCGIClient struct {
111
+ mutex sync.Mutex
112
+ rwc io.ReadWriteCloser
113
+ h header
114
+ buf bytes.Buffer
115
+ keepAlive bool
116
+ }
117
+
118
+ func New (h string , args ... interface {}) (fcgi * FCGIClient , err error ) {
119
+ var conn net.Conn
120
+ if len (args ) != 1 {
121
+ err = errors .New ("fcgi: not enough params" )
122
+ return
123
+ }
124
+ switch args [0 ].(type ) {
125
+ case int :
126
+ addr := h + ":" + strconv .FormatInt (int64 (args [0 ].(int )), 10 )
127
+ conn , err = net .Dial ("tcp" , addr )
128
+ case string :
129
+ addr := h + ":" + args [0 ].(string )
130
+ conn , err = net .Dial ("unix" , addr )
131
+ default :
132
+ err = errors .New ("fcgi: we only accept int (port) or string (socket) params." )
133
+ }
134
+ fcgi = & FCGIClient {
135
+ rwc : conn ,
136
+ keepAlive : false ,
137
+ }
138
+ return
139
+ }
140
+
141
+ func (this * FCGIClient ) writeRecord (recType uint8 , reqId uint16 , content []byte ) (err error ) {
142
+ this .mutex .Lock ()
143
+ defer this .mutex .Unlock ()
144
+ this .buf .Reset ()
145
+ this .h .init (recType , reqId , len (content ))
146
+ if err := binary .Write (& this .buf , binary .BigEndian , this .h ); err != nil {
147
+ return err
148
+ }
149
+ if _ , err := this .buf .Write (content ); err != nil {
150
+ return err
151
+ }
152
+ if _ , err := this .buf .Write (pad [:this .h .PaddingLength ]); err != nil {
153
+ return err
154
+ }
155
+ _ , err = this .rwc .Write (this .buf .Bytes ())
156
+ return err
157
+ }
158
+
159
+ func (this * FCGIClient ) writeBeginRequest (reqId uint16 , role uint16 , flags uint8 ) error {
160
+ b := [8 ]byte {byte (role >> 8 ), byte (role ), flags }
161
+ return this .writeRecord (FCGI_BEGIN_REQUEST , reqId , b [:])
162
+ }
163
+
164
+ func (this * FCGIClient ) writeEndRequest (reqId uint16 , appStatus int , protocolStatus uint8 ) error {
165
+ b := make ([]byte , 8 )
166
+ binary .BigEndian .PutUint32 (b , uint32 (appStatus ))
167
+ b [4 ] = protocolStatus
168
+ return this .writeRecord (FCGI_END_REQUEST , reqId , b )
169
+ }
170
+
171
+ func (this * FCGIClient ) writePairs (recType uint8 , reqId uint16 , pairs map [string ]string ) error {
172
+ w := newWriter (this , recType , reqId )
173
+ b := make ([]byte , 8 )
174
+ for k , v := range pairs {
175
+ n := encodeSize (b , uint32 (len (k )))
176
+ n += encodeSize (b [n :], uint32 (len (v )))
177
+ if _ , err := w .Write (b [:n ]); err != nil {
178
+ return err
179
+ }
180
+ if _ , err := w .WriteString (k ); err != nil {
181
+ return err
182
+ }
183
+ if _ , err := w .WriteString (v ); err != nil {
184
+ return err
185
+ }
186
+ }
187
+ w .Close ()
188
+ return nil
189
+ }
190
+
191
+ func readSize (s []byte ) (uint32 , int ) {
192
+ if len (s ) == 0 {
193
+ return 0 , 0
194
+ }
195
+ size , n := uint32 (s [0 ]), 1
196
+ if size & (1 << 7 ) != 0 {
197
+ if len (s ) < 4 {
198
+ return 0 , 0
199
+ }
200
+ n = 4
201
+ size = binary .BigEndian .Uint32 (s )
202
+ size &^= 1 << 31
203
+ }
204
+ return size , n
205
+ }
206
+
207
+ func readString (s []byte , size uint32 ) string {
208
+ if size > uint32 (len (s )) {
209
+ return ""
210
+ }
211
+ return string (s [:size ])
212
+ }
213
+
214
+ func encodeSize (b []byte , size uint32 ) int {
215
+ if size > 127 {
216
+ size |= 1 << 31
217
+ binary .BigEndian .PutUint32 (b , size )
218
+ return 4
219
+ }
220
+ b [0 ] = byte (size )
221
+ return 1
222
+ }
223
+
224
+ // bufWriter encapsulates bufio.Writer but also closes the underlying stream when
225
+ // Closed.
226
+ type bufWriter struct {
227
+ closer io.Closer
228
+ * bufio.Writer
229
+ }
230
+
231
+ func (w * bufWriter ) Close () error {
232
+ if err := w .Writer .Flush (); err != nil {
233
+ w .closer .Close ()
234
+ return err
235
+ }
236
+ return w .closer .Close ()
237
+ }
238
+
239
+ func newWriter (c * FCGIClient , recType uint8 , reqId uint16 ) * bufWriter {
240
+ s := & streamWriter {c : c , recType : recType , reqId : reqId }
241
+ w := bufio .NewWriterSize (s , maxWrite )
242
+ return & bufWriter {s , w }
243
+ }
244
+
245
+ // streamWriter abstracts out the separation of a stream into discrete records.
246
+ // It only writes maxWrite bytes at a time.
247
+ type streamWriter struct {
248
+ c * FCGIClient
249
+ recType uint8
250
+ reqId uint16
251
+ }
252
+
253
+ func (w * streamWriter ) Write (p []byte ) (int , error ) {
254
+ nn := 0
255
+ for len (p ) > 0 {
256
+ n := len (p )
257
+ if n > maxWrite {
258
+ n = maxWrite
259
+ }
260
+ if err := w .c .writeRecord (w .recType , w .reqId , p [:n ]); err != nil {
261
+ return nn , err
262
+ }
263
+ nn += n
264
+ p = p [n :]
265
+ }
266
+ return nn , nil
267
+ }
268
+
269
+ func (w * streamWriter ) Close () error {
270
+ // send empty record to close the stream
271
+ return w .c .writeRecord (w .recType , w .reqId , nil )
272
+ }
273
+
274
+ func (this * FCGIClient ) Request (env map [string ]string , reqStr string ) (retout []byte , reterr []byte , err error ) {
275
+
276
+ var reqId uint16 = 1
277
+ defer this .rwc .Close ()
278
+
279
+ err = this .writeBeginRequest (reqId , uint16 (FCGI_RESPONDER ), 0 )
280
+ if err != nil {
281
+ return
282
+ }
283
+ err = this .writePairs (FCGI_PARAMS , reqId , env )
284
+ if err != nil {
285
+ return
286
+ }
287
+ if len (reqStr ) > 0 {
288
+ err = this .writeRecord (FCGI_STDIN , reqId , []byte (reqStr ))
289
+ if err != nil {
290
+ return
291
+ }
292
+ }
293
+
294
+ rec := & record {}
295
+ var err1 error
296
+
297
+ // recive untill EOF or FCGI_END_REQUEST
298
+ for {
299
+ err1 = rec .read (this .rwc )
300
+ if err1 != nil {
301
+ if err1 != io .EOF {
302
+ err = err1
303
+ }
304
+ break
305
+ }
306
+ switch {
307
+ case rec .h .Type == FCGI_STDOUT :
308
+ retout = append (retout , rec .content ()... )
309
+ case rec .h .Type == FCGI_STDERR :
310
+ reterr = append (reterr , rec .content ()... )
311
+ case rec .h .Type == FCGI_END_REQUEST :
312
+ fallthrough
313
+ default :
314
+ break
315
+ }
316
+ }
317
+
318
+ return
319
+ }
0 commit comments