ruby-lsapi 5.7 → 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)
509
589
  {
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)
590
+ errno = EOVERFLOW;
591
+ return 1;
592
+ }
593
+ s_body.bodyLen = bodyLen;
594
+
595
+ if (bodyLen == 0)
596
+ return 0;
597
+
598
+ if (isBodyWriteToFile())
599
+ {
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)
@@ -732,6 +874,11 @@ static VALUE lsapi_isatty(VALUE self)
732
874
  return Qfalse;
733
875
  }
734
876
 
877
+ static VALUE lsapi_isclosed(VALUE self)
878
+ {
879
+ return Qfalse;
880
+ }
881
+
735
882
  static VALUE lsapi_sync(VALUE self)
736
883
  {
737
884
  return Qfalse;
@@ -744,21 +891,11 @@ static VALUE lsapi_setsync(VALUE self,VALUE sync)
744
891
 
745
892
  static VALUE lsapi_close(VALUE self)
746
893
  {
747
- LSAPI_Flush_r( &g_req );
748
- if (isBodyWriteToFile())
749
- {
750
- //msync(s_body.bodyBuf, s_body.bodyLen, MS_SYNC);
751
- //sleep(5);
752
- munmap(s_body.bodyBuf, s_body.bodyLen);
753
- }
754
- else
755
- free(s_body.bodyBuf);
756
-
757
- s_body.bodyBuf = NULL;
758
- s_body.bodyLen = -1;
759
- s_body.bodyCurrentLen = 0;
760
- s_body.curPos = 0;
761
- //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");
762
899
 
763
900
  return Qnil;
764
901
  }
@@ -783,49 +920,83 @@ static VALUE lsapi_reopen( int argc, VALUE *argv, VALUE self)
783
920
  return self;
784
921
  }
785
922
 
786
- static void readMaxBodyBufLength()
923
+ static void readMaxBodyBufLength(void)
787
924
  {
788
- int n;
925
+ uintmax_t n;
926
+ uintmax_t multiplier = 1;
927
+ char *end;
789
928
  const char *p = getenv( "LSAPI_MAX_BODYBUF_LENGTH" );
790
- if ( p )
929
+ if (p)
791
930
  {
792
- n = atoi( p );
793
- 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)
794
945
  {
795
- if (strstr(p, "M") || strstr(p, "m"))
796
- MAX_BODYBUF_LENGTH = n * 1024 * 1024;
797
- else if (strstr(p, "K") || strstr(p, "k"))
798
- MAX_BODYBUF_LENGTH = n * 1024;
799
- else
800
- MAX_BODYBUF_LENGTH = n;
946
+ ++end;
947
+ if (*end == 'B' || *end == 'b')
948
+ ++end;
801
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);
802
955
  }
803
956
  }
804
957
 
805
- static void readTempFileTemplate()
958
+ static void readTempFileTemplate(void)
806
959
  {
807
960
  const char *p = getenv( "LSAPI_TEMPFILE" );
808
- if (p == NULL || strlen(p) > 1024 - 7)
809
- p = "/tmp/lsapi.XXXXXX";
810
-
811
- strcpy(sTempFile, p);
812
- if (strlen(p) <= 6 || strcmp(p + (strlen(p) - 6), "XXXXXX") != 0)
813
- 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
+ }
814
981
  }
815
982
 
816
- static void initBodyBuf()
983
+ static void initBodyBuf(void)
817
984
  {
818
985
  s_body.bodyBuf = NULL;
819
986
  s_body.bodyLen = -1;
820
987
  s_body.bodyCurrentLen = 0;
821
988
  s_body.curPos = 0;
989
+ s_body.isMapped = 0;
822
990
  }
