ruby-lsapi 5.8 → 5.9
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.
- checksums.yaml +4 -4
- data/README +11 -2
- data/examples/lsapi_with_cgi.rb +17 -2
- data/examples/testlsapi.rb +18 -1
- data/ext/lsapi/lsapilib.c +637 -198
- data/ext/lsapi/lsapilib.h +25 -4
- data/ext/lsapi/lsruby.c +388 -204
- data/lsapi.gemspec +2 -2
- data/rails/dispatch.lsapi +30 -2
- data/scripts/lsruby_runner.rb +33 -10
- metadata +3 -6
data/ext/lsapi/lsruby.c
CHANGED
|
@@ -12,10 +12,17 @@
|
|
|
12
12
|
#endif
|
|
13
13
|
|
|
14
14
|
#include "lsapilib.h"
|
|
15
|
+
#include <ctype.h>
|
|
15
16
|
#include <errno.h>
|
|
17
|
+
#include <inttypes.h>
|
|
18
|
+
#include <limits.h>
|
|
16
19
|
#include <netinet/in.h>
|
|
17
20
|
#include <netinet/tcp.h>
|
|
18
21
|
#include <signal.h>
|
|
22
|
+
#include <stdint.h>
|
|
23
|
+
#include <stdio.h>
|
|
24
|
+
#include <stdlib.h>
|
|
25
|
+
#include <string.h>
|
|
19
26
|
#include <sys/socket.h>
|
|
20
27
|
#include <sys/wait.h>
|
|
21
28
|
#include <unistd.h>
|
|
@@ -46,7 +53,7 @@ static VALUE env_copy;
|
|
|
46
53
|
|
|
47
54
|
static VALUE lsapi_env;
|
|
48
55
|
|
|
49
|
-
static
|
|
56
|
+
static size_t MAX_BODYBUF_LENGTH = (10 * 1024 * 1024);
|
|
50
57
|
|
|
51
58
|
#if RUBY_API_VERSION_CODE >= 20700
|
|
52
59
|
# if defined rb_tainted_str_new
|
|
@@ -65,6 +72,7 @@ typedef struct lsapi_data
|
|
|
65
72
|
}lsapi_data;
|
|
66
73
|
|
|
67
74
|
static VALUE cLSAPI;
|
|
75
|
+
static VALUE eLSAPIConnectionError;
|
|
68
76
|
|
|
69
77
|
static VALUE s_req = Qnil;
|
|
70
78
|
static lsapi_data * s_req_data;
|
|
@@ -76,9 +84,10 @@ static pid_t s_pid = 0;
|
|
|
76
84
|
typedef struct lsapi_body
|
|
77
85
|
{
|
|
78
86
|
char *bodyBuf; //we put small one into memory, otherwise, into a memory mapping file, and we still use the bodyBuf to access this mapping
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
87
|
+
off_t bodyLen; //expected length got form content-length
|
|
88
|
+
off_t bodyCurrentLen; //current length by read() readBodyToReqBuf
|
|
89
|
+
off_t curPos;
|
|
90
|
+
int isMapped;
|
|
82
91
|
}lsapi_body;
|
|
83
92
|
|
|
84
93
|
static lsapi_body s_body;
|
|
@@ -99,7 +108,8 @@ static char sTempFile[1024] = {0};
|
|
|
99
108
|
|
|
100
109
|
static void lsapi_mark( lsapi_data * data )
|
|
101
110
|
{
|
|
102
|
-
|
|
111
|
+
if (data)
|
|
112
|
+
rb_gc_mark( data->env );
|
|
103
113
|
}
|
|
104
114
|
/*
|
|
105
115
|
* static void lsapi_free_data( lsapi_data * data )
|
|
@@ -112,20 +122,22 @@ static int add_env_rails( const char * pKey, int keyLen, const char * pValue, in
|
|
|
112
122
|
{
|
|
113
123
|
char * p;
|
|
114
124
|
int len;
|
|
125
|
+
if (!pKey || keyLen <= 0 || !pValue || valLen < 0)
|
|
126
|
+
return 1;
|
|
115
127
|
/* Fixup some environment variables for rails */
|
|
116
128
|
switch( *pKey )
|
|
117
129
|
{
|
|
118
130
|
case 'Q':
|
|
119
|
-
if (
|
|
131
|
+
if (keyLen == 12 && memcmp(pKey, "QUERY_STRING", 12) == 0)
|
|
120
132
|
{
|
|
121
|
-
if (
|
|
133
|
+
if (valLen == 0)
|
|
122
134
|
return 1;
|
|
123
135
|
}
|
|
124
136
|
break;
|
|
125
137
|
case 'R':
|
|
126
|
-
if (
|
|
138
|
+
if (keyLen == 11 && memcmp(pKey, "REQUEST_URI", 11) == 0)
|
|
127
139
|
{
|
|
128
|
-
p =
|
|
140
|
+
p = memchr( pValue, '?', valLen );
|
|
129
141
|
if ( p )
|
|
130
142
|
{
|
|
131
143
|
len = valLen - ( p - pValue ) - 1;
|
|
@@ -143,21 +155,21 @@ static int add_env_rails( const char * pKey, int keyLen, const char * pValue, in
|
|
|
143
155
|
rb_tainted_str_new(pValue, p - pValue));
|
|
144
156
|
rb_hash_aset( lsapi_env,rb_tainted_str_new("REQUEST_PATH", 12),
|
|
145
157
|
rb_tainted_str_new(pValue, p - pValue));
|
|
146
|
-
if (
|
|
158
|
+
if (p != pValue + valLen)
|
|
147
159
|
++p;
|
|
148
160
|
rb_hash_aset( lsapi_env,rb_tainted_str_new("QUERY_STRING", 12),
|
|
149
161
|
rb_tainted_str_new(p, len));
|
|
150
162
|
}
|
|
151
163
|
break;
|
|
152
164
|
case 'S':
|
|
153
|
-
if (
|
|
165
|
+
if (keyLen == 11 && memcmp(pKey, "SCRIPT_NAME", 11) == 0)
|
|
154
166
|
{
|
|
155
167
|
pValue = "/";
|
|
156
168
|
valLen = 1;
|
|
157
169
|
}
|
|
158
170
|
break;
|
|
159
171
|
case 'P':
|
|
160
|
-
if (
|
|
172
|
+
if (keyLen == 9 && memcmp(pKey, "PATH_INFO", 9) == 0)
|
|
161
173
|
return 1;
|
|
162
174
|
default:
|
|
163
175
|
break;
|
|
@@ -173,6 +185,8 @@ static int add_env_rails( const char * pKey, int keyLen, const char * pValue, in
|
|
|
173
185
|
static int add_env_no_fix( const char * pKey, int keyLen, const char * pValue, int valLen,
|
|
174
186
|
void * arg )
|
|
175
187
|
{
|
|
188
|
+
if (!pKey || keyLen <= 0 || !pValue || valLen < 0)
|
|
189
|
+
return 1;
|
|
176
190
|
rb_hash_aset( lsapi_env,rb_tainted_str_new(pKey, keyLen),
|
|
177
191
|
rb_tainted_str_new(pValue, valLen));
|
|
178
192
|
return 1;
|
|
@@ -183,7 +197,7 @@ typedef int (*fn_add_env)( const char * pKey, int keyLen, const char * pValue, i
|
|
|
183
197
|
|
|
184
198
|
fn_add_env s_fn_add_env = add_env_no_fix;
|
|
185
199
|
|
|
186
|
-
static void clear_env()
|
|
200
|
+
static void clear_env(void)
|
|
187
201
|
{
|
|
188
202
|
/* rb_funcall( lsapi_env, rb_intern( "clear" ), 0 ); */
|
|
189
203
|
rb_funcall( lsapi_env, rb_intern( "replace" ), 1, env_copy );
|
|
@@ -197,21 +211,32 @@ static void setup_cgi_env( lsapi_data * data )
|
|
|
197
211
|
LSAPI_ForeachEnv_r( data->req, s_fn_add_env, data );
|
|
198
212
|
}
|
|
199
213
|
|
|
214
|
+
static void resetBodyBuf(void)
|
|
215
|
+
{
|
|
216
|
+
if (s_body.bodyBuf != NULL)
|
|
217
|
+
{
|
|
218
|
+
if (s_body.isMapped)
|
|
219
|
+
munmap(s_body.bodyBuf, (size_t)s_body.bodyLen);
|
|
220
|
+
else
|
|
221
|
+
free(s_body.bodyBuf);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
s_body.bodyBuf = NULL;
|
|
225
|
+
s_body.bodyLen = -1;
|
|
226
|
+
s_body.bodyCurrentLen = 0;
|
|
227
|
+
s_body.curPos = 0;
|
|
228
|
+
s_body.isMapped = 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
200
232
|
static VALUE lsapi_s_accept( VALUE self )
|
|
201
233
|
{
|
|
202
234
|
int pid;
|
|
235
|
+
resetBodyBuf();
|
|
203
236
|
if ( LSAPI_Prefork_Accept_r( &g_req ) == -1 )
|
|
204
237
|
return Qnil;
|
|
205
238
|
else
|
|
206
239
|
{
|
|
207
|
-
if (s_body.bodyBuf != NULL)
|
|
208
|
-
free (s_body.bodyBuf);
|
|
209
|
-
|
|
210
|
-
s_body.bodyBuf = NULL;
|
|
211
|
-
s_body.bodyLen = -1;
|
|
212
|
-
s_body.bodyCurrentLen = 0;
|
|
213
|
-
s_body.curPos = 0;
|
|
214
|
-
|
|
215
240
|
pid = getpid();
|
|
216
241
|
if ( pid != s_pid )
|
|
217
242
|
{
|
|
@@ -248,6 +273,13 @@ static VALUE lsapi_s_postfork_parent(VALUE self)
|
|
|
248
273
|
}
|
|
249
274
|
|
|
250
275
|
|
|
276
|
+
static VALUE lsapi_s_postfork_failed(VALUE self)
|
|
277
|
+
{
|
|
278
|
+
LSAPI_Postfork_Failed(&g_req);
|
|
279
|
+
return Qnil;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
|
|
251
283
|
/*
|
|
252
284
|
* static int chdir_file( const char * pFile )
|
|
253
285
|
* {
|
|
@@ -295,28 +327,77 @@ static VALUE lsapi_process( VALUE self )
|
|
|
295
327
|
}
|
|
296
328
|
|
|
297
329
|
|
|
330
|
+
//Output written while no request is active (application load, or after the
|
|
331
|
+
//last request) must not reach the LSAPI connection. m_fd alone is not
|
|
332
|
+
//enough: in connected/pipe mode it is set before any request is read.
|
|
333
|
+
//Response output is discarded, as it always has been, and stderr goes to the
|
|
334
|
+
//real fd 2. A failure during a request raises LSAPI::ConnectionError, so the
|
|
335
|
+
//runner can drop the request when the client goes away.
|
|
336
|
+
static inline int isRequestActive(lsapi_data *data)
|
|
337
|
+
{
|
|
338
|
+
return data->req->m_reqState != 0;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
static void raiseConnectionError(const char *message)
|
|
343
|
+
{
|
|
344
|
+
rb_raise(eLSAPIConnectionError, "%s", message);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
//write output produced outside a request; returns bytes consumed
|
|
349
|
+
static long writeInactive(lsapi_data *data, const char *pBuf, long len)
|
|
350
|
+
{
|
|
351
|
+
long done = 0;
|
|
352
|
+
ssize_t n;
|
|
353
|
+
|
|
354
|
+
if (data->fn_write == LSAPI_Write_r)
|
|
355
|
+
return len; //discarded, but consumed
|
|
356
|
+
while (done < len)
|
|
357
|
+
{
|
|
358
|
+
n = write(STDERR_FILENO, pBuf + done, (size_t)(len - done));
|
|
359
|
+
if (n > 0)
|
|
360
|
+
done += n;
|
|
361
|
+
else if (n == -1 && errno == EINTR)
|
|
362
|
+
continue;
|
|
363
|
+
else
|
|
364
|
+
rb_sys_fail("write to stderr");
|
|
365
|
+
}
|
|
366
|
+
return done;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
|
|
298
370
|
static VALUE lsapi_putc(VALUE self, VALUE c)
|
|
299
371
|
{
|
|
300
372
|
char ch = NUM2CHR(c);
|
|
301
373
|
lsapi_data *data;
|
|
302
374
|
Data_Get_Struct(self,lsapi_data, data);
|
|
375
|
+
if (!isRequestActive(data))
|
|
376
|
+
{
|
|
377
|
+
writeInactive(data, &ch, 1);
|
|
378
|
+
return c;
|
|
379
|
+
}
|
|
303
380
|
if ( (*data->fn_write)( data->req, &ch, 1 ) == 1 )
|
|
304
381
|
return c;
|
|
305
|
-
|
|
306
|
-
|
|
382
|
+
raiseConnectionError("LSAPI response write failed");
|
|
383
|
+
return Qnil;
|
|
307
384
|
}
|
|
308
385
|
|
|
309
386
|
|
|
310
387
|
static VALUE lsapi_write( VALUE self, VALUE str )
|
|
311
388
|
{
|
|
312
389
|
lsapi_data *data;
|
|
313
|
-
|
|
390
|
+
ssize_t len;
|
|
314
391
|
Data_Get_Struct(self,lsapi_data, data);
|
|
315
392
|
/* len = LSAPI_Write_r( data->req, RSTRING_PTR(str), RSTRING_LEN(str) ); */
|
|
316
393
|
if (TYPE(str) != T_STRING)
|
|
317
394
|
str = rb_obj_as_string(str);
|
|
395
|
+
if (!isRequestActive(data))
|
|
396
|
+
return LONG2NUM(writeInactive(data, RSTRING_PTR(str), RSTRING_LEN(str)));
|
|
318
397
|
len = (*data->fn_write)( data->req, RSTRING_PTR(str), RSTRING_LEN(str) );
|
|
319
|
-
|
|
398
|
+
if (len < 0)
|
|
399
|
+
raiseConnectionError("LSAPI response write failed");
|
|
400
|
+
return LONG2NUM((long)len);
|
|
320
401
|
}
|
|
321
402
|
|
|
322
403
|
static VALUE lsapi_print( int argc, VALUE *argv, VALUE out )
|
|
@@ -433,7 +514,8 @@ static VALUE lsapi_puts(int argc, VALUE *argv, VALUE out)
|
|
|
433
514
|
}
|
|
434
515
|
line = rb_obj_as_string(line);
|
|
435
516
|
lsapi_write(out, line);
|
|
436
|
-
if (
|
|
517
|
+
if (RSTRING_LEN(line) == 0 ||
|
|
518
|
+
*( RSTRING_PTR(line) + RSTRING_LEN(line) - 1 ) != '\n')
|
|
437
519
|
{
|
|
438
520
|
lsapi_write(out, rb_default_rs);
|
|
439
521
|
}
|
|
@@ -455,142 +537,195 @@ static VALUE lsapi_flush( VALUE self )
|
|
|
455
537
|
* lsapi_data *data;
|
|
456
538
|
* Data_Get_Struct(self,lsapi_data, data);
|
|
457
539
|
*/
|
|
458
|
-
|
|
540
|
+
int active = (g_req.m_reqState != 0);
|
|
541
|
+
if (LSAPI_Flush_r( &g_req ) == -1 && active)
|
|
542
|
+
raiseConnectionError("LSAPI response flush failed");
|
|
459
543
|
return Qnil;
|
|
460
544
|
}
|
|
461
545
|
|
|
462
|
-
static
|
|
546
|
+
static inline int isBodyWriteToFile(void)
|
|
463
547
|
{
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
* lsapi_data *data;
|
|
467
|
-
* Data_Get_Struct(self,lsapi_data, data);
|
|
468
|
-
*/
|
|
469
|
-
#if RUBY_API_VERSION_CODE < 20700
|
|
470
|
-
if (rb_safe_level() >= 4 && !OBJ_TAINTED(self))
|
|
471
|
-
{
|
|
472
|
-
rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
|
|
473
|
-
}
|
|
474
|
-
#endif
|
|
475
|
-
ch = LSAPI_ReqBodyGetChar_r( &g_req );
|
|
476
|
-
if ( ch == EOF )
|
|
477
|
-
return Qnil;
|
|
478
|
-
return INT2NUM( ch );
|
|
548
|
+
return s_body.bodyLen >= 0 &&
|
|
549
|
+
(uintmax_t)s_body.bodyLen >= (uintmax_t)MAX_BODYBUF_LENGTH;
|
|
479
550
|
}
|
|
480
551
|
|
|
481
|
-
static inline int isBodyWriteToFile()
|
|
482
|
-
{
|
|
483
|
-
return ((s_body.bodyLen >= MAX_BODYBUF_LENGTH)? (1): (0));
|
|
484
|
-
}
|
|
485
552
|
|
|
486
553
|
//create a temp file and open it, if failed, fd = -1
|
|
487
|
-
static inline int createTempFile()
|
|
554
|
+
static inline int createTempFile(void)
|
|
488
555
|
{
|
|
489
|
-
int fd
|
|
490
|
-
char
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
556
|
+
int fd;
|
|
557
|
+
char sfn[sizeof(sTempFile)];
|
|
558
|
+
|
|
559
|
+
memcpy(sfn, sTempFile, sizeof(sfn));
|
|
560
|
+
fd = mkstemp(sfn);
|
|
561
|
+
if (fd == -1)
|
|
494
562
|
fprintf(stderr, "%s: %s\n", sfn, strerror(errno));
|
|
495
|
-
}
|
|
496
563
|
else
|
|
497
564
|
unlink(sfn);
|
|
498
|
-
|
|
499
|
-
free(sfn);
|
|
565
|
+
|
|
500
566
|
return fd;
|
|
501
567
|
}
|
|
502
568
|
|
|
503
|
-
|
|
569
|
+
|
|
570
|
+
//return 1 if error occurred
|
|
504
571
|
//if already created, always OK (0)
|
|
505
|
-
static int createBodyBuf()
|
|
572
|
+
static int createBodyBuf(void)
|
|
506
573
|
{
|
|
507
|
-
int fd
|
|
508
|
-
|
|
574
|
+
int fd;
|
|
575
|
+
off_t bodyLen;
|
|
576
|
+
|
|
577
|
+
if (s_body.bodyLen != -1)
|
|
578
|
+
return 0;
|
|
579
|
+
|
|
580
|
+
bodyLen = LSAPI_GetReqBodyLen_r(&g_req);
|
|
581
|
+
/* A missing Content-Length is valid for requests whose transport has
|
|
582
|
+
already decoded framing (for example chunked HTTP/1 or HTTP/2). The
|
|
583
|
+
LSAPI peer normally supplies the decoded length; retain the historical
|
|
584
|
+
empty-body behavior when it is genuinely unavailable. */
|
|
585
|
+
if (bodyLen < 0)
|
|
586
|
+
bodyLen = 0;
|
|
587
|
+
if ((uintmax_t)bodyLen > (uintmax_t)SIZE_MAX ||
|
|
588
|
+
(uintmax_t)bodyLen > (uintmax_t)LONG_MAX)
|
|
589
|
+
{
|
|
590
|
+
errno = EOVERFLOW;
|
|
591
|
+
return 1;
|
|
592
|
+
}
|
|
593
|
+
s_body.bodyLen = bodyLen;
|
|
594
|
+
|
|
595
|
+
if (bodyLen == 0)
|
|
596
|
+
return 0;
|
|
597
|
+
|
|
598
|
+
if (isBodyWriteToFile())
|
|
509
599
|
{
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
600
|
+
fd = createTempFile();
|
|
601
|
+
if (fd == -1)
|
|
602
|
+
return 1;
|
|
603
|
+
if (ftruncate(fd, bodyLen) != 0)
|
|
513
604
|
{
|
|
514
|
-
|
|
515
|
-
|
|
605
|
+
int saved_errno = errno;
|
|
606
|
+
close(fd);
|
|
607
|
+
errno = saved_errno;
|
|
608
|
+
return 1;
|
|
516
609
|
}
|
|
517
|
-
|
|
518
|
-
|
|
610
|
+
s_body.bodyBuf = mmap(NULL, (size_t)bodyLen, PROT_READ | PROT_WRITE,
|
|
611
|
+
MAP_SHARED, fd, 0);
|
|
612
|
+
if (s_body.bodyBuf == MAP_FAILED)
|
|
519
613
|
{
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
{
|
|
526
|
-
return 1;
|
|
527
|
-
}
|
|
528
|
-
if (ftruncate(fd, s_body.bodyLen) == 0)
|
|
529
|
-
{
|
|
530
|
-
perror("ftruncate() failed. \n");
|
|
531
|
-
close(fd);
|
|
532
|
-
return 1;
|
|
533
|
-
}
|
|
534
|
-
s_body.bodyBuf = mmap(NULL, s_body.bodyLen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
535
|
-
if (s_body.bodyBuf == MAP_FAILED)
|
|
536
|
-
{
|
|
537
|
-
perror("File mapping failed. \n");
|
|
538
|
-
close(fd);
|
|
539
|
-
return 1;
|
|
540
|
-
}
|
|
541
|
-
close(fd); //close since needn't it anymore
|
|
542
|
-
}
|
|
543
|
-
else
|
|
544
|
-
{
|
|
545
|
-
s_body.bodyBuf = (char *)calloc(s_body.bodyLen, sizeof(char));
|
|
546
|
-
if (s_body.bodyBuf == NULL)
|
|
547
|
-
{
|
|
548
|
-
perror("Memory calloc error");
|
|
549
|
-
return 1;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
614
|
+
int saved_errno = errno;
|
|
615
|
+
s_body.bodyBuf = NULL;
|
|
616
|
+
close(fd);
|
|
617
|
+
errno = saved_errno;
|
|
618
|
+
return 1;
|
|
552
619
|
}
|
|
620
|
+
s_body.isMapped = 1;
|
|
621
|
+
close(fd);
|
|
622
|
+
}
|
|
623
|
+
else
|
|
624
|
+
{
|
|
625
|
+
s_body.bodyBuf = (char *)malloc((size_t)bodyLen);
|
|
626
|
+
if (s_body.bodyBuf == NULL)
|
|
627
|
+
return 1;
|
|
553
628
|
}
|
|
554
629
|
|
|
555
|
-
return 0;
|
|
630
|
+
return 0;
|
|
556
631
|
}
|
|
557
632
|
|
|
558
|
-
|
|
633
|
+
|
|
634
|
+
static void raiseBodyError(const char *message)
|
|
559
635
|
{
|
|
560
|
-
|
|
636
|
+
int saved_errno = errno;
|
|
637
|
+
resetBodyBuf();
|
|
638
|
+
errno = saved_errno ? saved_errno : EIO;
|
|
639
|
+
rb_sys_fail(message);
|
|
561
640
|
}
|
|
562
641
|
|
|
563
|
-
|
|
642
|
+
|
|
643
|
+
static inline int isAllBodyRead(void)
|
|
564
644
|
{
|
|
565
|
-
return
|
|
645
|
+
return s_body.bodyCurrentLen >= s_body.bodyLen;
|
|
566
646
|
}
|
|
567
|
-
|
|
568
|
-
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
static inline int isEofBodyBuf(void)
|
|
569
650
|
{
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
651
|
+
return s_body.bodyLen >= 0 && s_body.curPos >= s_body.bodyLen;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
//try to read length as multiples of 8KB
|
|
656
|
+
static ssize_t readBodyBuf(off_t needRead)
|
|
657
|
+
{
|
|
658
|
+
const off_t blockSize = 8192;
|
|
659
|
+
off_t available = s_body.bodyLen - s_body.bodyCurrentLen;
|
|
660
|
+
off_t readMore;
|
|
661
|
+
off_t remain;
|
|
662
|
+
ssize_t nRead;
|
|
578
663
|
|
|
579
|
-
if (
|
|
664
|
+
if (available <= 0)
|
|
580
665
|
return 0;
|
|
581
|
-
|
|
582
|
-
nRead = LSAPI_ReadReqBody_r(&g_req, buff, readMore);
|
|
583
|
-
if ( nRead > 0 )
|
|
584
|
-
s_body.bodyCurrentLen += nRead;
|
|
585
666
|
|
|
667
|
+
readMore = needRead;
|
|
668
|
+
if (readMore < blockSize)
|
|
669
|
+
readMore = blockSize;
|
|
670
|
+
else if (readMore % blockSize != 0 &&
|
|
671
|
+
readMore <= available - (blockSize - readMore % blockSize))
|
|
672
|
+
readMore += blockSize - readMore % blockSize;
|
|
673
|
+
if (readMore > available)
|
|
674
|
+
readMore = available;
|
|
675
|
+
|
|
676
|
+
remain = LSAPI_GetReqBodyRemain_r(&g_req);
|
|
677
|
+
if (remain <= 0)
|
|
678
|
+
{
|
|
679
|
+
errno = EIO;
|
|
680
|
+
return -1;
|
|
681
|
+
}
|
|
682
|
+
if (readMore > remain)
|
|
683
|
+
readMore = remain;
|
|
684
|
+
if ((uintmax_t)readMore > (uintmax_t)SSIZE_MAX)
|
|
685
|
+
readMore = SSIZE_MAX;
|
|
686
|
+
|
|
687
|
+
nRead = LSAPI_ReadReqBody_r(&g_req,
|
|
688
|
+
s_body.bodyBuf + (size_t)s_body.bodyCurrentLen,
|
|
689
|
+
(size_t)readMore);
|
|
690
|
+
if (nRead > 0)
|
|
691
|
+
s_body.bodyCurrentLen += nRead;
|
|
692
|
+
else
|
|
693
|
+
{
|
|
694
|
+
if (errno == 0)
|
|
695
|
+
errno = EIO;
|
|
696
|
+
return -1;
|
|
697
|
+
}
|
|
586
698
|
return nRead;
|
|
587
699
|
}
|
|
588
700
|
|
|
701
|
+
|
|
702
|
+
static VALUE lsapi_getc( VALUE self )
|
|
703
|
+
{
|
|
704
|
+
unsigned char ch;
|
|
705
|
+
#if RUBY_API_VERSION_CODE < 20700
|
|
706
|
+
if (rb_safe_level() >= 4 && !OBJ_TAINTED(self))
|
|
707
|
+
{
|
|
708
|
+
rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
|
|
709
|
+
}
|
|
710
|
+
#endif
|
|
711
|
+
if (createBodyBuf() != 0)
|
|
712
|
+
raiseBodyError("cannot allocate LSAPI request body buffer");
|
|
713
|
+
if (isEofBodyBuf())
|
|
714
|
+
return Qnil;
|
|
715
|
+
while (s_body.bodyCurrentLen <= s_body.curPos)
|
|
716
|
+
{
|
|
717
|
+
if (readBodyBuf(1) <= 0)
|
|
718
|
+
raiseBodyError("cannot read LSAPI request body");
|
|
719
|
+
}
|
|
720
|
+
ch = (unsigned char)s_body.bodyBuf[(size_t)s_body.curPos++];
|
|
721
|
+
return INT2NUM( ch );
|
|
722
|
+
}
|
|
723
|
+
|
|
589
724
|
static VALUE lsapi_gets( VALUE self )
|
|
590
725
|
{
|
|
591
726
|
VALUE str;
|
|
592
727
|
const int blkSize = 4096;
|
|
593
|
-
|
|
728
|
+
off_t n;
|
|
594
729
|
char *p = NULL;
|
|
595
730
|
|
|
596
731
|
#if RUBY_API_VERSION_CODE < 20700
|
|
@@ -599,34 +734,37 @@ static VALUE lsapi_gets( VALUE self )
|
|
|
599
734
|
rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
|
|
600
735
|
}
|
|
601
736
|
#endif
|
|
602
|
-
if (createBodyBuf()
|
|
603
|
-
|
|
737
|
+
if (createBodyBuf() != 0)
|
|
738
|
+
raiseBodyError("cannot allocate LSAPI request body buffer");
|
|
739
|
+
if (isEofBodyBuf())
|
|
604
740
|
return Qnil;
|
|
605
|
-
}
|
|
606
741
|
|
|
607
742
|
//comment:
|
|
608
|
-
while((
|
|
743
|
+
while(memchr(s_body.bodyBuf + (size_t)s_body.curPos, '\n',
|
|
744
|
+
(size_t)(s_body.bodyCurrentLen - s_body.curPos)) == NULL)
|
|
609
745
|
{
|
|
610
746
|
if (isAllBodyRead() == 1)
|
|
611
747
|
break;
|
|
612
748
|
//read one page and check, then reply
|
|
613
|
-
readBodyBuf(blkSize)
|
|
749
|
+
if (readBodyBuf(blkSize) <= 0)
|
|
750
|
+
raiseBodyError("cannot read LSAPI request body");
|
|
614
751
|
}
|
|
615
752
|
|
|
616
|
-
p =
|
|
753
|
+
p = memchr(s_body.bodyBuf + (size_t)s_body.curPos, '\n',
|
|
754
|
+
(size_t)(s_body.bodyCurrentLen - s_body.curPos));
|
|
617
755
|
if (p != NULL)
|
|
618
756
|
n = p - s_body.bodyBuf - s_body.curPos + 1;
|
|
619
757
|
else
|
|
620
758
|
n = s_body.bodyCurrentLen - s_body.curPos;
|
|
621
759
|
|
|
622
|
-
str = rb_str_buf_new(
|
|
760
|
+
str = rb_str_buf_new((long)n);
|
|
623
761
|
#if RUBY_API_VERSION_CODE < 20700
|
|
624
762
|
OBJ_TAINT(str);
|
|
625
763
|
#endif
|
|
626
764
|
|
|
627
765
|
if (n > 0)
|
|
628
766
|
{
|
|
629
|
-
rb_str_buf_cat(
|
|
767
|
+
rb_str_buf_cat(str, s_body.bodyBuf + (size_t)s_body.curPos, (long)n);
|
|
630
768
|
s_body.curPos += n;
|
|
631
769
|
}
|
|
632
770
|
|
|
@@ -636,9 +774,11 @@ static VALUE lsapi_gets( VALUE self )
|
|
|
636
774
|
static VALUE lsapi_read(int argc, VALUE *argv, VALUE self)
|
|
637
775
|
{
|
|
638
776
|
VALUE str;
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
777
|
+
VALUE length = Qnil;
|
|
778
|
+
VALUE outbuf = Qnil;
|
|
779
|
+
off_t n;
|
|
780
|
+
off_t target;
|
|
781
|
+
int length_given;
|
|
642
782
|
|
|
643
783
|
#if RUBY_API_VERSION_CODE < 20700
|
|
644
784
|
if (rb_safe_level() >= 4 && !OBJ_TAINTED(self))
|
|
@@ -646,54 +786,52 @@ static VALUE lsapi_read(int argc, VALUE *argv, VALUE self)
|
|
|
646
786
|
rb_raise(rb_eSecurityError, "Insecure: operation on untainted IO");
|
|
647
787
|
}
|
|
648
788
|
#endif
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
}
|
|
789
|
+
rb_scan_args(argc, argv, "02", &length, &outbuf);
|
|
790
|
+
if (createBodyBuf() != 0)
|
|
791
|
+
raiseBodyError("cannot allocate LSAPI request body buffer");
|
|
653
792
|
|
|
654
793
|
//we need to consider these 4 cases:
|
|
655
794
|
//1, need all data since argc == 0, we may have all data 2, or not
|
|
656
795
|
//3, need a length of data (argv >= 1), we may have enough data already read, 4, or not
|
|
657
|
-
|
|
796
|
+
length_given = argc > 0 && !NIL_P(length);
|
|
797
|
+
if (!length_given)
|
|
658
798
|
n = s_body.bodyLen - s_body.curPos;
|
|
659
799
|
else
|
|
660
800
|
{
|
|
661
|
-
|
|
801
|
+
long requested = NUM2LONG(length);
|
|
802
|
+
n = (off_t)requested;
|
|
662
803
|
if (n < 0)
|
|
663
|
-
|
|
804
|
+
rb_raise(rb_eArgError, "negative length %ld given", requested);
|
|
664
805
|
if (n > s_body.bodyLen - s_body.curPos)
|
|
665
|
-
n = s_body.bodyLen - s_body.curPos;
|
|
806
|
+
n = s_body.bodyLen - s_body.curPos;
|
|
666
807
|
}
|
|
667
|
-
|
|
668
|
-
if (
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
808
|
+
|
|
809
|
+
if (!NIL_P(outbuf))
|
|
810
|
+
{
|
|
811
|
+
StringValue(outbuf);
|
|
812
|
+
rb_str_resize(outbuf, 0);
|
|
813
|
+
str = outbuf;
|
|
814
|
+
}
|
|
815
|
+
else
|
|
816
|
+
str = rb_str_buf_new((long)n);
|
|
672
817
|
#if RUBY_API_VERSION_CODE < 20700
|
|
673
818
|
OBJ_TAINT(str);
|
|
674
819
|
#endif
|
|
675
820
|
if (n == 0)
|
|
676
|
-
return str;
|
|
677
|
-
|
|
678
|
-
//copy already have part first
|
|
679
|
-
if (n - needRead != 0)
|
|
680
821
|
{
|
|
681
|
-
|
|
682
|
-
|
|
822
|
+
if (length_given && NUM2LONG(length) != 0)
|
|
823
|
+
return Qnil;
|
|
824
|
+
return str;
|
|
683
825
|
}
|
|
684
|
-
|
|
685
|
-
|
|
826
|
+
|
|
827
|
+
target = s_body.curPos + n;
|
|
828
|
+
while (s_body.bodyCurrentLen < target)
|
|
686
829
|
{
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
if (nRead > 0)
|
|
690
|
-
{
|
|
691
|
-
n = ((nRead < needRead) ? nRead : needRead);
|
|
692
|
-
rb_str_buf_cat( str, s_body.bodyBuf + s_body.curPos, n );
|
|
693
|
-
s_body.curPos += n;
|
|
694
|
-
}
|
|
830
|
+
if (readBodyBuf(target - s_body.bodyCurrentLen) <= 0)
|
|
831
|
+
raiseBodyError("cannot read LSAPI request body");
|
|
695
832
|
}
|
|
696
|
-
|
|
833
|
+
rb_str_buf_cat(str, s_body.bodyBuf + (size_t)s_body.curPos, (long)n);
|
|
834
|
+
s_body.curPos += n;
|
|
697
835
|
return str;
|
|
698
836
|
}
|
|
699
837
|
|
|
@@ -711,6 +849,8 @@ static VALUE lsapi_each(VALUE self)
|
|
|
711
849
|
while(isEofBodyBuf() != 1)
|
|
712
850
|
{
|
|
713
851
|
str = lsapi_gets(self);
|
|
852
|
+
if (NIL_P(str))
|
|
853
|
+
break;
|
|
714
854
|
rb_yield(str);
|
|
715
855
|
}
|
|
716
856
|
return self;
|
|
@@ -719,7 +859,9 @@ static VALUE lsapi_each(VALUE self)
|
|
|
719
859
|
|
|
720
860
|
static VALUE lsapi_eof(VALUE self)
|
|
721
861
|
{
|
|
722
|
-
|
|
862
|
+
if (createBodyBuf() != 0)
|
|
863
|
+
raiseBodyError("cannot allocate LSAPI request body buffer");
|
|
864
|
+
return isEofBodyBuf() ? Qtrue : Qfalse;
|
|
723
865
|
}
|
|
724
866
|
|
|
725
867
|
static VALUE lsapi_binmode(VALUE self)
|
|
@@ -749,21 +891,11 @@ static VALUE lsapi_setsync(VALUE self,VALUE sync)
|
|
|
749
891
|
|
|
750
892
|
static VALUE lsapi_close(VALUE self)
|
|
751
893
|
{
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
munmap(s_body.bodyBuf, s_body.bodyLen);
|
|
758
|
-
}
|
|
759
|
-
else
|
|
760
|
-
free(s_body.bodyBuf);
|
|
761
|
-
|
|
762
|
-
s_body.bodyBuf = NULL;
|
|
763
|
-
s_body.bodyLen = -1;
|
|
764
|
-
s_body.bodyCurrentLen = 0;
|
|
765
|
-
s_body.curPos = 0;
|
|
766
|
-
//Should the temp be deleted here?!
|
|
894
|
+
int active = (g_req.m_reqState != 0);
|
|
895
|
+
int flushResult = LSAPI_Flush_r( &g_req );
|
|
896
|
+
resetBodyBuf();
|
|
897
|
+
if (flushResult == -1 && active)
|
|
898
|
+
raiseConnectionError("LSAPI response flush failed");
|
|
767
899
|
|
|
768
900
|
return Qnil;
|
|
769
901
|
}
|
|
@@ -788,49 +920,83 @@ static VALUE lsapi_reopen( int argc, VALUE *argv, VALUE self)
|
|
|
788
920
|
return self;
|
|
789
921
|
}
|
|
790
922
|
|
|
791
|
-
static void readMaxBodyBufLength()
|
|
923
|
+
static void readMaxBodyBufLength(void)
|
|
792
924
|
{
|
|
793
|
-
|
|
925
|
+
uintmax_t n;
|
|
926
|
+
uintmax_t multiplier = 1;
|
|
927
|
+
char *end;
|
|
794
928
|
const char *p = getenv( "LSAPI_MAX_BODYBUF_LENGTH" );
|
|
795
|
-
if (
|
|
929
|
+
if (p)
|
|
796
930
|
{
|
|
797
|
-
n
|
|
798
|
-
|
|
931
|
+
//accepts "<n>", "<n>K", "<n>M", with optional spaces and "B" suffix
|
|
932
|
+
while (*p == ' ')
|
|
933
|
+
++p;
|
|
934
|
+
if (!isdigit((unsigned char)*p))
|
|
935
|
+
return;
|
|
936
|
+
errno = 0;
|
|
937
|
+
n = strtoumax(p, &end, 10);
|
|
938
|
+
while (*end == ' ')
|
|
939
|
+
++end;
|
|
940
|
+
if (*end == 'M' || *end == 'm')
|
|
941
|
+
multiplier = 1024 * 1024;
|
|
942
|
+
else if (*end == 'K' || *end == 'k')
|
|
943
|
+
multiplier = 1024;
|
|
944
|
+
if (multiplier != 1)
|
|
799
945
|
{
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
MAX_BODYBUF_LENGTH = n * 1024;
|
|
804
|
-
else
|
|
805
|
-
MAX_BODYBUF_LENGTH = n;
|
|
946
|
+
++end;
|
|
947
|
+
if (*end == 'B' || *end == 'b')
|
|
948
|
+
++end;
|
|
806
949
|
}
|
|
950
|
+
while (*end == ' ')
|
|
951
|
+
++end;
|
|
952
|
+
if (errno == 0 && n > 0 && *end == '\0' &&
|
|
953
|
+
n <= (uintmax_t)SIZE_MAX / multiplier)
|
|
954
|
+
MAX_BODYBUF_LENGTH = (size_t)(n * multiplier);
|
|
807
955
|
}
|
|
808
956
|
}
|
|
809
957
|
|
|
810
|
-
static void readTempFileTemplate()
|
|
958
|
+
static void readTempFileTemplate(void)
|
|
811
959
|
{
|
|
812
960
|
const char *p = getenv( "LSAPI_TEMPFILE" );
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
961
|
+
size_t len;
|
|
962
|
+
|
|
963
|
+
if (p == NULL || *p == '\0')
|
|
964
|
+
p = "/tmp/lsapi.XXXXXX";
|
|
965
|
+
len = strlen(p);
|
|
966
|
+
if (len >= sizeof(sTempFile) ||
|
|
967
|
+
(len > sizeof(sTempFile) - sizeof(".XXXXXX") &&
|
|
968
|
+
(len < 6 || memcmp(p + len - 6, "XXXXXX", 6) != 0)))
|
|
969
|
+
{
|
|
970
|
+
p = "/tmp/lsapi.XXXXXX";
|
|
971
|
+
len = strlen(p);
|
|
972
|
+
}
|
|
973
|
+
if (len < 6 || memcmp(p + len - 6, "XXXXXX", 6) != 0)
|
|
974
|
+
{
|
|
975
|
+
snprintf(sTempFile, sizeof(sTempFile), "%s.XXXXXX", p);
|
|
976
|
+
}
|
|
977
|
+
else
|
|
978
|
+
{
|
|
979
|
+
snprintf(sTempFile, sizeof(sTempFile), "%s", p);
|
|
980
|
+
}
|
|
819
981
|
}
|
|
820
982
|
|
|
821
|
-
static void initBodyBuf()
|
|
983
|
+
static void initBodyBuf(void)
|
|
822
984
|
{
|
|
823
985
|
s_body.bodyBuf = NULL;
|
|
824
986
|
s_body.bodyLen = -1;
|
|
825
987
|
s_body.bodyCurrentLen = 0;
|
|
826
988
|
s_body.curPos = 0;
|
|
989
|
+
s_body.isMapped = 0;
|
|
827
990
|
}
|
|
828
|
-
void Init_lsapi()
|
|
991
|
+
void Init_lsapi(void)
|
|
829
992
|
{
|
|
830
993
|
VALUE orig_verbose;
|
|
831
994
|
char * p;
|
|
832
995
|
int prefork = 0;
|
|
833
|
-
|
|
996
|
+
if (geteuid() == 0)
|
|
997
|
+
rb_raise(rb_eSecurityError, "ruby-lsapi refuses to run as root");
|
|
998
|
+
if (LSAPI_Init() == -1)
|
|
999
|
+
rb_raise(rb_eRuntimeError, "failed to initialize LSAPI");
|
|
834
1000
|
initBodyBuf();
|
|
835
1001
|
|
|
836
1002
|
readMaxBodyBufLength();
|
|
@@ -841,9 +1007,11 @@ void Init_lsapi()
|
|
|
841
1007
|
prefork = 1;
|
|
842
1008
|
|
|
843
1009
|
#ifdef rb_thread_select
|
|
844
|
-
LSAPI_Init_Env_Parameters( rb_thread_select )
|
|
1010
|
+
if (LSAPI_Init_Env_Parameters( rb_thread_select ) == -1)
|
|
1011
|
+
rb_raise(rb_eRuntimeError, "failed to initialize LSAPI environment");
|
|
845
1012
|
#else
|
|
846
|
-
LSAPI_Init_Env_Parameters( select )
|
|
1013
|
+
if (LSAPI_Init_Env_Parameters( select ) == -1)
|
|
1014
|
+
rb_raise(rb_eRuntimeError, "failed to initialize LSAPI environment");
|
|
847
1015
|
#endif
|
|
848
1016
|
|
|
849
1017
|
s_pid = getpid();
|
|
@@ -852,7 +1020,7 @@ void Init_lsapi()
|
|
|
852
1020
|
if ( p )
|
|
853
1021
|
{
|
|
854
1022
|
if ( chdir( p ) == -1 )
|
|
855
|
-
|
|
1023
|
+
rb_sys_fail( "cannot chdir to RACK_ROOT" );
|
|
856
1024
|
}
|
|
857
1025
|
if ( p || getenv( "RACK_ENV" ) )
|
|
858
1026
|
s_fn_add_env = add_env_rails;
|
|
@@ -868,19 +1036,32 @@ void Init_lsapi()
|
|
|
868
1036
|
|
|
869
1037
|
/* tell the garbage collector it is a global variable, do not recycle it. */
|
|
870
1038
|
rb_global_variable(&env_copy);
|
|
1039
|
+
/* $stdin/$stdout/$stderr, STDERR and ENV are replaced below; keep the
|
|
1040
|
+
original objects alive. Ruby <= 2.0 only references its own STDERR
|
|
1041
|
+
through those, yet still writes errors to it when a process exits. */
|
|
1042
|
+
rb_global_variable(&orig_stdin);
|
|
1043
|
+
rb_global_variable(&orig_stdout);
|
|
1044
|
+
rb_global_variable(&orig_stderr);
|
|
1045
|
+
rb_global_variable(&orig_env);
|
|
871
1046
|
|
|
872
|
-
rb_hash_aset( env_copy,rb_tainted_str_new("
|
|
1047
|
+
rb_hash_aset( env_copy,rb_tainted_str_new("GATEWAY_INTERFACE", 17),
|
|
873
1048
|
rb_tainted_str_new("CGI/1.2", 7));
|
|
874
1049
|
|
|
875
1050
|
rb_define_global_function("eval_string_wrap", lsapi_eval_string_wrap, 1);
|
|
876
1051
|
|
|
877
1052
|
cLSAPI = rb_define_class("LSAPI", rb_cObject);
|
|
1053
|
+
/* instances only come from Data_Make_Struct() below */
|
|
1054
|
+
rb_undef_alloc_func(cLSAPI);
|
|
1055
|
+
/* raised when writing the response fails because the client is gone */
|
|
1056
|
+
eLSAPIConnectionError = rb_define_class_under(cLSAPI, "ConnectionError", rb_eIOError);
|
|
878
1057
|
rb_define_singleton_method(cLSAPI, "accept", lsapi_s_accept, 0);
|
|
879
|
-
|
|
1058
|
+
/* the prefork server only exists when listening on a socket */
|
|
1059
|
+
if (prefork && LSAPI_Is_Listen())
|
|
880
1060
|
{
|
|
881
1061
|
rb_define_singleton_method(cLSAPI, "accept_new_connection", lsapi_s_accept_new_conn, 0);
|
|
882
1062
|
rb_define_singleton_method(cLSAPI, "postfork_child", lsapi_s_postfork_child, 0);
|
|
883
1063
|
rb_define_singleton_method(cLSAPI, "postfork_parent", lsapi_s_postfork_parent, 0);
|
|
1064
|
+
rb_define_singleton_method(cLSAPI, "postfork_failed", lsapi_s_postfork_failed, 0);
|
|
884
1065
|
}
|
|
885
1066
|
|
|
886
1067
|
rb_define_method(cLSAPI, "process", lsapi_process, 0 );
|
|
@@ -917,6 +1098,7 @@ void Init_lsapi()
|
|
|
917
1098
|
|
|
918
1099
|
s_req = Data_Make_Struct( cLSAPI, lsapi_data, lsapi_mark, free, s_req_data );
|
|
919
1100
|
s_req_data->req = &g_req;
|
|
1101
|
+
s_req_data->env = Qnil;
|
|
920
1102
|
s_req_data->fn_write = LSAPI_Write_r;
|
|
921
1103
|
rb_stdin = rb_stdout = s_req;
|
|
922
1104
|
|
|
@@ -928,6 +1110,7 @@ void Init_lsapi()
|
|
|
928
1110
|
|
|
929
1111
|
s_req_stderr = Data_Make_Struct( cLSAPI, lsapi_data, lsapi_mark, free, s_stderr_data );
|
|
930
1112
|
s_stderr_data->req = &g_req;
|
|
1113
|
+
s_stderr_data->env = Qnil;
|
|
931
1114
|
s_stderr_data->fn_write = LSAPI_Write_Stderr_r;
|
|
932
1115
|
rb_stderr = s_req_stderr;
|
|
933
1116
|
rb_global_variable(&s_req_stderr );
|
|
@@ -937,6 +1120,8 @@ void Init_lsapi()
|
|
|
937
1120
|
ruby_verbose = Qnil;
|
|
938
1121
|
|
|
939
1122
|
lsapi_env = rb_hash_new();
|
|
1123
|
+
s_req_data->env = lsapi_env;
|
|
1124
|
+
s_stderr_data->env = lsapi_env;
|
|
940
1125
|
clear_env();
|
|
941
1126
|
/* redefine ENV using a hash table, should be faster than char **environment */
|
|
942
1127
|
rb_define_global_const("ENV", lsapi_env);
|
|
@@ -947,4 +1132,3 @@ void Init_lsapi()
|
|
|
947
1132
|
|
|
948
1133
|
return;
|
|
949
1134
|
}
|
|
950
|
-
|