passenger 2.2.4 → 2.2.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (105) hide show
  1. data/NEWS +137 -0
  2. data/Rakefile +101 -19
  3. data/bin/passenger-install-nginx-module +10 -3
  4. data/bin/passenger-make-enterprisey +1 -1
  5. data/doc/Users guide Apache.html +227 -92
  6. data/doc/Users guide Apache.txt +169 -75
  7. data/doc/Users guide Nginx.html +1 -1
  8. data/doc/cxxapi/Bucket_8h-source.html +1 -1
  9. data/doc/cxxapi/Configuration_8h-source.html +373 -338
  10. data/doc/cxxapi/DirectoryMapper_8h-source.html +1 -1
  11. data/doc/cxxapi/Hooks_8h-source.html +1 -1
  12. data/doc/cxxapi/annotated.html +1 -1
  13. data/doc/cxxapi/classHooks-members.html +1 -1
  14. data/doc/cxxapi/classHooks.html +2 -2
  15. data/doc/cxxapi/classPassenger_1_1DirectoryMapper-members.html +1 -1
  16. data/doc/cxxapi/classPassenger_1_1DirectoryMapper.html +1 -1
  17. data/doc/cxxapi/classes.html +1 -1
  18. data/doc/cxxapi/definitions_8h-source.html +1 -1
  19. data/doc/cxxapi/files.html +1 -1
  20. data/doc/cxxapi/functions.html +1 -1
  21. data/doc/cxxapi/functions_func.html +1 -1
  22. data/doc/cxxapi/graph_legend.html +1 -1
  23. data/doc/cxxapi/group__Configuration.html +1 -1
  24. data/doc/cxxapi/group__Core.html +1 -1
  25. data/doc/cxxapi/group__Hooks.html +1 -1
  26. data/doc/cxxapi/group__Support.html +1 -1
  27. data/doc/cxxapi/main.html +1 -1
  28. data/doc/cxxapi/modules.html +1 -1
  29. data/doc/rdoc/classes/ConditionVariable.html +59 -59
  30. data/doc/rdoc/classes/Exception.html +11 -11
  31. data/doc/rdoc/classes/GC.html +4 -4
  32. data/doc/rdoc/classes/IO.html +14 -14
  33. data/doc/rdoc/classes/PhusionPassenger.html +1 -1
  34. data/doc/rdoc/classes/PhusionPassenger/AbstractInstaller.html +8 -8
  35. data/doc/rdoc/classes/PhusionPassenger/AbstractRequestHandler.html +136 -136
  36. data/doc/rdoc/classes/PhusionPassenger/AbstractServer.html +254 -254
  37. data/doc/rdoc/classes/PhusionPassenger/AbstractServerCollection.html +61 -61
  38. data/doc/rdoc/classes/PhusionPassenger/AppInitError.html +4 -4
  39. data/doc/rdoc/classes/PhusionPassenger/Application.html +14 -14
  40. data/doc/rdoc/classes/PhusionPassenger/ConsoleTextTemplate.html +12 -12
  41. data/doc/rdoc/classes/PhusionPassenger/FrameworkInitError.html +4 -4
  42. data/doc/rdoc/classes/PhusionPassenger/HTMLTemplate.html +12 -12
  43. data/doc/rdoc/classes/PhusionPassenger/InitializationError.html +5 -5
  44. data/doc/rdoc/classes/PhusionPassenger/MessageChannel.html +139 -139
  45. data/doc/rdoc/classes/PhusionPassenger/Rack/RequestHandler.html +61 -56
  46. data/doc/rdoc/classes/PhusionPassenger/Railz/ApplicationSpawner.html +3 -3
  47. data/doc/rdoc/classes/PhusionPassenger/Railz/FrameworkSpawner.html +7 -7
  48. data/doc/rdoc/classes/PhusionPassenger/Railz/RequestHandler.html +15 -15
  49. data/doc/rdoc/classes/PhusionPassenger/SpawnManager.html +119 -119
  50. data/doc/rdoc/classes/PhusionPassenger/UnknownError.html +4 -4
  51. data/doc/rdoc/classes/PhusionPassenger/Utils.html +310 -312
  52. data/doc/rdoc/classes/PhusionPassenger/Utils/PseudoIO.html +169 -0
  53. data/doc/rdoc/classes/PhusionPassenger/VersionNotFound.html +4 -4
  54. data/doc/rdoc/classes/PlatformInfo.html +165 -164
  55. data/doc/rdoc/classes/Signal.html +23 -23
  56. data/doc/rdoc/created.rid +1 -1
  57. data/doc/rdoc/files/ext/phusion_passenger/native_support_c.html +1 -1
  58. data/doc/rdoc/files/lib/phusion_passenger/abstract_request_handler_rb.html +26 -28
  59. data/doc/rdoc/files/lib/phusion_passenger/abstract_server_rb.html +26 -28
  60. data/doc/rdoc/files/lib/phusion_passenger/admin_tools/control_process_rb.html +1 -1
  61. data/doc/rdoc/files/lib/phusion_passenger/constants_rb.html +1 -1
  62. data/doc/rdoc/files/lib/phusion_passenger/message_channel_rb.html +26 -28
  63. data/doc/rdoc/files/lib/phusion_passenger/platform_info_rb.html +1 -1
  64. data/doc/rdoc/files/lib/phusion_passenger/rack/application_spawner_rb.html +1 -1
  65. data/doc/rdoc/files/lib/phusion_passenger/rack/request_handler_rb.html +26 -28
  66. data/doc/rdoc/files/lib/phusion_passenger/railz/application_spawner_rb.html +1 -2
  67. data/doc/rdoc/files/lib/phusion_passenger/railz/framework_spawner_rb.html +1 -1
  68. data/doc/rdoc/files/lib/phusion_passenger/railz/request_handler_rb.html +26 -28
  69. data/doc/rdoc/files/lib/phusion_passenger/spawn_manager_rb.html +26 -28
  70. data/doc/rdoc/files/lib/phusion_passenger/utils_rb.html +34 -36
  71. data/doc/rdoc/fr_class_index.html +1 -0
  72. data/doc/rdoc/fr_method_index.html +72 -68
  73. data/ext/apache2/Configuration.cpp +69 -15
  74. data/ext/apache2/Configuration.h +37 -2
  75. data/ext/apache2/Hooks.cpp +167 -59
  76. data/ext/common/ApplicationPoolServerExecutable.cpp +1 -1
  77. data/ext/common/MessageChannel.h +4 -4
  78. data/ext/common/StandardApplicationPool.h +1 -1
  79. data/ext/common/Timer.h +2 -0
  80. data/ext/common/Version.h +1 -1
  81. data/ext/nginx/Configuration.c +3 -3
  82. data/ext/nginx/ContentHandler.c +16 -4
  83. data/ext/nginx/HelperServer.cpp +1 -1
  84. data/ext/oxt/system_calls.cpp +6 -1
  85. data/ext/oxt/thread.hpp +17 -2
  86. data/ext/phusion_passenger/native_support.c +4 -4
  87. data/lib/phusion_passenger/abstract_request_handler.rb +3 -3
  88. data/lib/phusion_passenger/abstract_server.rb +1 -0
  89. data/lib/phusion_passenger/constants.rb +1 -1
  90. data/lib/phusion_passenger/message_channel.rb +1 -0
  91. data/lib/phusion_passenger/platform_info.rb +3 -2
  92. data/lib/phusion_passenger/rack/request_handler.rb +11 -7
  93. data/lib/phusion_passenger/railz/application_spawner.rb +7 -4
  94. data/lib/phusion_passenger/railz/request_handler.rb +1 -0
  95. data/lib/phusion_passenger/spawn_manager.rb +1 -0
  96. data/lib/phusion_passenger/utils.rb +38 -20
  97. data/test/integration_tests/apache2_tests.rb +162 -100
  98. data/test/integration_tests/mycook_spec.rb +63 -62
  99. data/test/integration_tests/nginx_tests.rb +12 -5
  100. data/test/ruby/utils_spec.rb +98 -14
  101. data/test/stub/apache2/httpd.conf.erb +2 -1
  102. data/test/stub/rails_apps/mycook/app/controllers/welcome_controller.rb +8 -0
  103. data/test/support/apache2_controller.rb +5 -1
  104. data/test/support/test_helper.rb +42 -13
  105. metadata +7 -137
@@ -28,6 +28,7 @@
28
28
  #ifdef __cplusplus
29
29
  #include "Utils.h"
30
30
  #include "MessageChannel.h"
31
+ #include "Logging.h"
31
32
  #endif
32
33
 
33
34
  /* The APR headers must come after the Passenger headers. See Hooks.cpp
@@ -134,11 +135,25 @@
134
135
  * in the directory configuration. */
135
136
  bool memoryLimitSpecified;
136
137
 
138
+ /** Whether symlinks in the document root path should be resolved.
139
+ * The implication of this is documented in the users guide, section
140
+ * "How Phusion Passenger detects whether a virtual host is a web application".
141
+ */
142
+ Threeway resolveSymlinksInDocRoot;
143
+
144
+ /** Whether high performance mode should be turned on. */
137
145
  Threeway highPerformance;
138
146
 
139
147
  /** Whether global queuing should be used. */
140
148
  Threeway useGlobalQueue;
141
149
 
150
+ /**
151
+ * Whether encoded slashes in URLs should be supported. This however conflicts
152
+ * with mod_rewrite support because of a bug/limitation in Apache, so it's one
153
+ * or the other.
154
+ */
155
+ Threeway allowEncodedSlashes;
156
+
142
157
  /**
143
158
  * Throttle the number of stat() calls on files like
144
159
  * restart.txt to the once per given number of seconds.
@@ -170,7 +185,11 @@
170
185
 
171
186
  string getAppRoot(const char *documentRoot) const {
172
187
  if (appRoot == NULL) {
173
- return extractDirName(documentRoot);
188
+ if (resolveSymlinksInDocRoot == DirConfig::ENABLED) {
189
+ return extractDirName(resolveSymlink(documentRoot));
190
+ } else {
191
+ return extractDirName(documentRoot);
192
+ }
174
193
  } else {
175
194
  return appRoot;
176
195
  }
@@ -178,7 +197,11 @@
178
197
 
179
198
  string getAppRoot(const string &documentRoot) const {
180
199
  if (appRoot == NULL) {
181
- return extractDirName(documentRoot);
200
+ if (resolveSymlinksInDocRoot == DirConfig::ENABLED) {
201
+ return extractDirName(resolveSymlink(documentRoot));
202
+ } else {
203
+ return extractDirName(documentRoot);
204
+ }
182
205
  } else {
183
206
  return appRoot;
184
207
  }
@@ -237,6 +260,10 @@
237
260
  return useGlobalQueue == ENABLED;
238
261
  }
239
262
 
263
+ bool allowsEncodedSlashes() const {
264
+ return allowEncodedSlashes == ENABLED;
265
+ }
266
+
240
267
  unsigned long getStatThrottleRate() const {
241
268
  if (statThrottleRateSpecified) {
242
269
  return statThrottleRate;
@@ -321,6 +348,14 @@
321
348
  */