823
- void Init_lsapi()
991
+ void Init_lsapi(void)
824
992
  {
825
993
  VALUE orig_verbose;
826
994
  char * p;
827
995
  int prefork = 0;
828
- 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");
829
1000
  initBodyBuf();
830
1001
 
831
1002
  readMaxBodyBufLength();
@@ -836,9 +1007,11 @@ void Init_lsapi()
836
1007
  prefork = 1;
837
1008
 
838
1009
  #ifdef rb_thread_select
839
- 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");
840
1012
  #else
841
- LSAPI_Init_Env_Parameters( select );
1013
+ if (LSAPI_Init_Env_Parameters( select ) == -1)
1014
+ rb_raise(rb_eRuntimeError, "failed to initialize LSAPI environment");
842
1015
  #endif
843
1016
 
844
1017
  s_pid = getpid();
@@ -847,7 +1020,7 @@ void Init_lsapi()
847
1020
  if ( p )
848
1021
  {
849
1022
  if ( chdir( p ) == -1 )
850
- perror( "chdir()" );
1023
+ rb_sys_fail( "cannot chdir to RACK_ROOT" );
851
1024
  }
852
1025
  if ( p || getenv( "RACK_ENV" ) )
853
1026
  s_fn_add_env = add_env_rails;
@@ -863,19 +1036,32 @@ void Init_lsapi()
863
1036
 
864
1037
  /* tell the garbage collector it is a global variable, do not recycle it. */
865
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);
866
1046
 
867
- 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),
868
1048
  rb_tainted_str_new("CGI/1.2", 7));
869
1049
 
870
1050
  rb_define_global_function("eval_string_wrap", lsapi_eval_string_wrap, 1);
871
1051
 
872
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);
873
1057
  rb_define_singleton_method(cLSAPI, "accept", lsapi_s_accept, 0);
874
- if (prefork)
1058
+ /* the prefork server only exists when listening on a socket */
1059
+ if (prefork && LSAPI_Is_Listen())
875
1060
  {
876
1061
  rb_define_singleton_method(cLSAPI, "accept_new_connection", lsapi_s_accept_new_conn, 0);
877
1062
  rb_define_singleton_method(cLSAPI, "postfork_child", lsapi_s_postfork_child, 0);
878
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);
879
1065
  }
880
1066
 
881
1067
  rb_define_method(cLSAPI, "process", lsapi_process, 0 );
@@ -901,7 +1087,7 @@ void Init_lsapi()
901
1087
  rb_define_method(cLSAPI, "eof", lsapi_eof, 0);
902
1088
  rb_define_method(cLSAPI, "eof?", lsapi_eof, 0);
903
1089
  rb_define_method(cLSAPI, "close", lsapi_close, 0);
904
- /* rb_define_method(cLSAPI, "closed?", lsapi_closed, 0); */
1090
+ rb_define_method(cLSAPI, "closed?", lsapi_isclosed, 0);
905
1091
  rb_define_method(cLSAPI, "binmode", lsapi_binmode, 0);
906
1092
  rb_define_method(cLSAPI, "isatty", lsapi_isatty, 0);
907
1093
  rb_define_method(cLSAPI, "tty?", lsapi_isatty, 0);
@@ -912,6 +1098,7 @@ void Init_lsapi()
912
1098
 
913
1099
  s_req = Data_Make_Struct( cLSAPI, lsapi_data, lsapi_mark, free, s_req_data );
914
1100
  s_req_data->req = &g_req;
1101
+ s_req_data->env = Qnil;
915
1102
  s_req_data->fn_write = LSAPI_Write_r;
916
1103
  rb_stdin = rb_stdout = s_req;
917
1104
 
@@ -923,6 +1110,7 @@ void Init_lsapi()
923
1110
 
924
1111
  s_req_stderr = Data_Make_Struct( cLSAPI, lsapi_data, lsapi_mark, free, s_stderr_data );
925
1112
  s_stderr_data->req = &g_req;
1113
+ s_stderr_data->env = Qnil;
926
1114
  s_stderr_data->fn_write = LSAPI_Write_Stderr_r;
927
1115
  rb_stderr = s_req_stderr;
928
1116
  rb_global_variable(&s_req_stderr );
@@ -932,6 +1120,8 @@ void Init_lsapi()
932
1120
  ruby_verbose = Qnil;
933
1121
 
934
1122
  lsapi_env = rb_hash_new();
1123
+ s_req_data->env = lsapi_env;
1124
+ s_stderr_data->env = lsapi_env;
935
1125
  clear_env();
936
1126
  /* redefine ENV using a hash table, should be faster than char **environment */
937
1127
  rb_define_global_const("ENV", lsapi_env);
@@ -942,4 +1132,3 @@ void Init_lsapi()
942
1132
 
943
1133
  return;
944
1134
  }
945
-