fcgi 0.8.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []