fcgi 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/MANIFEST +5 -0
  2. data/README +111 -0
  3. data/extconf.rb +9 -0
  4. data/fcgi.c +529 -0
  5. data/fcgi.gemspec +16 -0
  6. data/lib/fcgi.rb +606 -0
  7. metadata +44 -0
@@ -0,0 +1,5 @@
1
+ MANIFEST
2
+ README
3
+ extconf.rb
4
+ fcgi.c
5
+ lib/fcgi.rb
data/README ADDED
@@ -0,0 +1,111 @@
1
+ = fcgi - FastCGI library for Ruby
2
+
3
+ Version 0.8.5
4
+
5
+ == Depends
6
+
7
+ === C version
8
+ * http://www.fastcgi.com/#TheDevKit
9
+
10
+ === Pure Ruby Version
11
+ * StringIO
12
+
13
+ == Install
14
+
15
+ $ ruby install.rb config
16
+ (Pure Ruby Version: ruby install.rb config --without-ext)
17
+ (Some systems need: ruby install.rb config -- --with-fcgi-include=/usr/local/include --with-fcgi-lib=/usr/local/lib)
18
+ $ ruby install.rb setup
19
+ # ruby install.rb install
20
+
21
+ == Usage
22
+ === Class Method
23
+ --- FCGI.accept
24
+ Returns FCGI instance
25
+ --- FCGI.each
26
+
27
+ --- FCGI.each_request
28
+
29
+ --- FCGI.is_cgi?
30
+
31
+ --- FCGI.each_cgi
32
+ Automatically detects whether this program is running under the FastCGI
33
+ environment, and generates a 'CGI' type object for each request. Also
34
+ installs signal handlers for graceful handling of SIGPIPE (which may
35
+ occur if a client gives up on a request before it is complete) and
36
+ SIGUSR1 (generated by Apache for a 'graceful' exit)
37
+
38
+ If you are using the HTML output methods you can also pass the HTML type
39
+ e.g. FCGI.each_cgi('html3') do ... end
40
+
41
+ However, you should beware that the CGI library is quite slow when
42
+ used in this way, as it dynamically adds a large number of methods
43
+ to itself each time a new instance is created.
44
+
45
+
46
+ === Instance Method
47
+ --- FCGI#finish
48
+ Finish
49
+
50
+ --- FCGI#in
51
+ Returns Stream or StringIO
52
+
53
+ --- FCGI#out
54
+ Returns Stream or StringIO
55
+
56
+ --- FCGI#err
57
+ Returns Stream or StringIO
58
+
59
+ --- FCGI#env
60
+ Returns Environment(Hash)
61
+
62
+ == Sample
63
+ Using the FastCGI native interface:
64
+
65
+ #!/usr/bin/ruby
66
+ require "fcgi"
67
+
68
+ FCGI.each {|request|
69
+ out = request.out
70
+ out.print "Content-Type: text/plain\r\n"
71
+ out.print "\r\n"
72
+ out.print Time.now.to_s
73
+ request.finish
74
+ }
75
+
76
+ Using the CGI-compatible interface, which works both as a standalone CGI
77
+ and under FastCGI with no modifications:
78
+
79
+ #!/usr/bin/ruby
80
+ require "fcgi"
81
+
82
+ FCGI.each_cgi {|cgi|
83
+ name = cgi['name'][0]
84
+ puts cgi.header
85
+ puts "You are #{name} " if name
86
+ puts "Connecting from #{cgi.remote_addr}"
87
+ }
88
+
89
+ Note: you can't reference CGI environment variables using ENV when under
90
+ FastCGI. It is recommended that you use the CGI-generated methods, e.g.
91
+ cgi.remote_addr as above.
92
+
93
+ If you need to access environment variables directly, perhaps extra ones set
94
+ in your Apache config, then use cgi.env_table['REMOTE_ADDR'] instead. This
95
+ isn't quite as portable because env_table is a private method in the
96
+ standard CGI library.
97
+
98
+ == License
99
+
100
+ * http://www.ruby-lang.org/ja/LICENSE.txt (Japanese)
101
+ * http://www.ruby-lang.org/en/LICENSE.txt (English)
102
+
103
+ == Copyright
104
+
105
+ fcgi.c 0.1 Copyright (C) 1998-1999 Network Applied Communication Laboratory, Inc.
106
+ 0.8 Copyright (C) 2002 MoonWolf <moonwolf@moonwolf.com>
107
+
108
+ fastcgi.rb 0.7 Copyright (C) 2001 Eli Green
109
+ fcgi.rb 0.8 Copyright (C) 2002 MoonWolf <moonwolf@moonwolf.com>
110
+ fcgi.rb 0.8.5 Copyright (C) 2004 Minero Aoki
111
+
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "mkmf"
4
+
5
+ dir_config("fcgi")
6
+
7
+ if have_header("fcgiapp.h") && have_library("fcgi", "FCGX_Accept")
8
+ create_makefile("fcgi")
9
+ end
data/fcgi.c ADDED
@@ -0,0 +1,529 @@
1
+ /*
2
+ * fcgi.c
3
+ * Copyright (C) 1998-1999 Network Applied Communication Laboratory, Inc.
4
+ * Copyright (C) 2002 MoonWolf <moonwolf@moonwolf.com>
5
+ */
6
+
7
+ #include <stdio.h>
8
+ #include <sys/types.h>
9
+ #include <unistd.h>
10
+
11
+ #include "ruby.h"
12
+ #include "fcgiapp.h"
13
+
14
+ static VALUE cFCGI;
15
+ static VALUE eFCGIError;
16
+ static VALUE cFCGIStream;
17
+ static VALUE eFCGIStreamError;
18
+ static VALUE eFCGIStreamUnsupportedVersionError;
19
+ static VALUE eFCGIStreamProtocolError;
20
+ static VALUE eFCGIStreamParamsError;
21
+ static VALUE eFCGIStreamCallSeqError;
22
+
23
+ typedef struct fcgi_data {
24
+ FCGX_Request *req;
25
+ VALUE in;
26
+ VALUE out;
27
+ VALUE err;
28
+ VALUE env;
29
+ } fcgi_data;
30
+
31
+ static void fcgi_mark(fcgi_data *data)
32
+ {
33
+ /* rb_gc_mark(data->req); */
34
+ rb_gc_mark(data->in);
35
+ rb_gc_mark(data->out);
36
+ rb_gc_mark(data->err);
37
+ rb_gc_mark(data->env);
38
+ }
39
+
40
+ static VALUE fcgi_s_accept(VALUE self)
41
+ {
42
+ int status;
43
+ FCGX_Request *req;
44
+ fd_set readfds;
45
+
46
+ req = ALLOC(FCGX_Request);
47
+
48
+ status = FCGX_InitRequest(req,0,0);
49
+ if (status != 0) {
50
+ rb_raise(eFCGIError, "FCGX_Init() failed");
51
+ return Qnil;
52
+ }
53
+
54
+ FD_ZERO(&readfds);
55
+ FD_SET(req->listen_sock, &readfds);
56
+ if (rb_thread_select(req->listen_sock+1, &readfds, NULL, NULL, NULL) < 1) {
57
+ return Qnil;
58
+ }
59
+
60
+ status = FCGX_Accept_r(req);
61
+ if (status >= 0) {
62
+ fcgi_data *data;
63
+ char **env;
64
+ VALUE obj,key, value;
65
+ char *pkey,*pvalue;
66
+
67
+ obj = Data_Make_Struct(self, fcgi_data, fcgi_mark, 0, data);
68
+ data->req = req;
69
+ data->in = Data_Wrap_Struct(cFCGIStream, 0, 0, req->in);
70
+ data->out = Data_Wrap_Struct(cFCGIStream, 0, 0, req->out);
71
+ data->err = Data_Wrap_Struct(cFCGIStream, 0, 0, req->err);
72
+ data->env = rb_hash_new();
73
+ env = req->envp;
74
+ for (env; *env; env++) {
75
+ int size = 0;
76
+ pkey = *env;
77
+ pvalue = pkey;
78
+ while( *(pvalue++) != '=') size++;
79
+ key = rb_str_new(pkey, size);
80
+ value = rb_str_new2(pvalue);
81
+ OBJ_TAINT(key);
82
+ OBJ_TAINT(value);
83
+ rb_hash_aset(data->env, key, value);
84
+ }
85
+
86
+ return obj;
87
+ } else {
88
+ return Qnil;
89
+ }
90
+ }
91
+
92
+ static VALUE fcgi_s_each(VALUE self)
93
+ {
94
+ VALUE fcgi;
95
+
96
+ while ((fcgi = fcgi_s_accept(self)) != Qnil) {
97
+ rb_yield(fcgi);
98
+ }
99
+ return Qnil;
100
+ }
101
+
102
+ static VALUE fcgi_s_iscgi(VALUE self)
103
+ {
104
+ if (FCGX_IsCGI()) {
105
+ return Qtrue;
106
+ } else {
107
+ return Qfalse;
108
+ }
109
+ }
110
+
111
+ static VALUE fcgi_in(VALUE self)
112
+ {
113
+ fcgi_data *data;
114
+
115
+ Data_Get_Struct(self, fcgi_data, data);
116
+ return data->in;
117
+ }
118
+
119
+ static VALUE fcgi_out(VALUE self)
120
+ {
121
+ fcgi_data *data;
122
+
123
+ Data_Get_Struct(self, fcgi_data, data);
124
+ return data->out;
125
+ }
126
+
127
+ static VALUE fcgi_err(VALUE self)
128
+ {
129
+ fcgi_data *data;
130
+
131
+ Data_Get_Struct(self, fcgi_data, data);
132
+ return data->err;
133
+ }
134
+
135
+ static VALUE fcgi_env(VALUE self)
136
+ {
137
+ fcgi_data *data;
138
+
139
+ Data_Get_Struct(self, fcgi_data, data);
140
+ return data->env;
141
+ }
142
+
143
+ static VALUE fcgi_finish(VALUE self)
144
+ {
145
+ fcgi_data *data;
146
+
147
+ Data_Get_Struct(self, fcgi_data, data);
148
+
149
+ FCGX_Finish_r(data->req);
150
+
151
+ return Qtrue;
152
+ }
153
+
154
+ #define CHECK_STREAM_ERROR(stream) {\
155
+ int err = FCGX_GetError(stream);\
156
+ if (err) {\
157
+ if (err > 0) {\
158
+ rb_sys_fail(0);\
159
+ }\
160
+ else {\
161
+ switch (err) {\
162
+ case FCGX_UNSUPPORTED_VERSION:\
163
+ rb_raise(eFCGIStreamUnsupportedVersionError, "unsupported version");\
164
+ break;\
165
+ case FCGX_PROTOCOL_ERROR:\
166
+ rb_raise(eFCGIStreamProtocolError, "protocol error");\
167
+ break;\
168
+ case FCGX_PARAMS_ERROR:\
169
+ rb_raise(eFCGIStreamProtocolError, "parameter error");\
170
+ break;\
171
+ case FCGX_CALL_SEQ_ERROR:\
172
+ rb_raise(eFCGIStreamCallSeqError, "preconditions are not met");\
173
+ break;\
174
+ default:\
175
+ rb_raise(eFCGIStreamError, "unknown error");\
176
+ break;\
177
+ }\
178
+ }\
179
+ }\
180
+ }
181
+
182
+ static VALUE fcgi_stream_putc(VALUE self, VALUE ch)
183
+ {
184
+ FCGX_Stream *stream;
185
+ int c;
186
+
187
+ rb_secure(4);
188
+ Data_Get_Struct(self, FCGX_Stream, stream);
189
+ if ((c = FCGX_PutChar(NUM2INT(ch), stream)) == EOF)
190
+ CHECK_STREAM_ERROR(stream);
191
+ return INT2NUM(c);
192
+ }
193
+
194
+ static VALUE fcgi_stream_write(VALUE self, VALUE str)
195
+ {
196
+ FCGX_Stream *stream;
197
+ int len;
198
+
199
+ rb_secure(4);
200
+ Data_Get_Struct(self, FCGX_Stream, stream);
201
+ str = rb_obj_as_string(str);
202
+ len = FCGX_PutStr(RSTRING(str)->ptr, RSTRING(str)->len, stream);
203
+ if (len == EOF) CHECK_STREAM_ERROR(stream);
204
+ return INT2NUM(len);
205
+ }
206
+
207
+ static VALUE fcgi_stream_print(int argc, VALUE *argv, VALUE out)
208
+ {
209
+ int i;
210
+ VALUE line;
211
+
212
+ /* if no argument given, print `$_' */
213
+ if (argc == 0) {
214
+ argc = 1;
215
+ line = rb_lastline_get();
216
+ argv = &line;
217
+ }
218
+ for (i=0; i<argc; i++) {
219
+ if (!NIL_P(rb_output_fs) && i>0) {
220
+ fcgi_stream_write(out, rb_output_fs);
221
+ }
222
+ switch (TYPE(argv[i])) {
223
+ case T_NIL:
224
+ fcgi_stream_write(out, rb_str_new2("nil"));
225
+ break;
226
+ default:
227
+ fcgi_stream_write(out, argv[i]);
228
+ break;
229
+ }
230
+ }
231
+ if (!NIL_P(rb_output_rs)) {
232
+ fcgi_stream_write(out, rb_output_rs);
233
+ }
234
+
235
+ return Qnil;
236
+ }
237
+
238
+ static VALUE fcgi_stream_printf(int argc, VALUE *argv, VALUE out)
239
+ {
240
+ fcgi_stream_write(out, rb_f_sprintf(argc, argv));
241
+ return Qnil;
242
+ }
243
+
244
+ static VALUE fcgi_stream_puts _((int, VALUE*, VALUE));
245
+
246
+ static VALUE fcgi_stream_puts_ary(VALUE ary, VALUE out)
247
+ {
248
+ VALUE tmp;
249
+ int i;
250
+
251
+ for (i=0; i<RARRAY(ary)->len; i++) {
252
+ tmp = RARRAY(ary)->ptr[i];
253
+ if (rb_inspecting_p(tmp)) {
254
+ tmp = rb_str_new2("[...]");
255
+ }
256
+ fcgi_stream_puts(1, &tmp, out);
257
+ }
258
+ return Qnil;
259
+ }
260
+
261
+ static VALUE fcgi_stream_puts(int argc, VALUE *argv, VALUE out)
262
+ {
263
+ int i;
264
+ VALUE line;
265
+
266
+ /* if no argument given, print newline. */
267
+ if (argc == 0) {
268
+ fcgi_stream_write(out, rb_default_rs);
269
+ return Qnil;
270
+ }
271
+ for (i=0; i<argc; i++) {
272
+ switch (TYPE(argv[i])) {
273
+ case T_NIL:
274
+ line = rb_str_new2("nil");
275
+ break;
276
+ case T_ARRAY:
277
+ rb_protect_inspect(fcgi_stream_puts_ary, argv[i], out);
278
+ continue;
279
+ default:
280
+ line = argv[i];
281
+ break;
282
+ }
283
+ line = rb_obj_as_string(line);
284
+ fcgi_stream_write(out, line);
285
+ if (RSTRING(line)->ptr[RSTRING(line)->len-1] != '\n') {
286
+ fcgi_stream_write(out, rb_default_rs);
287
+ }
288
+ }
289
+
290
+ return Qnil;
291
+ }
292
+
293
+ static VALUE fcgi_stream_addstr(VALUE out, VALUE str)
294
+ {
295
+ fcgi_stream_write(out, str);
296
+ return out;
297
+ }
298
+
299
+ static VALUE fcgi_stream_flush(VALUE self)
300
+ {
301
+ FCGX_Stream *stream;
302
+
303
+ Data_Get_Struct(self, FCGX_Stream, stream);
304
+ if (FCGX_FFlush(stream) == EOF)
305
+ CHECK_STREAM_ERROR(stream);
306
+ return Qnil;
307
+ }
308
+
309
+ static VALUE fcgi_stream_getc(VALUE self)
310
+ {
311
+ FCGX_Stream *stream;
312
+ int c;
313
+
314
+ Data_Get_Struct(self, FCGX_Stream, stream);
315
+ if ((c = FCGX_GetChar(stream)) == EOF) {
316
+ CHECK_STREAM_ERROR(stream);
317
+ return Qnil;
318
+ }
319
+ else {
320
+ return INT2NUM(c);
321
+ }
322
+ }
323
+
324
+ static VALUE fcgi_stream_ungetc(VALUE self, VALUE ch)
325
+ {
326
+ FCGX_Stream *stream;
327
+ int c;
328
+
329
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
330
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
331
+ }
332
+ Data_Get_Struct(self, FCGX_Stream, stream);
333
+ c = FCGX_UnGetChar(NUM2INT(ch), stream);
334
+ CHECK_STREAM_ERROR(stream);
335
+ return INT2NUM(c);
336
+ }
337
+
338
+ static VALUE fcgi_stream_gets(VALUE self)
339
+ {
340
+ FCGX_Stream *stream;
341
+ char buff[BUFSIZ];
342
+ VALUE str = rb_str_new("", 0);
343
+ OBJ_TAINT(str);
344
+
345
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
346
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
347
+ }
348
+
349
+ Data_Get_Struct(self, FCGX_Stream, stream);
350
+
351
+ for (;;) {
352
+ if (FCGX_GetLine(buff, BUFSIZ, stream) == NULL) {
353
+ CHECK_STREAM_ERROR(stream);
354
+ break;
355
+ }
356
+ rb_str_cat(str, buff, strlen(buff));
357
+ if (strchr(buff, '\n')) break;
358
+ }
359
+ if (RSTRING(str)->len > 0)
360
+ return str;
361
+ else
362
+ return Qnil;
363
+ }
364
+
365
+ static VALUE fcgi_stream_read(int argc, VALUE *argv, VALUE self)
366
+ {
367
+ VALUE num,str;
368
+ FCGX_Stream *stream;
369
+ char *buff;
370
+ int n;
371
+
372
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
373
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
374
+ }
375
+
376
+ Data_Get_Struct(self, FCGX_Stream, stream);
377
+
378
+ if (argc==0) {
379
+ buff = ALLOC_N(char, 16384);
380
+ n = FCGX_GetStr(buff, 16384, stream);
381
+ CHECK_STREAM_ERROR(stream);
382
+ if (n == 0) return Qnil;
383
+ str = rb_str_new(buff, n);
384
+ OBJ_TAINT(str);
385
+
386
+ while(!FCGX_HasSeenEOF(stream)) {
387
+ n = FCGX_GetStr(buff, 16384, stream);
388
+ CHECK_STREAM_ERROR(stream);
389
+ if (n > 0) {
390
+ rb_str_cat(str, buff, n);
391
+ } else {
392
+ return Qnil;
393
+ }
394
+ }
395
+ return str;
396
+ }
397
+
398
+ num = argv[0];
399
+ n = NUM2INT(num);
400
+
401
+ buff = ALLOC_N(char, n);
402
+ n = FCGX_GetStr(buff, n, stream);
403
+ CHECK_STREAM_ERROR(stream);
404
+ if (n > 0) {
405
+ str = rb_str_new(buff, n);
406
+ OBJ_TAINT(str);
407
+ return str;
408
+ }
409
+ else {
410
+ return Qnil;
411
+ }
412
+ }
413
+
414
+ static VALUE fcgi_stream_eof(VALUE self)
415
+ {
416
+ FCGX_Stream *stream;
417
+
418
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
419
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
420
+ }
421
+ Data_Get_Struct(self, FCGX_Stream, stream);
422
+ return FCGX_HasSeenEOF(stream) ? Qtrue : Qfalse;
423
+ }
424
+
425
+ static VALUE fcgi_stream_close(VALUE self)
426
+ {
427
+ FCGX_Stream *stream;
428
+
429
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
430
+ rb_raise(rb_eSecurityError, "Insecure: can't close");
431
+ }
432
+ Data_Get_Struct(self, FCGX_Stream, stream);
433
+ if (FCGX_FClose(stream) == EOF)
434
+ CHECK_STREAM_ERROR(stream);
435
+ return Qnil;
436
+ }
437
+
438
+ static VALUE fcgi_stream_closed(VALUE self)
439
+ {
440
+ FCGX_Stream *stream;
441
+
442
+ Data_Get_Struct(self, FCGX_Stream, stream);
443
+ return stream->isClosed ? Qtrue : Qfalse;
444
+ }
445
+
446
+ static VALUE fcgi_stream_binmode(VALUE self)
447
+ {
448
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
449
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
450
+ }
451
+ return self;
452
+ }
453
+
454
+ static VALUE fcgi_stream_isatty(VALUE self)
455
+ {
456
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
457
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
458
+ }
459
+ return Qfalse;
460
+ }
461
+
462
+ static VALUE fcgi_stream_sync(VALUE self)
463
+ {
464
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
465
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
466
+ }
467
+ return Qfalse;
468
+ }
469
+
470
+ static VALUE fcgi_stream_setsync(VALUE self,VALUE sync)
471
+ {
472
+ if (rb_safe_level() >= 4 && !OBJ_TAINTED(self)) {
473
+ rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
474
+ }
475
+ return Qfalse;
476
+ }
477
+
478
+
479
+
480
+ void Init_fcgi()
481
+ {
482
+
483
+ FCGX_Init();
484
+
485
+ cFCGI = rb_define_class("FCGI", rb_cObject);
486
+ eFCGIError =rb_define_class_under(cFCGI, "Error", rb_eStandardError);
487
+ rb_define_singleton_method(cFCGI, "accept", fcgi_s_accept, 0);
488
+ rb_define_singleton_method(cFCGI, "each", fcgi_s_each, 0);
489
+ rb_define_singleton_method(cFCGI, "each_request", fcgi_s_each, 0);
490
+ rb_define_singleton_method(cFCGI, "is_cgi?", fcgi_s_iscgi, 0);
491
+ rb_define_method(cFCGI, "in", fcgi_in, 0);
492
+ rb_define_method(cFCGI, "out", fcgi_out, 0);
493
+ rb_define_method(cFCGI, "err", fcgi_err, 0);
494
+ rb_define_method(cFCGI, "env", fcgi_env, 0);
495
+ rb_define_method(cFCGI, "finish", fcgi_finish, 0);
496
+
497
+ cFCGIStream = rb_define_class_under(cFCGI, "Stream", rb_cObject);
498
+ eFCGIStreamError =rb_define_class_under(cFCGIStream, "Error", rb_eStandardError);
499
+ eFCGIStreamUnsupportedVersionError =
500
+ rb_define_class_under(cFCGIStream, "UnsupportedVersionError",
501
+ eFCGIStreamError);
502
+ eFCGIStreamProtocolError = rb_define_class_under(cFCGIStream, "ProtocolError",
503
+ eFCGIStreamError);
504
+ eFCGIStreamParamsError = rb_define_class_under(cFCGIStream, "ParamsError",
505
+ eFCGIStreamError);
506
+ eFCGIStreamCallSeqError = rb_define_class_under(cFCGIStream, "CallSeqError",
507
+ eFCGIStreamError);
508
+ rb_undef_method(CLASS_OF(cFCGIStream), "new");
509
+ rb_define_method(cFCGIStream, "putc", fcgi_stream_putc, 1);
510
+ rb_define_method(cFCGIStream, "write", fcgi_stream_write, 1);
511
+ rb_define_method(cFCGIStream, "print", fcgi_stream_print, -1);
512
+ rb_define_method(cFCGIStream, "printf", fcgi_stream_printf, -1);
513
+ rb_define_method(cFCGIStream, "puts", fcgi_stream_puts, -1);
514
+ rb_define_method(cFCGIStream, "<<", fcgi_stream_addstr, 1);
515
+ rb_define_method(cFCGIStream, "flush", fcgi_stream_flush, 0);
516
+ rb_define_method(cFCGIStream, "getc", fcgi_stream_getc, 0);
517
+ rb_define_method(cFCGIStream, "ungetc", fcgi_stream_ungetc, 1);
518
+ rb_define_method(cFCGIStream, "gets", fcgi_stream_gets, 0);
519
+ rb_define_method(cFCGIStream, "read", fcgi_stream_read, -1);
520
+ rb_define_method(cFCGIStream, "eof", fcgi_stream_eof, 0);
521
+ rb_define_method(cFCGIStream, "eof?", fcgi_stream_eof, 0);
522
+ rb_define_method(cFCGIStream, "close", fcgi_stream_close, 0);
523
+ rb_define_method(cFCGIStream, "closed?", fcgi_stream_closed, 0);
524
+ rb_define_method(cFCGIStream, "binmode", fcgi_stream_binmode, 0);
525
+ rb_define_method(cFCGIStream, "isatty", fcgi_stream_isatty, 0);
526
+ rb_define_method(cFCGIStream, "tty?", fcgi_stream_isatty, 0);
527
+ rb_define_method(cFCGIStream, "sync", fcgi_stream_sync, 0);
528
+ rb_define_method(cFCGIStream, "sync=", fcgi_stream_setsync, 1);
529
+ }
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{fcgi}
3
+ s.version = "0.8.5"
4
+ s.date = Time.now
5
+ s.require_path = %q{.}
6
+ s.summary = %q{FastCGI ruby binding.}
7
+ s.author = %q{Network Applied Communication Laboratory, Inc., MoonWolf, Eli Green, Minero Aoki}
8
+ s.platform = Gem::Platform::RUBY
9
+ s.files = Dir.glob('**/*')
10
+ s.email = %q{moonwolf@moonwolf.com}
11
+ s.homepage = %q{http://rwiki.moonwolf.com/rw-cgi.cgi?cmd=view;name=fcgi}
12
+ s.autorequire = %q{fcgi}
13
+ s.extensions = ["extconf.rb"]
14
+ s.has_rdoc = true
15
+ s.extra_rdoc_files = ["README"]
16
+ end
@@ -0,0 +1,606 @@
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
+ end
367
+
368
+ class AbortRequestRecord < Record
369
+ def AbortRequestRecord.parse(id, body)
370
+ new(id)
371
+ end
372
+
373
+ def initialize(id)
374
+ super FCGI_ABORT_REQUEST, id
375
+ end
376
+ end
377
+
378
+ class EndRequestRecord < Record
379
+ # uint32_t appStatus; (big endian)
380
+ # uint8_t protocolStatus;
381
+ # uint8_t reserved[3];
382
+ BODY_FORMAT = 'NCC3'
383
+
384
+ def self::parse(id, body)
385
+ appstatus, protostatus, *reserved = *body.unpack(BODY_FORMAT)
386
+ new(id, appstatus, protostatus)
387
+ end
388
+
389
+ def initialize(id, appstatus, protostatus)
390
+ super FCGI_END_REQUEST, id
391
+ @application_status = appstatus
392
+ @protocol_status = protostatus
393
+ end
394
+
395
+ attr_reader :application_status
396
+ attr_reader :protocol_status
397
+
398
+ private
399
+
400
+ def make_body
401
+ [@application_status, @protocol_status, 0, 0, 0].pack(BODY_FORMAT)
402
+ end
403
+ end
404
+
405
+ class UnknownTypeRecord < Record
406
+ # uint8_t type;
407
+ # uint8_t reserved[7];
408
+ BODY_FORMAT = 'CC7'
409
+
410
+ def self::parse(id, body)
411
+ type, *reserved = *body.unpack(BODY_FORMAT)
412
+ new(id, type)
413
+ end
414
+
415
+ def initialize(id, t)
416
+ super FCGI_UNKNOWN_TYPE, id
417
+ @unknown_type = t
418
+ end
419
+
420
+ attr_reader :unknown_type
421
+
422
+ private
423
+
424
+ def make_body
425
+ [@unknown_type, 0, 0, 0, 0, 0, 0, 0].pack(BODY_FORMAT)
426
+ end
427
+ end
428
+
429
+ class ValuesRecord < Record
430
+ def self::parse(id, body)
431
+ new(id, parse_values(body))
432
+ end
433
+
434
+ def self::parse_values(buf)
435
+ result = {}
436
+ until buf.empty?
437
+ name, value = *read_pair(buf)
438
+ result[name] = value
439
+ end
440
+ result
441
+ end
442
+
443
+ def self::read_pair(buf)
444
+ nlen = read_length(buf)
445
+ vlen = read_length(buf)
446
+ return buf.slice!(0, nlen), buf.slice!(0, vlen)
447
+ end
448
+
449
+ def self::read_length(buf)
450
+ if buf[0] >> 7 == 0
451
+ then buf.slice!(0,1)[0]
452
+ else buf.slice!(0,4).unpack('N')[0] & ((1<<31) - 1)
453
+ end
454
+ end
455
+
456
+ def initialize(type, id, values)
457
+ super type, id
458
+ @values = values
459
+ end
460
+
461
+ attr_reader :values
462
+
463
+ private
464
+
465
+ def make_body
466
+ buf = ''
467
+ @values.each do |name, value|
468
+ buf << serialize_length(name.length)
469
+ buf << serialize_length(value.length)
470
+ buf << name
471
+ buf << value
472
+ end
473
+ buf
474
+ end
475
+
476
+ def serialize_length(len)
477
+ if len < 0x80
478
+ then len.chr
479
+ else [len | (1<<31)].pack('N')
480
+ end
481
+ end
482
+ end
483
+
484
+ class GetValuesRecord < ValuesRecord
485
+ def initialize(id, values)
486
+ super FCGI_GET_VALUES, id, values
487
+ end
488
+ end
489
+
490
+ class ParamsRecord < ValuesRecord
491
+ def initialize(id, values)
492
+ super FCGI_PARAMS, id, values
493
+ end
494
+
495
+ def empty?
496
+ @values.empty?
497
+ end
498
+ end
499
+
500
+ class GenericDataRecord < Record
501
+ def self::parse(id, body)
502
+ new(id, body)
503
+ end
504
+
505
+ def initialize(type, id, flagment)
506
+ super type, id
507
+ @flagment = flagment
508
+ end
509
+
510
+ attr_reader :flagment
511
+
512
+ def empty?
513
+ @flagment.empty?
514
+ end
515
+
516
+ private
517
+
518
+ def make_body
519
+ @flagment
520
+ end
521
+ end
522
+
523
+ class StdinDataRecord < GenericDataRecord
524
+ def initialize(id, flagment)
525
+ super FCGI_STDIN, id, flagment
526
+ end
527
+ end
528
+
529
+ class DataRecord < GenericDataRecord
530
+ def initialize(id, flagment)
531
+ super FCGI_DATA, id, flagment
532
+ end
533
+ end
534
+
535
+ class Record # redefine
536
+ RECORD_CLASS = {
537
+ FCGI_GET_VALUES => GetValuesRecord,
538
+
539
+ FCGI_BEGIN_REQUEST => BeginRequestRecord,
540
+ FCGI_ABORT_REQUEST => AbortRequestRecord,
541
+ FCGI_PARAMS => ParamsRecord,
542
+ FCGI_STDIN => StdinDataRecord,
543
+ FCGI_DATA => DataRecord
544
+ }
545
+ end
546
+
547
+ end # FCGI class
548
+ end # begin
549
+
550
+ # There is no C version of 'each_cgi'
551
+ # Note: for ruby-1.6.8 at least, the constants CGI_PARAMS/CGI_COOKIES
552
+ # are defined within module 'CGI', even if you have subclassed it
553
+
554
+ class FCGI
555
+ def self::each_cgi(*args)
556
+ require 'cgi'
557
+
558
+ eval(<<-EOS,TOPLEVEL_BINDING)
559
+ class CGI
560
+ public :env_table
561
+ def self::remove_params
562
+ if (const_defined?(:CGI_PARAMS))
563
+ remove_const(:CGI_PARAMS)
564
+ remove_const(:CGI_COOKIES)
565
+ end
566
+ end
567
+ end # ::CGI class
568
+
569
+ class FCGI
570
+ class CGI < ::CGI
571
+ def initialize(request, *args)
572
+ ::CGI.remove_params
573
+ @request = request
574
+ super(*args)
575
+ @args = *args
576
+ end
577
+ def args
578
+ @args
579
+ end
580
+ def env_table
581
+ @request.env
582
+ end
583
+ def stdinput
584
+ @request.in
585
+ end
586
+ def stdoutput
587
+ @request.out
588
+ end
589
+ end # FCGI::CGI class
590
+ end # FCGI class
591
+ EOS
592
+
593
+ if FCGI::is_cgi?
594
+ yield ::CGI.new(*args)
595
+ else
596
+ exit_requested = false
597
+ FCGI::each {|request|
598
+ $stdout, $stderr = request.out, request.err
599
+
600
+ yield CGI.new(request, *args)
601
+
602
+ request.finish
603
+ }
604
+ end
605
+ end
606
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: "0.8"
3
+ specification_version: 1
4
+ name: fcgi
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.8.5
7
+ date: 2004-10-21
8
+ summary: FastCGI ruby binding.
9
+ require_paths:
10
+ - "."
11
+ author: "Network Applied Communication Laboratory, Inc., MoonWolf, Eli Green, Minero Aoki"
12
+ email: moonwolf@moonwolf.com
13
+ homepage: http://rwiki.moonwolf.com/rw-cgi.cgi?cmd=view;name=fcgi
14
+ rubyforge_project:
15
+ description:
16
+ autorequire: fcgi
17
+ default_executable:
18
+ bindir: bin
19
+ has_rdoc: true
20
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
21
+ requirements:
22
+ -
23
+ - ">"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.0.0
26
+ version:
27
+ platform: ruby
28
+ files:
29
+ - lib
30
+ - extconf.rb
31
+ - fcgi.c
32
+ - MANIFEST
33
+ - README
34
+ - fcgi.gemspec
35
+ - lib/fcgi.rb
36
+ test_files: []
37
+ rdoc_options: []
38
+ extra_rdoc_files:
39
+ - README
40
+ executables: []
41
+ extensions:
42
+ - extconf.rb
43
+ requirements: []
44
+ dependencies: []