322
349
  const char *tempDir;
323
350
 
351
+ const char *getRuby() const {
352
+ if (ruby != NULL) {
353
+ return ruby;
354
+ } else {
355
+ return "ruby";
356
+ }
357
+ }
358
+
324
359
  const char *getDefaultUser() const {
325
360
  if (defaultUser != NULL) {
326
361
  return defaultUser;
@@ -68,14 +68,24 @@ using namespace Passenger;
68
68
  extern "C" module AP_MODULE_DECLARE_DATA passenger_module;
69
69
 
70
70
 
71
- #define DEFAULT_RUBY_COMMAND "ruby"
71
+ /*
72
+ * This is the main source file which interfaces directly with Apache by
73
+ * installing hooks. The code here can look a bit convoluted, but it'll make
74
+ * more sense if you read:
75
+ * http://httpd.apache.org/docs/2.2/developer/request.html
76
+ *
77
+ * Scroll all the way down to passenger_register_hooks to get an idea of
78
+ * what we're hooking into and what we do in those hooks.
79
+ */
80
+
72
81
 
73
82
  /**
74
83
  * If the HTTP client sends POST data larger than this value (in bytes),
75
- * then the POST data will be fully saved into a temporary file, before
84
+ * then the POST data will be fully buffered into a temporary file, before
76
85
  * allocating a Ruby web application session.
86
+ * File uploads smaller than this are buffered into memory instead.
77
87
  */
78
- #define UPLOAD_ACCELERATION_THRESHOLD 1024 * 8
88
+ #define LARGE_UPLOAD_THRESHOLD 1024 * 8
79
89
 
80
90
 
81
91
  /**
@@ -120,13 +130,14 @@ private:
120
130
  ReportFileSystemError(const FileSystemException &ex): e(ex) { }
121
131
 
122
132
  int report(request_rec *r) {
133
+ r->status = 500;
123
134
  ap_set_content_type(r, "text/html; charset=UTF-8");
124
135
  ap_rputs("<h1>Passenger error #2</h1>\n", r);
125
136
  ap_rputs("An error occurred while trying to access '", r);
126
137
  ap_rputs(ap_escape_html(r->pool, e.filename().c_str()), r);
127
138
  ap_rputs("': ", r);
128
139
  ap_rputs(ap_escape_html(r->pool, e.what()), r);
129
- if (e.code() == EPERM) {
140
+ if (e.code() == EACCES || e.code() == EPERM) {
130
141
  ap_rputs("<p>", r);
131
142
  ap_rputs("Apache doesn't have read permissions to that file. ", r);
132
143
  ap_rputs("Please fix the relevant file permissions.", r);
@@ -310,12 +321,27 @@ private:
310
321
  * Later, in the handler hook, we inform the user about this
311
322
  * problem so that he can either disable Phusion Passenger's
312
323
  * autodetection routines, or fix the permissions.
324
+ *
325
+ * If it's not a permission problem then we'll disable
326
+ * Phusion Passenger for the rest of the request.
313
327
  */
314
- apr_pool_userdata_set(new ReportFileSystemError(e),
315
- "Phusion Passenger: error report",
316
- ReportFileSystemError::cleanup,
317
- r->pool);
318
- return true;
328
+ if (e.code() == EACCES || e.code() == EPERM) {
329
+ // TODO: filesystem error is not always reported. need
330
+ // to figure out why. test case:
331
+ // - mkdir /foo
332
+ // - mkdir /foo/public
333
+ // - mkdir /foo/config
334
+ // - chmod 000 /foo/config
335
+ // - add vhost 'foo' with document root /foo/public
336
+ // - curl http://foo/
337
+ apr_pool_userdata_set(new ReportFileSystemError(e),
338
+ "Phusion Passenger: error report",
339
+ ReportFileSystemError::cleanup,
340
+ r->pool);
341
+ return true;
342
+ } else {
343
+ return false;
344
+ }
319
345
  }
320
346
 
321
347
  /* Save some information for the hook methods that are called later.
@@ -449,7 +475,8 @@ private:
449
475
  this_thread::disable_syscall_interruption dsi;
450
476
  Application::SessionPtr session;
451
477
  bool expectingUploadData;
452
- shared_ptr<BufferedUpload> uploadData;
478
+ string uploadDataMemory;
479
+ shared_ptr<BufferedUpload> uploadDataFile;
453
480
  const char *contentLength;
454
481
 
455
482
  expectingUploadData = ap_should_client_block(r);
@@ -458,14 +485,18 @@ private:
458
485
  /* If the HTTP upload data is larger than a threshold, or if the HTTP
459
486
  * client sent HTTP upload data using the "chunked" transfer encoding
460
487
  * (which implies Content-Length == NULL), then buffer the upload
461
- * data into a tempfile.
488
+ * data into a tempfile. Otherwise, buffer it into memory.
489
+ *
490
+ * We never forward the data directly to the backend process because
491
+ * the HTTP client might block indefinitely until it's done uploading.
492
+ * This would quickly exhaust the application pool.
462
493
  */
463
- if (expectingUploadData && (
464
- contentLength == NULL ||
465
- atol(contentLength) > UPLOAD_ACCELERATION_THRESHOLD
466
- )
467
- ) {
468
- uploadData = receiveRequestBody(r, contentLength);
494
+ if (expectingUploadData) {
495
+ if (contentLength == NULL || atol(contentLength) > LARGE_UPLOAD_THRESHOLD) {
496
+ uploadDataFile = receiveRequestBody(r, contentLength);
497
+ } else {
498
+ receiveRequestBody(r, contentLength, uploadDataMemory);
499
+ }
469
500
  }
470
501
 
471
502
  if (expectingUploadData && contentLength == NULL) {
@@ -474,8 +505,13 @@ private:
474
505
  * data. Rails requires this header for its HTTP upload data
475
506
  * multipart parsing process.
476
507
  */
477
- apr_table_set(r->headers_in, "Content-Length",
478
- toString(ftell(uploadData->handle)).c_str());
508
+ if (uploadDataFile != NULL) {
509
+ apr_table_set(r->headers_in, "Content-Length",
510
+ toString(ftell(uploadDataFile->handle)).c_str());
511
+ } else {
512
+ apr_table_set(r->headers_in, "Content-Length",
513
+ toString(uploadDataMemory.size()).c_str());
514
+ }
479
515
  }
480
516
 
481
517
 
@@ -527,15 +563,13 @@ private:
527
563
  }
528
564
 
529
565
  UPDATE_TRACE_POINT();
530
- session->setReaderTimeout(r->server->timeout / 1000);
531
- session->setWriterTimeout(r->server->timeout / 1000);
532
- sendHeaders(r, session, mapper.getBaseURI());
566
+ sendHeaders(r, config, session, mapper.getBaseURI());
533
567
  if (expectingUploadData) {
534
- if (uploadData != NULL) {
535
- sendRequestBody(r, session, uploadData);
536
- uploadData.reset();
568
+ if (uploadDataFile != NULL) {
569
+ sendRequestBody(r, session, uploadDataFile);
570
+ uploadDataFile.reset();
537
571
  } else {
538
- sendRequestBody(r, session);
572
+ sendRequestBody(r, session, uploadDataMemory);
539
573
  }
540
574
  }
541
575
  try {
@@ -610,11 +644,14 @@ private:
610
644
  ap_pass_brigade(r->output_filters, bb);
611
645
 
612
646
  if (r->connection->aborted) {
613
- P_WARN("The HTTP client closed the connection before "
614
- "the response could be completely sent. As a "
615
- "result, you will probably see a 'Broken Pipe' "
647
+ P_WARN("Either the vistor clicked on the 'Stop' button in the "
648
+ "web browser, or the visitor's connection has stalled "
649
+ "and couldn't receive the data that Apache is sending "
650
+ "to it. As a result, you will probably see a 'Broken Pipe' "
616
651
  "error in this log file. Please ignore it, "
617
- "this is normal.");
652
+ "this is normal. You might also want to increase Apache's "
653
+ "TimeOut configuration option if you experience this "
654
+ "problem often.");
618
655
  } else if (!bucketState->completed) {
619
656
  P_WARN("Apache stopped forwarding the backend's response, "
620
657
  "even though the HTTP client did not close the "
@@ -747,13 +784,14 @@ private:
747
784
  }
748
785
  }
749
786
 
750
- apr_status_t sendHeaders(request_rec *r, Application::SessionPtr &session, const char *baseURI) {
787
+ apr_status_t sendHeaders(request_rec *r, DirConfig *config, Application::SessionPtr &session, const char *baseURI) {
751
788
  apr_table_t *headers;
752
789
  headers = apr_table_make(r->pool, 40);
753
790
  if (headers == NULL) {
754
791
  return APR_ENOMEM;
755
792
  }
756
793
 
794
+
757
795
  // Set standard CGI variables.
758
796
  addHeader(headers, "SERVER_SOFTWARE", ap_get_server_version());
759
797
  addHeader(headers, "SERVER_PROTOCOL", r->protocol);
@@ -765,17 +803,39 @@ private:
765
803
  addHeader(headers, "REMOTE_PORT", apr_psprintf(r->pool, "%d", r->connection->remote_addr->port));
766
804
  addHeader(headers, "REMOTE_USER", r->user);
767
805
  addHeader(headers, "REQUEST_METHOD", r->method);
768
- addHeader(headers, "REQUEST_URI", r->unparsed_uri);
769
806
  addHeader(headers, "QUERY_STRING", r->args ? r->args : "");
807
+ addHeader(headers, "HTTPS", lookupEnv(r, "HTTPS"));
808
+ addHeader(headers, "CONTENT_TYPE", lookupHeader(r, "Content-type"));
809
+ addHeader(headers, "DOCUMENT_ROOT", ap_document_root(r));
810
+
811
+ if (config->allowsEncodedSlashes()) {
812
+ /*
813
+ * Apache decodes encoded slashes in r->uri, so we must use r->unparsed_uri
814
+ * if we are to support encoded slashes. However mod_rewrite doesn't change
815
+ * r->unparsed_uri, so the user must make a choice between mod_rewrite
816
+ * support or encoded slashes support. Sucks. :-(
817
+ *
818
+ * http://code.google.com/p/phusion-passenger/issues/detail?id=113
819
+ * http://code.google.com/p/phusion-passenger/issues/detail?id=230
820
+ */
821
+ addHeader(headers, "REQUEST_URI", r->unparsed_uri);
822
+ } else {
823
+ const char *request_uri;
824
+ if (r->args != NULL) {
825
+ request_uri = apr_pstrcat(r->pool, r->uri, "?", r->args, NULL);
826
+ } else {
827
+ request_uri = r->uri;
828
+ }
829
+ addHeader(headers, "REQUEST_URI", request_uri);
830
+ }
831
+
770
832
  if (strcmp(baseURI, "/") == 0) {
771
833
  addHeader(headers, "SCRIPT_NAME", "");
834
+ addHeader(headers, "PATH_INFO", r->uri);
772
835
  } else {
773
836
  addHeader(headers, "SCRIPT_NAME", baseURI);
837
+ addHeader(headers, "PATH_INFO", r->uri + strlen(baseURI));
774
838
  }
775
- addHeader(headers, "HTTPS", lookupEnv(r, "HTTPS"));
776
- addHeader(headers, "CONTENT_TYPE", lookupHeader(r, "Content-type"));
777
- addHeader(headers, "DOCUMENT_ROOT", ap_document_root(r));
778
- addHeader(headers, "PATH_INFO", r->parsed_uri.path);
779
839
 
780
840
  // Set HTTP headers.
781
841
  const apr_array_header_t *hdrs_arr;
@@ -790,6 +850,16 @@ private:
790
850
  }
791
851
  }
792
852
 
853
+ // Add other environment variables.
854
+ const apr_array_header_t *env_arr;
855
+ apr_table_entry_t *env;
856
+
857
+ env_arr = apr_table_elts(r->subprocess_env);
858
+ env = (apr_table_entry_t*) env_arr->elts;
859
+ for (i = 0; i < env_arr->nelts; ++i) {
860
+ addHeader(headers, env[i].key, env[i].val);
861
+ }
862
+
793
863
  // Now send the headers.
794
864
  string buffer;
795
865
 
@@ -1035,11 +1105,58 @@ private:
1035
1105
  }
1036
1106
 
1037
1107
  if (contentLength != NULL && ftell(tempFile->handle) != atol(contentLength)) {
1038
- throw IOException("The HTTP client sent incomplete upload data.");
1108
+ string message = "It looks like the browser did not finish the file upload: "
1109
+ "it said it will upload ";
1110
+ message.append(contentLength);
1111
+ message.append(" bytes, but it closed the connection after sending ");
1112
+ message.append(toString(ftell(tempFile->handle)));
1113
+ message.append(" bytes. The user probably clicked Stop in the browser "
1114
+ "or his Internet connection stalled.");
1115
+ throw IOException(message);
1039
1116
  }
1040
1117
  return tempFile;
1041
1118
  }
1042
1119
 
1120
+ /**
1121
+ * Receive the HTTP upload data and buffer it into a string.
1122
+ *
1123
+ * @param r The request.
1124
+ * @param contentLength The value of the HTTP Content-Length header. This is used
1125
+ * to check whether the HTTP client has sent complete upload
1126
+ * data. NULL indicates that there is no Content-Length header,
1127
+ * i.e. that the HTTP client used chunked transfer encoding.
1128
+ * @param string The string to buffer into.
1129
+ * @throws RuntimeException
1130
+ * @throws IOException
1131
+ */
1132
+ void receiveRequestBody(request_rec *r, const char *contentLength, string &buffer) {
1133
+ TRACE_POINT();
1134
+ unsigned long l_contentLength = 0;
1135
+ char buf[1024 * 32];
1136
+ apr_off_t len;
1137
+
1138
+ buffer.clear();
1139
+ if (contentLength != NULL) {
1140
+ l_contentLength = atol(contentLength);
1141
+ buffer.reserve(l_contentLength);
1142
+ }
1143
+
1144
+ while ((len = readRequestBodyFromApache(r, buf, sizeof(buf))) > 0) {
1145
+ buffer.append(buf, len);
1146
+ }
1147
+
1148
+ if (contentLength != NULL && buffer.size() != l_contentLength) {
1149
+ string message = "It looks like the browser did not finish the file upload: "
1150
+ "it said it will upload ";
1151
+ message.append(contentLength);
1152
+ message.append(" bytes, but it closed the connection after sending ");
1153
+ message.append(toString(buffer.size()));
1154
+ message.append(" bytes. The user probably clicked Stop in the browser "
1155
+ "or his Internet connection stalled.");
1156
+ throw IOException(message);
1157
+ }
1158
+ }
1159
+
1043
1160
  void sendRequestBody(request_rec *r, Application::SessionPtr &session, shared_ptr<BufferedUpload> &uploadData) {
1044
1161
  TRACE_POINT();
1045
1162
  rewind(uploadData->handle);
@@ -1053,13 +1170,8 @@ private:
1053
1170
  }
1054
1171
  }
