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.
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 int MAX_BODYBUF_LENGTH = (10 * 1024 * 1024);
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
- int bodyLen; //expected length got form content-length
80
- int bodyCurrentLen; //current length by read() readBodyToReqBuf
81
- int curPos;
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
- rb_gc_mark( data->env );
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 ( strcmp( pKey, "QUERY_STRING" ) == 0 )
131
+ if (keyLen == 12 && memcmp(pKey, "QUERY_STRING", 12) == 0)
120
132
  {
121
- if ( !*pValue )
133
+ if (valLen == 0)
122
134
  return 1;
123
135
  }
124
136
  break;
125
137
  case 'R':
126
- if (( *(pKey+8) == 'U' )&&( strcmp( pKey, "REQUEST_URI" ) == 0 ))
138
+ if (keyLen == 11 && memcmp(pKey, "REQUEST_URI", 11) == 0)
127
139
  {
128
- p = strchr( pValue, '?' );
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 ( *p == '?' )
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 ( strcmp( pKey, "SCRIPT_NAME" ) == 0 )
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 ( strcmp( pKey, "PATH_INFO" ) == 0 )
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
- else
306
- return INT2NUM( EOF );
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
- int len;
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
- return INT2NUM( len );
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 (*( RSTRING_PTR(line) + RSTRING_LEN(line) - 1 ) != '\n')
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
- LSAPI_Flush_r( &g_req );
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 VALUE lsapi_getc( VALUE self )
546
+ static inline int isBodyWriteToFile(void)
463
547
  {
464
- int ch;
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 = -1;
490
- char *sfn = strdup(sTempFile);
491
-
492
- if ((fd = mkstemp(sfn)) == -1)
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
- //return 1 if error occured!
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 = -1;
508
- if (s_body.bodyLen == -1)
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
- s_body.bodyLen = LSAPI_GetReqBodyLen_r(&g_req);
511
- //Error if get a zeor length, should not happen
512
- if (s_body.bodyLen < 0)
600
+ fd = createTempFile();
601
+ if (fd == -1)
602
+ return 1;
603
+ if (ftruncate(fd, bodyLen) != 0)
513
604
  {
514
- //Wrong bode length will be treated as 0
515
- s_body.bodyLen = 0;
605
+ int saved_errno = errno;
606
+ close(fd);
607
+ errno = saved_errno;
608
+ return 1;
516
609
  }
517
-
518
- if (s_body.bodyLen > 0)
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
- if (isBodyWriteToFile())
521
- {
522
- //create file mapping
523
- fd = createTempFile();
524
- if (fd == -1)
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
- static inline int isAllBodyRead()
633
+
634
+ static void raiseBodyError(const char *message)
559
635
  {
560
- return (s_body.bodyCurrentLen < s_body.bodyLen)? 0 : 1;
636
+ int saved_errno = errno;
637
+ resetBodyBuf();
638
+ errno = saved_errno ? saved_errno : EIO;
639
+ rb_sys_fail(message);
561
640
  }
562
641
 
563
- static inline int isEofBodyBuf()
642
+
643
+ static inline int isAllBodyRead(void)
564
644
  {
565
- return (s_body.curPos < s_body.bodyLen) ? 0 : 1;
645
+ return s_body.bodyCurrentLen >= s_body.bodyLen;
566
646
  }
567
- //try to read length as times pagesize (such as 8KB * N)
568
- static int readBodyBuf(const int needRead)
647
+
648
+
649
+ static inline int isEofBodyBuf(void)
569
650
  {
570
- const int blockSize = 8192;
571
- char *buff = s_body.bodyBuf + s_body.bodyCurrentLen;
572
- int nRead;
573
- int readMore = (needRead + blockSize -1) / blockSize * blockSize;
574
- int remain = LSAPI_GetReqBodyRemain_r( &g_req );
575
- //Only when not enough left, needReadChange will be changed!!!
576
- if (remain < readMore)
577
- readMore = remain;
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 ( readMore <= 0 )
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
- int n;
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() == 1)
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((p = memmem(s_body.bodyBuf + s_body.curPos, s_body.bodyCurrentLen - s_body.curPos, "\n", 1)) == NULL)
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 = memmem(s_body.bodyBuf + s_body.curPos, s_body.bodyCurrentLen - s_body.curPos, "\n", 1);
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( n );
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( str, s_body.bodyBuf + s_body.curPos, n );
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
- int n;
640
- int needRead;
641
- int nRead;
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
- if (createBodyBuf() == 1)
650
- {
651
- return Qnil;
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
- if (argc == 0)
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
- n = NUM2INT(argv[0]); //request that length from currentpos
801
+ long requested = NUM2LONG(length);
802
+ n = (off_t)requested;
662
803
  if (n < 0)
663
- return Qnil;
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
- needRead = s_body.curPos + n - s_body.bodyCurrentLen;
668
- if (needRead < 0)
669
- needRead = 0;
670
-
671
- str = rb_str_buf_new( n );
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
- rb_str_buf_cat( str, s_body.bodyBuf + s_body.curPos, n - needRead);
682
- s_body.curPos += (n - needRead);
822
+ if (length_given && NUM2LONG(length) != 0)
823
+ return Qnil;
824
+ return str;
683
825
  }
684
-
685
- if (needRead > 0)
826
+
827
+ target = s_body.curPos + n;
828
+ while (s_body.bodyCurrentLen < target)
686
829
  {
687
- //try to read needRead, but may be less (changed) when read the end of the data
688
- nRead = readBodyBuf(needRead);
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
- return (LSAPI_GetReqBodyRemain_r( &g_req ) <= 0) ? Qtrue : Qfalse;
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
- LSAPI_Flush_r( &g_req );
753
- if (isBodyWriteToFile())
754
- {
755
- //msync(s_body.bodyBuf, s_body.bodyLen, MS_SYNC);
756
- //sleep(5);
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
- int n;
925
+ uintmax_t n;
926
+ uintmax_t multiplier = 1;
927
+ char *end;
794
928
  const char *p = getenv( "LSAPI_MAX_BODYBUF_LENGTH" );
795
- if ( p )
929
+ if (p)
796
930
  {
797
- n = atoi( p );
798
- if (n > 0)
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
- if (strstr(p, "M") || strstr(p, "m"))
801
- MAX_BODYBUF_LENGTH = n * 1024 * 1024;
802
- else if (strstr(p, "K") || strstr(p, "k"))
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
- if (p == NULL || strlen(p) > 1024 - 7)
814
- p = "/tmp/lsapi.XXXXXX";
815
-
816
- strcpy(sTempFile, p);
817
- if (strlen(p) <= 6 || strcmp(p + (strlen(p) - 6), "XXXXXX") != 0)
818
- strcat(sTempFile, ".XXXXXX");
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
- LSAPI_Init();
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
- perror( "chdir()" );
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("GATEWAY_Irewindable_input.rbNTERFACE", 17),
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
- if (prefork)
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
-