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.
- data/MANIFEST +5 -0
- data/README +111 -0
- data/extconf.rb +9 -0
- data/fcgi.c +529 -0
- data/fcgi.gemspec +16 -0
- data/lib/fcgi.rb +606 -0
- metadata +44 -0
data/MANIFEST
ADDED
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
|
+
|
data/extconf.rb
ADDED
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
|
+
}
|
data/fcgi.gemspec
ADDED
@@ -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
|
data/lib/fcgi.rb
ADDED
@@ -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: []
|