1055
1172
 
1056
- void sendRequestBody(request_rec *r, Application::SessionPtr &session) {
1057
- char buf[1024 * 32];
1058
- apr_off_t len;
1059
-
1060
- while ((len = readRequestBodyFromApache(r, buf, sizeof(buf))) > 0) {
1061
- session->sendBodyBlock(buf, len);
1062
- }
1173
+ void sendRequestBody(request_rec *r, Application::SessionPtr &session, const string &buffer) {
1174
+ session->sendBodyBlock(buffer.c_str(), buffer.size());
1063
1175
  }
1064
1176
 
1065
1177
  public:
@@ -1075,22 +1187,13 @@ public:
1075
1187
  P_DEBUG("Initializing Phusion Passenger...");
1076
1188
  ap_add_version_component(pconf, "Phusion_Passenger/" PASSENGER_VERSION);
1077
1189
 
1078
- const char *ruby, *user;
1190
+ const char *user;
1079
1191
  string applicationPoolServerExe, spawnServer;
1080
1192
 
1081
- /*
1082
- * As described in the comment in init_module, upon (re)starting
1083
- * Apache, the Hooks constructor is called twice. We unset
1084
- * PASSENGER_INSTANCE_TEMP_DIR before calling createPassengerTempDir()
1085
- * because we want the temp directory's name to contain the PID
1086
- * of the process in which the Hooks constructor was called for
1087
- * the second time.
1088
- */
1089
1193
  createPassengerTempDir(config->getTempDir(), config->userSwitching,
1090
1194
  config->getDefaultUser(), unixd_config.user_id,
1091
1195
  unixd_config.group_id);
1092
1196
 
1093
- ruby = (config->ruby != NULL) ? config->ruby : DEFAULT_RUBY_COMMAND;
1094
1197
  if (config->userSwitching) {
1095
1198
  user = "";
1096
1199
  } else {
@@ -1124,7 +1227,7 @@ public:
1124
1227
  applicationPoolServer = ptr(
1125
1228
  new ApplicationPoolServer(
1126
1229
  applicationPoolServerExe, spawnServer, "",
1127
- ruby, user)
1230
+ config->getRuby(), user)
1128
1231
  );
1129
1232
 
1130
1233
  ApplicationPoolPtr pool(applicationPoolServer->connect());
@@ -1162,7 +1265,7 @@ public:
1162
1265
  * A is top-most directory that exists. B is the first filename piece that
1163
1266
  * normally follows A. For example, suppose that a website's DocumentRoot
1164
1267
  * is /website, on server http://test.com/. Suppose that there's also a
1165
- * directory /website/images.
1268
+ * directory /website/images. No other files or directories exist in /website.
1166
1269
  *
1167
1270
  * If we access: then r->filename will be:
1168
1271
  * http://test.com/foo/bar /website/foo
@@ -1187,14 +1290,19 @@ public:
1187
1290
  */
1188
1291
  return OK;
1189
1292
  } else {
1190
- // core.c's map_to_storage hook will transform the filename, as
1191
- // described by saveOriginalFilename(). Here we restore the
1192
- // original filename.
1293
+ /* core.c's map_to_storage hook will transform the filename, as
1294
+ * described by saveOriginalFilename(). Here we restore the
1295
+ * original filename.
1296
+ */
1193
1297
  const char *filename = apr_table_get(r->notes, "Phusion Passenger: original filename");
1194
1298
  if (filename == NULL) {
1195
1299
  return DECLINED;
1196
1300
  } else {
1197
1301
  prepareRequest(r, config, filename);
1302
+ /* Always return declined in order to let other modules'
1303
+ * hooks run, regardless of what prepareRequest()'s
1304
+ * result is.
1305
+ */
1198
1306
  return DECLINED;
1199
1307
  }
1200
1308
  }