ruby-fcgi 0.8.8

Sign up to get free protection for your applications and to get access to all the features.
data/lib/fcgi.rb ADDED
@@ -0,0 +1,618 @@
1
+ =begin
2
+
3
+ fcgi.rb 0.8.5 - fcgi.so compatible pure-ruby FastCGI library
4
+
5
+ fastcgi.rb Copyright (C) 2001 Eli Green
6
+ fcgi.rb Copyright (C) 2002-2003 MoonWolf <moonwolf@moonwolf.com>
7
+ fcgi.rb Copyright (C) 2004 Minero Aoki
8
+
9
+ =end
10
+ trap('SIGTERM') { exit }
11
+ trap('SIGPIPE','IGNORE')
12
+
13
+ begin
14
+ raise LoadError if defined?(FCGI_PURE_RUBY) && FCGI_PURE_RUBY
15
+ require "fcgi.so"
16
+ rescue LoadError
17
+ require 'socket'
18
+ require 'stringio'
19
+
20
+ class FCGI
21
+
22
+ def self::is_cgi?
23
+ begin
24
+ s = Socket.for_fd($stdin.fileno)
25
+ s.getpeername
26
+ false
27
+ rescue Errno::ENOTCONN
28
+ false
29
+ rescue Errno::ENOTSOCK, Errno::EINVAL
30
+ true
31
+ end
32
+ end
33
+
34
+ def self::each(&block)
35
+ f = default_connection()
36
+ Server.new(f).each_request(&block)
37
+ ensure
38
+ f.close if f
39
+ end
40
+
41
+ def self::each_request(&block)
42
+ f = default_connection()
43
+ Server.new(f).each_request(&block)
44
+ ensure
45
+ f.close if f
46
+ end
47
+
48
+ def self::default_connection
49
+ ::Socket.for_fd($stdin.fileno)
50
+ end
51
+
52
+
53
+
54
+ ProtocolVersion = 1
55
+
56
+ # Record types
57
+ FCGI_BEGIN_REQUEST = 1
58
+ FCGI_ABORT_REQUEST = 2
59
+ FCGI_END_REQUEST = 3
60
+ FCGI_PARAMS = 4
61
+ FCGI_STDIN = 5
62
+ FCGI_STDOUT = 6
63
+ FCGI_STDERR = 7
64
+ FCGI_DATA = 8
65
+ FCGI_GET_VALUES = 9
66
+ FCGI_GET_VALUES_RESULT = 10
67
+ FCGI_UNKNOWN_TYPE = 11
68
+ FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
69
+
70
+ FCGI_NULL_REQUEST_ID = 0
71
+
72
+ # FCGI_BEGIN_REQUSET.role
73
+ FCGI_RESPONDER = 1
74
+ FCGI_AUTHORIZER = 2
75
+ FCGI_FILTER = 3
76
+
77
+ # FCGI_BEGIN_REQUEST.flags
78
+ FCGI_KEEP_CONN = 1
79
+
80
+ # FCGI_END_REQUEST.protocolStatus
81
+ FCGI_REQUEST_COMPLETE = 0
82
+ FCGI_CANT_MPX_CONN = 1
83
+ FCGI_OVERLOADED = 2
84
+ FCGI_UNKNOWN_ROLE = 3
85
+
86
+
87
+ class Server
88
+
89
+ def initialize(server)
90
+ @server = server
91
+ @buffers = {}
92
+ @default_parameters = {
93
+ "FCGI_MAX_CONNS" => 1,
94
+ "FCGI_MAX_REQS" => 1,
95
+ "FCGI_MPX_CONNS" => true
96
+ }
97
+ end
98
+
99
+ def each_request(&block)
100
+ graceful = false
101
+ trap("SIGUSR1") { graceful = true }
102
+ while true
103
+ begin
104
+ session(&block)
105
+ rescue Errno::EPIPE, EOFError
106
+ # HTTP request is canceled by the remote user
107
+ end
108
+ exit 0 if graceful
109
+ end
110
+ end
111
+
112
+ def session
113
+ sock, addr = *@server.accept
114
+ return unless sock
115
+ fsock = FastCGISocket.new(sock)
116
+ req = next_request(fsock)
117
+ yield req
118
+ respond_to req, fsock, FCGI_REQUEST_COMPLETE
119
+ ensure
120
+ sock.close if sock and not sock.closed?
121
+ end
122
+
123
+ private
124
+
125
+ def next_request(sock)
126
+ while rec = sock.read_record
127
+ if rec.management_record?
128
+ case rec.type
129
+ when FCGI_GET_VALUES
130
+ sock.send_record handle_GET_VALUES(rec)
131
+ else
132
+ sock.send_record UnknownTypeRecord.new(rec.request_id, rec.type)
133
+ end
134
+ else
135
+ case rec.type
136
+ when FCGI_BEGIN_REQUEST
137
+ @buffers[rec.request_id] = RecordBuffer.new(rec)
138
+ when FCGI_ABORT_REQUEST
139
+ raise "got ABORT_REQUEST" # FIXME
140
+ else
141
+ buf = @buffers[rec.request_id] or next # inactive request
142
+ buf.push rec
143
+ if buf.ready?
144
+ @buffers.delete rec.request_id
145
+ return buf.new_request
146
+ end
147
+ end
148
+ end
149
+ end
150
+ raise "must not happen: FCGI socket unexpected EOF"
151
+ end
152
+
153
+ def handle_GET_VALUES(rec)
154
+ h = {}
155
+ rec.values.each_key do |name|
156
+ h[name] = @default_parameters[name]
157
+ end
158
+ ValuesRecord.new(FCGI_GET_VALUES_RESULT, rec.request_id, h)
159
+ end
160
+
161
+ def respond_to(req, sock, status)
162
+ split_data(FCGI_STDOUT, req.id, req.out) do |rec|
163
+ sock.send_record rec
164
+ end
165
+ split_data(FCGI_STDERR, req.id, req.err) do |rec|
166
+ sock.send_record rec
167
+ end if req.err.length > 0
168
+ sock.send_record EndRequestRecord.new(req.id, 0, status)
169
+ end
170
+
171
+ DATA_UNIT = 16384
172
+
173
+ def split_data(type, id, f)
174
+ unless f.length == 0
175
+ f.rewind
176
+ while s = f.read(DATA_UNIT)
177
+ yield GenericDataRecord.new(type, id, s)
178
+ end
179
+ end
180
+ yield GenericDataRecord.new(type, id, '')
181
+ end
182
+
183
+ end
184
+
185
+
186
+ class FastCGISocket
187
+ def initialize(sock)
188
+ @socket = sock
189
+ end
190
+
191
+ def read_record
192
+ header = @socket.read(Record::HEADER_LENGTH) or return nil
193
+ return nil unless header.size == Record::HEADER_LENGTH
194
+ version, type, reqid, clen, padlen, reserved = *Record.parse_header(header)
195
+ Record.class_for(type).parse(reqid, read_record_body(clen, padlen))
196
+ end
197
+
198
+ def read_record_body(clen, padlen)
199
+ buf = ''
200
+ while buf.length < clen
201
+ buf << @socket.read([1024, clen - buf.length].min)
202
+ end
203
+ @socket.read padlen if padlen
204
+ buf
205
+ end
206
+ private :read_record_body
207
+
208
+ def send_record(rec)
209
+ @socket.write rec.serialize
210
+ @socket.flush
211
+ end
212
+ end
213
+
214
+
215
+ class RecordBuffer
216
+ def initialize(rec)
217
+ @begin_request = rec
218
+ @envs = []
219
+ @stdins = []
220
+ @datas = []
221
+ end
222
+
223
+ def push(rec)
224
+ case rec
225
+ when ParamsRecord
226
+ @envs.push rec
227
+ when StdinDataRecord
228
+ @stdins.push rec
229
+ when DataRecord
230
+ @datas.push rec
231
+ else
232
+ raise "got unknown record: #{rec.class}"
233
+ end
234
+ end
235
+
236
+ def ready?
237
+ case @begin_request.role
238
+ when FCGI_RESPONDER
239
+ completed?(@envs) and
240
+ completed?(@stdins)
241
+ when FCGI_AUTHORIZER
242
+ completed?(@envs)
243
+ when FCGI_FILTER
244
+ completed?(@envs) and
245
+ completed?(@stdins) and
246
+ completed?(@datas)
247
+ else
248
+ raise "unknown role: #{@begin_request.role}"
249
+ end
250
+ end
251
+
252
+ def completed?(records)
253
+ records.last and records.last.empty?
254
+ end
255
+ private :completed?
256
+
257
+ def new_request
258
+ Request.new(@begin_request.request_id, env(), stdin(), nil, nil, data())
259
+ end
260
+
261
+ def env
262
+ h = {}
263
+ @envs.each {|rec| h.update rec.values }
264
+ h
265
+ end
266
+
267
+ def stdin
268
+ StringIO.new(@stdins.inject('') {|buf, rec| buf << rec.flagment })
269
+ end
270
+
271
+ def data
272
+ StringIO.new(@datas.inject('') {|buf, rec| buf << rec.flagment })
273
+ end
274
+ end
275
+
276
+
277
+ class Request
278
+ def initialize(id, env, stdin, stdout = nil, stderr = nil, data = nil)
279
+ @id = id
280
+ @env = env
281
+ @in = stdin
282
+ @out = stdout || StringIO.new
283
+ @err = stderr || StringIO.new
284
+ @data = data || StringIO.new
285
+ end
286
+
287
+ attr_reader :id
288
+ attr_reader :env
289
+ attr_reader :in
290
+ attr_reader :out
291
+ attr_reader :err
292
+ attr_reader :data
293
+
294
+ def finish # for backword compatibility
295
+ end
296
+ end
297
+
298
+
299
+ class Record
300
+ # uint8_t protocol_version;
301
+ # uint8_t record_type;
302
+ # uint16_t request_id; (big endian)
303
+ # uint16_t content_length; (big endian)
304
+ # uint8_t padding_length;
305
+ # uint8_t reserved;
306
+ HEADER_FORMAT = 'CCnnCC'
307
+ HEADER_LENGTH = 8
308
+
309
+ def self::parse_header(buf)
310
+ return *buf.unpack(HEADER_FORMAT)
311
+ end
312
+
313
+ def self::class_for(type)
314
+ RECORD_CLASS[type]
315
+ end
316
+
317
+ def initialize(type, reqid)
318
+ @type = type
319
+ @request_id = reqid
320
+ end
321
+
322
+ def version
323
+ ::FCGI::ProtocolVersion
324
+ end
325
+
326
+ attr_reader :type
327
+ attr_reader :request_id
328
+
329
+ def management_record?
330
+ @request_id == FCGI_NULL_REQUEST_ID
331
+ end
332
+
333
+ def serialize
334
+ body = make_body()
335
+ padlen = body.length % 8
336
+ header = make_header(body.length, padlen)
337
+ header + body + "\000" * padlen
338
+ end
339
+
340
+ private
341
+
342
+ def make_header(clen, padlen)
343
+ [version(), @type, @request_id, clen, padlen, 0].pack(HEADER_FORMAT)
344
+ end
345
+ end
346
+
347
+ class BeginRequestRecord < Record
348
+ # uint16_t role; (big endian)
349
+ # uint8_t flags;
350
+ # uint8_t reserved[5];
351
+ BODY_FORMAT = 'nCC5'
352
+
353
+ def BeginRequestRecord.parse(id, body)
354
+ role, flags, *reserved = *body.unpack(BODY_FORMAT)
355
+ new(id, role, flags)
356
+ end
357
+
358
+ def initialize(id, role, flags)
359
+ super FCGI_BEGIN_REQUEST, id
360
+ @role = role
361
+ @flags = flags
362
+ end
363
+
364
+ attr_reader :role
365
+ attr_reader :flags
366
+
367
+ def make_body
368
+ [@role, @flags, 0, 0, 0, 0, 0].pack(BODY_FORMAT)
369
+ end
370
+ end
371
+
372
+ class AbortRequestRecord < Record
373
+ def AbortRequestRecord.parse(id, body)
374
+ new(id)
375
+ end
376
+
377
+ def initialize(id)
378
+ super FCGI_ABORT_REQUEST, id
379
+ end
380
+ end
381
+
382
+ class EndRequestRecord < Record
383
+ # uint32_t appStatus; (big endian)
384
+ # uint8_t protocolStatus;
385
+ # uint8_t reserved[3];
386
+ BODY_FORMAT = 'NCC3'
387
+
388
+ def self::parse(id, body)
389
+ appstatus, protostatus, *reserved = *body.unpack(BODY_FORMAT)
390
+ new(id, appstatus, protostatus)
391
+ end
392
+
393
+ def initialize(id, appstatus, protostatus)
394
+ super FCGI_END_REQUEST, id
395
+ @application_status = appstatus
396
+ @protocol_status = protostatus
397
+ end
398
+
399
+ attr_reader :application_status
400
+ attr_reader :protocol_status
401
+
402
+ private
403
+
404
+ def make_body
405
+ [@application_status, @protocol_status, 0, 0, 0].pack(BODY_FORMAT)
406
+ end
407
+ end
408
+
409
+ class UnknownTypeRecord < Record
410
+ # uint8_t type;
411
+ # uint8_t reserved[7];
412
+ BODY_FORMAT = 'CC7'
413
+
414
+ def self::parse(id, body)
415
+ type, *reserved = *body.unpack(BODY_FORMAT)
416
+ new(id, type)
417
+ end
418
+
419
+ def initialize(id, t)
420
+ super FCGI_UNKNOWN_TYPE, id
421
+ @unknown_type = t
422
+ end
423
+
424
+ attr_reader :unknown_type
425
+
426
+ private
427
+
428
+ def make_body
429
+ [@unknown_type, 0, 0, 0, 0, 0, 0, 0].pack(BODY_FORMAT)
430
+ end
431
+ end
432
+
433
+ class ValuesRecord < Record
434
+ def self::parse(id, body)
435
+ new(id, parse_values(body))
436
+ end
437
+
438
+ def self::parse_values(buf)
439
+ result = {}
440
+ until buf.empty?
441
+ name, value = *read_pair(buf)
442
+ result[name] = value
443
+ end
444
+ result
445
+ end
446
+
447
+ def self::read_pair(buf)
448
+ nlen = read_length(buf)
449
+ vlen = read_length(buf)
450
+ return buf.slice!(0, nlen), buf.slice!(0, vlen)
451
+ end
452
+
453
+ def self::read_length(buf)
454
+ if buf[0] >> 7 == 0
455
+ then buf.slice!(0,1)[0]
456
+ else buf.slice!(0,4).unpack('N')[0] & ((1<<31) - 1)
457
+ end
458
+ end
459
+
460
+ def initialize(type, id, values)
461
+ super type, id
462
+ @values = values
463
+ end
464
+
465
+ attr_reader :values
466
+
467
+ private
468
+
469
+ def make_body
470
+ buf = ''
471
+ @values.each do |name, value|
472
+ buf << serialize_length(name.length)
473
+ buf << serialize_length(value.length)
474
+ buf << name
475
+ buf << value
476
+ end
477
+ buf
478
+ end
479
+
480
+ def serialize_length(len)
481
+ if len < 0x80
482
+ then len.chr
483
+ else [len | (1<<31)].pack('N')
484
+ end
485
+ end
486
+ end
487
+
488
+ class GetValuesRecord < ValuesRecord
489
+ def initialize(id, values)
490
+ super FCGI_GET_VALUES, id, values
491
+ end
492
+ end
493
+
494
+ class ParamsRecord < ValuesRecord
495
+ def initialize(id, values)
496
+ super FCGI_PARAMS, id, values
497
+ end
498
+
499
+ def empty?
500
+ @values.empty?
501
+ end
502
+ end
503
+
504
+ class GenericDataRecord < Record
505
+ def self::parse(id, body)
506
+ new(id, body)
507
+ end
508
+
509
+ def initialize(type, id, flagment)
510
+ super type, id
511
+ @flagment = flagment
512
+ end
513
+
514
+ attr_reader :flagment
515
+
516
+ def empty?
517
+ @flagment.empty?
518
+ end
519
+
520
+ private
521
+
522
+ def make_body
523
+ @flagment
524
+ end
525
+ end
526
+
527
+ class StdinDataRecord < GenericDataRecord
528
+ def initialize(id, flagment)
529
+ super FCGI_STDIN, id, flagment
530
+ end
531
+ end
532
+
533
+ class StdoutDataRecord < GenericDataRecord
534
+ def initialize(id, flagment)
535
+ super FCGI_STDOUT, id, flagment
536
+ end
537
+ end
538
+
539
+ class DataRecord < GenericDataRecord
540
+ def initialize(id, flagment)
541
+ super FCGI_DATA, id, flagment
542
+ end
543
+ end
544
+
545
+ class Record # redefine
546
+ RECORD_CLASS = {
547
+ FCGI_GET_VALUES => GetValuesRecord,
548
+
549
+ FCGI_BEGIN_REQUEST => BeginRequestRecord,
550
+ FCGI_ABORT_REQUEST => AbortRequestRecord,
551
+ FCGI_PARAMS => ParamsRecord,
552
+ FCGI_STDIN => StdinDataRecord,
553
+ FCGI_DATA => DataRecord,
554
+ FCGI_STDOUT => StdoutDataRecord,
555
+ FCGI_END_REQUEST => EndRequestRecord
556
+ }
557
+ end
558
+
559
+ end # FCGI class
560
+ end # begin
561
+
562
+ # There is no C version of 'each_cgi'
563
+ # Note: for ruby-1.6.8 at least, the constants CGI_PARAMS/CGI_COOKIES
564
+ # are defined within module 'CGI', even if you have subclassed it
565
+
566
+ class FCGI
567
+ def self::each_cgi(*args)
568
+ require 'cgi'
569
+
570
+ eval(<<-EOS,TOPLEVEL_BINDING)
571
+ class CGI
572
+ public :env_table
573
+ def self::remove_params
574
+ if (const_defined?(:CGI_PARAMS))
575
+ remove_const(:CGI_PARAMS)
576
+ remove_const(:CGI_COOKIES)
577
+ end
578
+ end
579
+ end # ::CGI class
580
+
581
+ class FCGI
582
+ class CGI < ::CGI
583
+ def initialize(request, *args)
584
+ ::CGI.remove_params
585
+ @request = request
586
+ super(*args)
587
+ @args = *args
588
+ end
589
+ def args
590
+ @args
591
+ end
592
+ def env_table
593
+ @request.env
594
+ end
595
+ def stdinput
596
+ @request.in
597
+ end
598
+ def stdoutput
599
+ @request.out
600
+ end
601
+ end # FCGI::CGI class
602
+ end # FCGI class
603
+ EOS
604
+
605
+ if FCGI::is_cgi?
606
+ yield ::CGI.new(*args)
607
+ else
608
+ exit_requested = false
609
+ FCGI::each {|request|
610
+ $stdout, $stderr = request.out, request.err
611
+
612
+ yield CGI.new(request, *args)
613
+
614
+ request.finish
615
+ }
616
+ end
617
+ end
618
+ end