passenger 4.0.44 → 4.0.45

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 (110) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/.travis.yml +3 -0
  5. data/CHANGELOG +31 -0
  6. data/CONTRIBUTING.md +70 -10
  7. data/CONTRIBUTORS +4 -0
  8. data/README.md +1 -1
  9. data/Vagrantfile +50 -0
  10. data/bin/passenger-install-nginx-module +7 -2
  11. data/build/basics.rb +4 -1
  12. data/build/documentation.rb +6 -0
  13. data/build/node_tests.rb +7 -1
  14. data/build/packaging.rb +5 -0
  15. data/build/test_basics.rb +3 -3
  16. data/debian.template/copyright +1 -1
  17. data/debian.template/passenger.manpages +0 -1
  18. data/dev/rack.test/config.ru +5 -0
  19. data/dev/rack.test/public/asset.txt +1 -0
  20. data/dev/vagrant/apache_default_site.conf +35 -0
  21. data/dev/vagrant/apache_passenger.conf +5 -0
  22. data/dev/vagrant/apache_passenger.load +1 -0
  23. data/dev/vagrant/apache_ports.conf +24 -0
  24. data/dev/vagrant/apache_rack_test.conf +9 -0
  25. data/dev/vagrant/bashrc +21 -0
  26. data/dev/vagrant/nginx.conf +39 -0
  27. data/dev/vagrant/nginx_rakefile +34 -0
  28. data/dev/vagrant/nginx_start +32 -0
  29. data/dev/vagrant/provision.sh +115 -0
  30. data/dev/vagrant/sudoers.conf +5 -0
  31. data/doc/Design and Architecture.txt +515 -0
  32. data/doc/DeveloperQuickstart.md +70 -0
  33. data/doc/Users guide Apache.idmap.txt +24 -18
  34. data/doc/Users guide Apache.txt +200 -62
  35. data/doc/Users guide Nginx.idmap.txt +53 -45
  36. data/doc/Users guide Nginx.txt +501 -360
  37. data/doc/Users guide Standalone.txt +8 -0
  38. data/doc/images/direct_spawning.png +0 -0
  39. data/doc/images/direct_spawning.svg +16 -13
  40. data/doc/images/helper_agent_core_architecture.png +0 -0
  41. data/doc/images/passenger_architecture_overview.png +0 -0
  42. data/doc/images/smart_spawning.png +0 -0
  43. data/doc/images/{smart.svg → smart_spawning.svg} +23 -20
  44. data/doc/images/spawning_preparation_work.png +0 -0
  45. data/doc/images/startup_sequence.png +0 -0
  46. data/doc/users_guide_snippets/appendix_c_spawning_methods.txt +82 -121
  47. data/doc/users_guide_snippets/environment_variables.txt +1 -1
  48. data/doc/users_guide_snippets/support_information.txt +2 -0
  49. data/doc/users_guide_snippets/tips.txt +117 -9
  50. data/ext/apache2/Configuration.hpp +4 -2
  51. data/ext/apache2/ConfigurationCommands.cpp +14 -0
  52. data/ext/apache2/ConfigurationFields.hpp +4 -0
  53. data/ext/apache2/ConfigurationSetters.cpp +22 -0
  54. data/ext/apache2/CreateDirConfig.cpp +2 -0
  55. data/ext/apache2/Hooks.cpp +30 -14
  56. data/ext/apache2/MergeDirConfig.cpp +14 -0
  57. data/ext/apache2/SetHeaders.cpp +8 -0
  58. data/ext/common/ApplicationPool2/AppTypes.cpp +6 -1
  59. data/ext/common/ApplicationPool2/Implementation.cpp +1 -1
  60. data/ext/common/ApplicationPool2/Session.h +1 -1
  61. data/ext/common/Constants.h +9 -7
  62. data/ext/common/Utils/HttpHeaderBufferer.h +23 -4
  63. data/ext/common/Utils/StrIntUtils.h +35 -0
  64. data/ext/common/Utils/StringScanning.h +4 -10
  65. data/ext/common/agents/HelperAgent/RequestHandler.h +90 -49
  66. data/ext/nginx/CacheLocationConfig.c +40 -0
  67. data/ext/nginx/ConfigurationCommands.c +20 -0
  68. data/ext/nginx/ConfigurationFields.h +4 -0
  69. data/ext/nginx/ContentHandler.c +1 -1
  70. data/ext/nginx/CreateLocationConfig.c +9 -0
  71. data/ext/nginx/MergeLocationConfig.c +12 -0
  72. data/ext/nginx/config +2 -2
  73. data/ext/nginx/ngx_http_passenger_module.c +4 -4
  74. data/helper-scripts/node-loader.js +40 -27
  75. data/lib/phusion_passenger.rb +1 -1
  76. data/lib/phusion_passenger/apache2/config_options.rb +14 -2
  77. data/lib/phusion_passenger/constants.rb +7 -6
  78. data/lib/phusion_passenger/loader_shared_helpers.rb +11 -1
  79. data/lib/phusion_passenger/nginx/config_options.rb +8 -0
  80. data/lib/phusion_passenger/packaging.rb +8 -3
  81. data/lib/phusion_passenger/platform_info/apache.rb +3 -0
  82. data/lib/phusion_passenger/platform_info/ruby.rb +4 -1
  83. data/lib/phusion_passenger/standalone/command.rb +0 -1
  84. data/lib/phusion_passenger/standalone/package_runtime_command.rb +1 -0
  85. data/lib/phusion_passenger/standalone/start_command.rb +80 -62
  86. data/lib/phusion_passenger/standalone/status_command.rb +1 -0
  87. data/lib/phusion_passenger/standalone/stop_command.rb +1 -0
  88. data/man/passenger-config.1 +1 -1
  89. data/man/passenger-memory-stats.8 +1 -1
  90. data/man/passenger-status.8 +1 -1
  91. data/npm-shrinkwrap.json +229 -0
  92. data/package.json +28 -0
  93. data/resources/templates/standalone/config.erb +2 -0
  94. data/rpm/Vagrantfile +0 -3
  95. data/test/config.json.vagrant +30 -0
  96. data/test/cxx/HttpHeaderBuffererTest.cpp +64 -10
  97. data/test/cxx/RequestHandlerTest.cpp +35 -13
  98. data/test/integration_tests/apache2_tests.rb +1 -0
  99. data/test/stub/node/app.js +26 -18
  100. metadata +28 -13
  101. metadata.gz.asc +7 -7
  102. data/doc/Architectural overview.idmap.txt +0 -36
  103. data/doc/Architectural overview.txt +0 -410
  104. data/doc/images/smart.png +0 -0
  105. data/ext/common/ApplicationPool2/README.md +0 -56
  106. data/man/passenger-stress-test.1 +0 -43
  107. data/node_lib/phusion_passenger/httplib_emulation.js +0 -215
  108. data/node_lib/phusion_passenger/request_handler.js +0 -73
  109. data/node_lib/phusion_passenger/session_protocol_parser.js +0 -113
  110. data/test/node/httplib_emulation_spec.js +0 -623
@@ -701,53 +701,75 @@ namespace tut {
701
701
  }
702
702
 
703
703
  TEST_METHOD(33) {
704
- set_test_name("It rejects GET/HEAD requests with a Content-Length header.");
704
+ set_test_name("It accepts GET/HEAD requests with a Content-Length header.");
705
+
706
+ DeleteFileEventually d("/tmp/output.txt");
705
707
 
706
708
  init();
707
709
  connect();
708
710
  sendHeaders(defaultHeaders,
709
711
  "PASSENGER_APP_ROOT", wsgiAppPath.c_str(),
710
- "PATH_INFO", "/",
712
+ "PATH_INFO", "/raw_upload_to_file",
711
713
  "REQUEST_METHOD", "GET",
712
714
  "CONTENT_LENGTH", "2",
715
+ "HTTP_X_OUTPUT", "/tmp/output.txt",
713
716
  NULL);
714
- string response = readAll(connection);
715
- ensure(containsSubstring(response, "HTTP/1.1 400 Bad Request"));
717
+ writeExact(connection, "hi");
718
+
719
+ string result = stripHeaders(readAll(connection));
720
+ ensure_equals(result, "ok");
721
+ ensure_equals(readAll("/tmp/output.txt"), "hi");
716
722
 
717
723
  connect();
718
724
  sendHeaders(defaultHeaders,
719
725
  "PASSENGER_APP_ROOT", wsgiAppPath.c_str(),
720
- "PATH_INFO", "/",
726
+ "PATH_INFO", "/raw_upload_to_file",
721
727
  "REQUEST_METHOD", "HEAD",
722
728
  "CONTENT_LENGTH", "2",
729
+ "HTTP_X_OUTPUT", "/tmp/output.txt",
723
730
  NULL);
724
- response = readAll(connection);
725
- ensure(containsSubstring(response, "HTTP/1.1 400 Bad Request"));
731
+ writeExact(connection, "ho");
732
+
733
+ result = stripHeaders(readAll(connection));
734
+ ensure_equals(result, "ok");
735
+ ensure_equals(readAll("/tmp/output.txt"), "ho");
726
736
  }
727
737
 
728
738
  TEST_METHOD(34) {
729
739
  set_test_name("It rejects GET/HEAD requests with a Transfer-Encoding header.");
730
740
 
741
+ DeleteFileEventually d("/tmp/output.txt");
742
+
731
743
  init();
732
744
  connect();
733
745
  sendHeaders(defaultHeaders,
734
746
  "PASSENGER_APP_ROOT", wsgiAppPath.c_str(),
735
- "PATH_INFO", "/",
747
+ "PATH_INFO", "/raw_upload_to_file",
736
748
  "REQUEST_METHOD", "GET",
737
749
  "HTTP_TRANSFER_ENCODING", "chunked",
750
+ "HTTP_X_OUTPUT", "/tmp/output.txt",
738
751
  NULL);
739
- string response = readAll(connection);
740
- ensure(containsSubstring(response, "HTTP/1.1 400 Bad Request"));
752
+ writeExact(connection, "hi");
753
+ shutdown(connection, SHUT_WR);
754
+
755
+ string result = stripHeaders(readAll(connection));
756
+ ensure_equals(result, "ok");
757
+ ensure_equals(readAll("/tmp/output.txt"), "hi");
741
758
 
742
759
  connect();
743
760
  sendHeaders(defaultHeaders,
744
761
  "PASSENGER_APP_ROOT", wsgiAppPath.c_str(),
745
- "PATH_INFO", "/",
762
+ "PATH_INFO", "/raw_upload_to_file",
746
763
  "REQUEST_METHOD", "HEAD",
747
764
  "HTTP_TRANSFER_ENCODING", "chunked",
765
+ "HTTP_X_OUTPUT", "/tmp/output.txt",
748
766
  NULL);
749
- response = readAll(connection);
750
- ensure(containsSubstring(response, "HTTP/1.1 400 Bad Request"));
767
+ writeExact(connection, "ho");
768
+ shutdown(connection, SHUT_WR);
769
+
770
+ result = stripHeaders(readAll(connection));
771
+ ensure_equals(result, "ok");
772
+ ensure_equals(readAll("/tmp/output.txt"), "ho");
751
773
  }
752
774
 
753
775
 
@@ -16,6 +16,7 @@ describe "Apache 2 module" do
16
16
  check_hosts_configuration
17
17
  @passenger_temp_dir = "/tmp/passenger-test.#{$$}"
18
18
  Dir.mkdir(@passenger_temp_dir)
19
+ FileUtils.chmod_R(0777, @passenger_temp_dir)
19
20
  ENV['PASSENGER_TEMP_DIR'] = @passenger_temp_dir
20
21
  end
21
22
 
@@ -56,20 +56,28 @@ app.all('/pid', function(req, res) {
56
56
  });
57
57
 
58
58
  app.all(/^\/env/, function(req, res) {
59
- var body = '';
60
- var keys = [];
61
- for (var key in req.cgiHeaders) {
62
- keys.push(key);
59
+ var result = [];
60
+ var requestUri = req.url;
61
+ var urlParts = url.parse(req.url);
62
+
63
+ if (process.env.PASSENGER_BASE_URI) {
64
+ requestUri = process.env.PASSENGER_BASE_URI + requestUri;
65
+ result.push('SCRIPT_NAME = ' + process.env.PASSENGER_BASE_URI);
66
+ } else {
67
+ result.push('SCRIPT_NAME = ');
63
68
  }
64
- keys.sort();
65
- for (var i = 0; i < keys.length; i++) {
66
- var val = req.cgiHeaders[keys[i]];
67
- if (val === undefined) {
68
- val = '';
69
- }
70
- body += keys[i] + " = " + val + "\n";
69
+
70
+ result.push('REQUEST_URI = ' + requestUri);
71
+ result.push('PATH_INFO = ' + req.path);
72
+ result.push('QUERY_STRING = ' + (urlParts.query || ''));
73
+
74
+ for (var key in req.headers) {
75
+ result.push('HTTP_' + key.toUpperCase().replace(/-/g, '_')
76
+ + ' = ' + req.headers[key]);
71
77
  }
72
- textResponse(res, body);
78
+
79
+ result.sort();
80
+ textResponse(res, result.join("\n") + "\n");
73
81
  });
74
82
 
75
83
  app.all('/touch_file', function(req, res) {
@@ -95,15 +103,15 @@ app.all('/upload_with_params', function(req, res) {
95
103
  bodyParser(req, res, function() {
96
104
  var name1 = req.query.name1 || req.body.name1;
97
105
  var name2 = req.query.name2 || req.body.name2;
98
- var data = fs.readFileSync(req.files.data.path);
99
- var body =
106
+ var data = req.body.data ? new Buffer(req.body.data) : fs.readFileSync(req.files.data.path);
107
+ var preamble = new Buffer(
100
108
  "name 1 = " + name1 + "\n" +
101
109
  "name 2 = " + name2 + "\n" +
102
- "data = ";
103
- var bodyBuffer = new Buffer(body);
110
+ "data = ");
111
+
104
112
  res.setHeader("Content-Type", "text/plain");
105
- res.setHeader("Content-Length", bodyBuffer.length + data.length);
106
- res.write(bodyBuffer);
113
+ res.setHeader("Transfer-Encoding", "chunked");
114
+ res.write(preamble);
107
115
  res.write(data);
108
116
  res.end();
109
117
  });
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passenger
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.44
4
+ version: 4.0.45
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phusion - http://www.phusion.nl/
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-29 00:00:00.000000000 Z
11
+ date: 2014-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -75,6 +75,7 @@ files:
75
75
  - LICENSE
76
76
  - README.md
77
77
  - Rakefile
78
+ - Vagrantfile
78
79
  - bin/passenger
79
80
  - bin/passenger-config
80
81
  - bin/passenger-install-apache2-module
@@ -125,15 +126,28 @@ files:
125
126
  - dev/find_owner_pipe_leaks.rb
126
127
  - dev/install_scripts_bootstrap_code.rb
127
128
  - dev/list_tests.rb
129
+ - dev/rack.test/config.ru
130
+ - dev/rack.test/public/asset.txt
128
131
  - dev/render_error_pages.rb
129
132
  - dev/run_travis.sh
130
133
  - dev/runner
131
134
  - dev/test_rpm_packaging.sh
132
- - doc/Architectural overview.html
133
- - doc/Architectural overview.idmap.txt
134
- - doc/Architectural overview.txt
135
+ - dev/vagrant/apache_default_site.conf
136
+ - dev/vagrant/apache_passenger.conf
137
+ - dev/vagrant/apache_passenger.load
138
+ - dev/vagrant/apache_ports.conf
139
+ - dev/vagrant/apache_rack_test.conf
140
+ - dev/vagrant/bashrc
141
+ - dev/vagrant/nginx.conf
142
+ - dev/vagrant/nginx_rakefile
143
+ - dev/vagrant/nginx_start
144
+ - dev/vagrant/provision.sh
145
+ - dev/vagrant/sudoers.conf
135
146
  - doc/CodingTipsAndPitfalls.md
136
147
  - doc/DebuggingAndStressTesting.md
148
+ - doc/Design and Architecture.html
149
+ - doc/Design and Architecture.txt
150
+ - doc/DeveloperQuickstart.md
137
151
  - doc/Packaging.html
138
152
  - doc/Packaging.txt.md
139
153
  - doc/Security of user switching support.html
@@ -155,6 +169,7 @@ files:
155
169
  - doc/images/direct_spawning.svg
156
170
  - doc/images/glyphicons-halflings-white.png
157
171
  - doc/images/glyphicons-halflings.png
172
+ - doc/images/helper_agent_core_architecture.png
158
173
  - doc/images/icons/README
159
174
  - doc/images/icons/callouts/1.png
160
175
  - doc/images/icons/callouts/10.png
@@ -184,14 +199,17 @@ files:
184
199
  - doc/images/many_web_framework_protocols.png
185
200
  - doc/images/passenger_architecture.png
186
201
  - doc/images/passenger_architecture.svg
202
+ - doc/images/passenger_architecture_overview.png
187
203
  - doc/images/passenger_nodejs_architecture.svg
188
204
  - doc/images/phusion_banner.png
189
205
  - doc/images/phusion_banner_small.png
190
206
  - doc/images/rack.png
191
- - doc/images/smart.png
192
- - doc/images/smart.svg
207
+ - doc/images/smart_spawning.png
208
+ - doc/images/smart_spawning.svg
193
209
  - doc/images/spawn_server_architecture.png
194
210
  - doc/images/spawn_server_architecture.svg
211
+ - doc/images/spawning_preparation_work.png
212
+ - doc/images/startup_sequence.png
195
213
  - doc/images/typical_isolated_web_application.png
196
214
  - doc/images/typical_isolated_web_application.svg
197
215
  - doc/templates/bootstrap.min.css
@@ -2137,7 +2155,6 @@ files:
2137
2155
  - ext/common/ApplicationPool2/PipeWatcher.h
2138
2156
  - ext/common/ApplicationPool2/Pool.h
2139
2157
  - ext/common/ApplicationPool2/Process.h
2140
- - ext/common/ApplicationPool2/README.md
2141
2158
  - ext/common/ApplicationPool2/Session.h
2142
2159
  - ext/common/ApplicationPool2/SmartSpawner.h
2143
2160
  - ext/common/ApplicationPool2/Socket.h
@@ -2442,11 +2459,9 @@ files:
2442
2459
  - man/passenger-config.1
2443
2460
  - man/passenger-memory-stats.8
2444
2461
  - man/passenger-status.8
2445
- - man/passenger-stress-test.1
2446
- - node_lib/phusion_passenger/httplib_emulation.js
2447
2462
  - node_lib/phusion_passenger/line_reader.js
2448
- - node_lib/phusion_passenger/request_handler.js
2449
- - node_lib/phusion_passenger/session_protocol_parser.js
2463
+ - npm-shrinkwrap.json
2464
+ - package.json
2450
2465
  - passenger.gemspec
2451
2466
  - resources/mime.types
2452
2467
  - resources/oss-binaries.phusionpassenger.com.crt
@@ -2507,6 +2522,7 @@ files:
2507
2522
  - test/config.json.example
2508
2523
  - test/config.json.rpm-automation
2509
2524
  - test/config.json.travis
2525
+ - test/config.json.vagrant
2510
2526
  - test/cxx/ApplicationPool2/DirectSpawnerTest.cpp
2511
2527
  - test/cxx/ApplicationPool2/OptionsTest.cpp
2512
2528
  - test/cxx/ApplicationPool2/PoolTest.cpp
@@ -2552,7 +2568,6 @@ files:
2552
2568
  - test/integration_tests/source_packaging_test.rb
2553
2569
  - test/integration_tests/spec_helper.rb
2554
2570
  - test/integration_tests/standalone_tests.rb
2555
- - test/node/httplib_emulation_spec.js
2556
2571
  - test/node/line_reader_spec.js
2557
2572
  - test/node/spec_helper.js
2558
2573
  - test/oxt/backtrace_test.cpp
metadata.gz.asc CHANGED
@@ -2,11 +2,11 @@
2
2
  Version: GnuPG/MacGPG2 v2.0.17 (Darwin)
3
3
  Comment: GPGTools - http://gpgtools.org
4
4
 
5
- iQEcBAABAgAGBQJThyRmAAoJECrHRaUKISqMSB8IAIR8v8UA2iRIVjotf11sZu5K
6
- eJXVNxKmgRklkBoDQlglyYdjgEcH3WTxQIKzxFG2bHYYNmjkG5XUW4Tvzk29W0ox
7
- u1AkddCkO9b9q1dD982e7uHzgWIh7Qt1YRohLiOoJAWJARPINkRA0BgyxRVyxLsV
8
- EmDdGIpZyVhPZXMwwFDFQYTZmqsf2TrfzCvPa7zYZIqaH8/8vPmIdBP+/Ek+GQkL
9
- UKGvZsjvIEgCh3cEueVlQ3vggJyt9ADMuvZMuKpuoFU2sJ+cy8K60/pPC6RnogPd
10
- 78ddmUT1UlzOAp0IjpseVlwC/pLG6Dbz+kKJbHPoMg+pL/qx1/GIYcCwYF8sCVg=
11
- =wwAY
5
+ iQEcBAABAgAGBQJTmYzGAAoJECrHRaUKISqMNuEIAJ0KBHgumSlkufhMIDJH0ive
6
+ jSKD7jiWPsi8SyBOl5ohnr9QJMmO3L77WUt8AM7gLy9D0XHBd9FlzktzngEyAEJn
7
+ NnXhDZyTKQLai2qmNFnYa/N8EMfVQ0Hz4e7aqima3DLrUkKVoGhXm//duU3Tjn5b
8
+ rjPp1GKAfUIS7zHSOygQ0nZE+n1RlHpV3D0vKkf+3HYOxBFOGd7lwLb3HNZ7MO4M
9
+ GuW3vmkYWDJnJbw1B5xI3MiHuwdRVY5ekTKBlTuoHkjn44rQyQfN3iDvBg73Pnb3
10
+ 3oe+8qru2B7GvvRWveM8/16ey73Z3K49mMhVD1i+tBYnGW8wAN0b1H/SFVDp3fk=
11
+ =Kul/
12
12
  -----END PGP SIGNATURE-----
@@ -1,36 +0,0 @@
1
- ###### Autogenerated by Mizuho, DO NOT EDIT ######
2
- # This file maps section names to IDs so that the commenting system knows which
3
- # comments belong to which section. Section names may be changed at will but
4
- # IDs always stay the same, allowing one to retain old comments even if you
5
- # rename a section.
6
- #
7
- # This file is autogenerated but is not a cache; you MUST NOT DELETE this
8
- # file and you must check it into your version control system. If you lose
9
- # this file you may lose the ability to identity old comments.
10
- #
11
- # Entries marked with "fuzzy" indicate that the section title has changed
12
- # and that Mizuho has found an ID which appears to be associated with that
13
- # section. You should check whether it is correct, and if not, fix it.
14
-
15
- 1. About the involved technologies => about-the-involved-technologies-14ot82u
16
-
17
- 1.1. Typical web applications => typical-web-applications-1yd1iav
18
-
19
- 1.2. Ruby on Rails => ruby-on-rails-5v776m
20
-
21
- 1.3. Apache => apache-123bbrn
22
-
23
- 2. Passenger architecture => passenger-architecture-ehq6p7
24
-
25
- 2.1. Overview => overview-1ickxaj
26
-
27
- 2.2. Spawning and caching of code and applications => spawning-and-caching-of-code-and-applications-rtkxsp
28
-
29
- 2.3. The spawn server => the-spawn-server-ktu9q0
30
-
31
- 2.3.1. Memory sharing => memory-sharing-30wsnk
32
-
33
- 2.4. Handling of concurrent requests => handling-of-concurrent-requests-1ud4gns
34
-
35
- 3. Appendix A: About this document => appendix-a-about-this-document-fo5ufe
36
-
@@ -1,410 +0,0 @@
1
- Phusion Passenger design & architecture
2
- =======================================
3
-
4
- image:images/phusion_banner.png[link="http://www.phusion.nl/"]
5
-
6
- Last updated: June 5, 2012.
7
-
8
- This document describes Phusion Passenger's design and architure in a global way.
9
- Its purpose is to lower the barrier to entry for new contributors,
10
- to explain some of the design choices we have made and to educate people
11
- about how Phusion Passenger works.
12
-
13
-
14
- Introduction to related technologies
15
- ------------------------------------
16
-
17
- [[web_app_models]]
18
- === Web application models ===
19
-
20
- Before we describe Phusion Passenger, it is important to understand how typical web
21
- applications work from the viewpoint of someone who wants to connect the
22
- application to a web server.
23
-
24
- A typical, isolated, web application accepts an HTTP request from some I/O
25
- channel, processes it internally, and outputs an HTTP response, which is sent
26
- back to the client. This is done in a loop, until the application is commanded
27
- to exit. This does not necessarily mean that the web application speaks HTTP
28
- directly: it just means that the web application accepts some kind of
29
- representation of an HTTP request.
30
-
31
- image:images/typical_isolated_web_application.png[Architecture of a typical
32
- web application in isolation]
33
-
34
- Few web applications are accessible directly by HTTP clients. Common models
35
- are:
36
-
37
- 1. The web application is contained in an application server. This application
38
- server may or may not be able to contain multiple web applications. The
39
- application server is then connected to the web server through some kind of
40
- protocol. This protocol may be HTTP, FastCGI, SCGI, AJP or whatever. The web server
41
- dispatches requests to the application server, which in turn dispatches
42
- requests to the correct web application, in a format that the web application
43
- understands. Conversely, HTTP responses outputted by the web application are
44
- sent to the application server, which in turn sends them to the web server,
45
- and eventually to the HTTP client.
46
- +
47
- Typical examples of such a model:
48
- +
49
- * A J2EE application, contained in the Tomcat application server, proxied
50
- behind the Apache web server. Tomcat can contain multiple web applications
51
- in a single Tomcat instance.
52
- * Most Ruby application servers besides Phusion Passenger (Thin, Unicorn,
53
- Goliath, etc). These application servers can only contain a single Ruby
54
- web application per instance. They load the web application into their
55
- own process and are put behind a web server (Apache, Nginx) in a reverse
56
- proxy setup.
57
-
58
- 2. The web application is contained in a web server. In this case, the web
59
- server acts like an application server. This is the case for PHP applications
60
- on Apache servers with 'mod_php'. Note that this does not necessarily mean
61
- that the web application is run inside the same process as the web server:
62
- it just means that the web server manages applications. In case of mod_php
63
- however PHP does run directly inside the Apache worker processes.
64
- +
65
- Phusion Passenger for Apache and Phusion Passenger for Nginx implement this model.
66
-
67
- 3. The web application *is* a web server, and can accept HTTP requests
68
- directly. This is the case for the Trac bug tracking system, running in its
69
- standalone server. In most setups they are reverse proxied behind a real
70
- web server such as Apache or Nginx, instead of accepting HTTP requests directly.
71
- +
72
- Phusion Passenger Standalone implements this model. You can expose Phusion Passenger
73
- Standalone directly to the Internet because it uses Nginx internally.
74
-
75
- 4. The web application does not speak HTTP directly, but is connected directly
76
- to the web server through some communication adapter. CGI, FastCGI and SCGI
77
- are good examples of this.
78
-
79
- These descriptions are true for virtually all web applications, whether they're
80
- based on PHP, Django, J2EE, ASP.NET, Ruby on Rails, or whatever. Note that all
81
- of these models provide the same functionality, i.e. no model can do something
82
- that a different model can't. The critical reader will notice that all of these
83
- models are identical to the one described in the first diagram, if the
84
- combination of web servers, application servers, web applications etc. are
85
- considered to be a single entity; a black box if you will.
86
-
87
- It should also be noted that these models do not enforce any particular
88
- I/O processing implementation. The web servers, application servers, web
89
- applications, etc. could process I/O serially (i.e. one request at a time),
90
- could multiplex I/O with a single thread (e.g. by using `select(2)` or
91
- `poll(2)`) or it could process I/O with multiple threads and/or multiple
92
- processes.
93
-
94
- Of course, there are many variations possible. For example, load balancers
95
- could be used. But that is outside the scope of this document.
96
-
97
- ==== Why reverse proxy? ====
98
-
99
- As you've seen, it is often necessary to put the web application or its
100
- application server behind a real web server in a reverse proxy setup even
101
- when the web app/app server already speaks HTTP. This is because implementing
102
- HTTP in a proper, secure way involves more than just speaking the protocol.
103
- The public Internet is a hostile environment where clients can send any
104
- arbitrary data and can exhibit any arbitrary I/O patterns. Web servers like
105
- Apache and Nginx have already implemented world-class I/O and connection
106
- handling code and it would be a waste to reinvent their wheel. In the end,
107
- putting the application in a reverse proxying setup makes the whole system
108
- more robust and and more secure.
109
-
110
- A typical problem involves dealing with *slow clients*. These clients may send
111
- HTTP requests slowly and read HTTP responses slowly, perhaps taking many seconds
112
- to complete their work. A naive single-threaded HTTP server implementation
113
- that reads an HTTP requests, processes, and sends the HTTP response in a loop
114
- may end up spending so much time waiting for I/O that spends very little time
115
- doing actual work. Worse: suppose that the client is malicious, just leaves the
116
- socket open and never reads the HTTP response, then the server will spend
117
- forever waiting for the client, not being able to handle any more requests.
118
-
119
- .An example of a naive HTTP server implementation
120
- -------------------
121
- while true
122
- client = accept_next_client()
123
- request = read_http_request(client)
124
- response = process_request(request)
125
- send_http_response(client, response)
126
- end
127
- -------------------
128
-
129
- There are many ways to solve this problem. One could use one thread per client,
130
- one could implement I/O timeouts, one could use an evented I/O architecture, one
131
- could have a dedicated I/O thread or process buffer requests and responses.
132
- The point is, implementing all this properly is non-trivial. Instead of
133
- reimplementing these over and over in each application server, it's better to
134
- let a real web server deal with all the details and let the application server
135
- and the web application do what they're best at: their own core business logic.
136
-
137
-
138
- === Ruby Rack and Ruby on Rails ===
139
-
140
- The de-facto standard interface for Ruby web applications is link:http://rack.rubyforge.org/[Rack].
141
- Rack specifies an programming interface for web application developers to implement.
142
- This interface covers HTTP request and response handling, and is not dependent on
143
- any particular application server. The idea is that any Rack-compliant application
144
- server can implement the Rack specification and work with all Rack-compliant web applications.
145
-
146
- image:images/rack.png[]
147
-
148
- In the distant past, each Ruby web framework had its own interface, so application
149
- servers needed to explicitly add support for each web framework. Nowadays application
150
- servers just support Rack.
151
-
152
- image:images/many_web_framework_protocols.png[]
153
-
154
- Ruby on Rails has been fully Rack compliant since version 3.0. Rails 2.3 was partially
155
- Rack-compliant while earlier versions were not Rack-compliant at all. Phusion Passenger
156
- supports Rack as well as all Rails 1.x and 2.x versions.
157
-
158
- A particularly interesting thing to note is that a lot of the memory
159
- occupied by Ruby on Rails applications is spent on storing the program code
160
- (i.e. the link:http://en.wikipedia.org/wiki/Abstract_syntax_tree[abstract
161
- syntax tree (AST)]) in memory. This is observed through the use of the
162
- memory statistics function in link:http://www.rubyenterpriseedition.com/[Ruby
163
- Enterprise Edition]. Also, a lot of the startup time of a Ruby on Rails
164
- application is spent on bootstrapping the Rails framework.
165
-
166
-
167
- === Apache ===
168
-
169
- The Apache web server has a dynamic module system and a pluggable I/O
170
- multiprocessing (the ability to
171
- handle more than 1 concurrent HTTP client at the same time) architecture. An
172
- Apache module which implements a particular multiprocessing strategy, is called
173
- a Multi-Processing Module (MPM). The single-threaded multi-process
174
- link:http://httpd.apache.org/docs/2.4/mod/prefork.html[prefork MPM] had been
175
- the default and the most popular one for a long time, but in recent times the
176
- hybrid multi-threaded/multi-process link:http://httpd.apache.org/docs/2.4/mod/worker.html[worker MPM]
177
- is becoming increasingly popular because of its better performance and scalability.
178
- Furthermore, Apache 2.4 introduced the link:http://httpd.apache.org/docs/2.4/mod/event.html[event MPM]
179
- which is a hybrid evented/multi-threaded/multi-process MPM and offers even more
180
- scalability benefits.
181
-
182
- The prefork MPM remains in wide use today because it's the only MPM that works well with mod_php.
183
-
184
- The prefork MPM spawns multiple worker child processes. HTTP requests are first accepted by a
185
- so-called control process, and then forwarded to one of the worker processes.
186
- The next section contains a diagram which shows the prefork MPM's architecture.
187
-
188
-
189
- === Nginx ===
190
-
191
- Nginx is a lightweight web server that is becoming increasingly popular. It is known
192
- to be smaller, lighter weight and more scalable than Apache thanks to its evented I/O
193
- architecture. That said, Nginx is less flexible than Apache. For example it has no
194
- dynamic module system: all modules must be statically compiled into Nginx.
195
-
196
-
197
- Phusion Passenger architecture
198
- ------------------------------
199
-
200
- === Overview ===
201
-
202
- Phusion Passenger's architecture is a lot like model #2 described in
203
- <<web_app_models,Web application models>>. In other words,
204
- Phusion Passenger extends Apache/Nginx and allows it to act like an
205
- application server. This is shown in the following diagram:
206
-
207
- image:images/passenger_architecture.png[Passenger's architecture]
208
-
209
- Phusion Passenger consists of:
210
-
211
- * an Apache module, 'mod_passenger'. This is written in C++, and can be found in the directory 'ext/apache2'.
212
- * an Nginx module 'ngx_http_passenger_module'. This is written in C, and can be found in the directory 'ext/nginx'.
213
- * Common code used by both the Apache and the Nginx module. For example the helper agent is among this code. This code is mostly C++ and can be found in the directory 'ext/common'.
214
-
215
- The module is active all Apache/Nginx processes. When an HTTP request comes in, the Phusion Passenger module checks whether the request should be handled by a Phusion Passenger-served application. If so, then the module spawns one or more processes for the corresponding application (if necessary), forwards the request to that application process and forwards its generated response back to the client. This is all done with the assistance of the Phusion Passenger helper agent, which stores state that must be shared among all web server worker processes and handles much of the internal I/O between the web server and the application processes.
216
-
217
- It should be noted that applications do *not* run in the same address space as the web server.
218
- This differentiates Passenger from other
219
- application-server-inside-web-server software such as mod_php, mod_perl and
220
- mod_ruby. If the application crashes or leak memory, it will have no
221
- effect on the web server. In fact, stability is one of our highest goals. Phusion Passenger
222
- is carefully designed and implemented so that the web server shouldn't crash because
223
- of Phusion Passenger.
224
-
225
- === Spawning and caching of code and applications ===
226
-
227
- A very naive implementation of an application server would spawn an application
228
- process every time an HTTP request is received, just like CGI would.
229
- However, spawning Ruby applications is typically expensive. It can take a few
230
- seconds on a modern computer, and possibly much longer on a heavily loaded server.
231
- A less naive implementation would keep spawned application processes alive,
232
- similar to how Lighttpd's FastCGI implementation works.
233
- However, this still has several problems:
234
-
235
- 1. The first request to a Rails website will be slow, and subsequent requests
236
- will be fast. But the first request to a different Rails website - on the
237
- same web server - will still be slow.
238
- 2. As we've explained earlier in this article, a lot of memory in a Rails
239
- application is spent on storing the AST of the Ruby on Rails framework and
240
- the application. Especially on shared hosts and on memory-constrained
241
- Virtual Private Servers (VPS), this can be a problem.
242
-
243
- Both of these problems are very much solvable, and we've chosen to do just
244
- that.
245
-
246
- The first problem can be solved by preloading Rails applications, i.e. by
247
- running the Rails application before a request is ever made to that website.
248
- This is the approach taken by most Rails hosts, for example in the form of a
249
- Mongrel cluster which is running all the time. However, this is unacceptable
250
- for a shared host: such an application would just sit there and waste memory
251
- even if it's not doing anything. Instead, we've chosen to take a different
252
- approach, which solves both of the aforementioned problems.
253
-
254
- We spawn Rails applications via a 'spawn server'. The spawn server caches Ruby
255
- on Rails framework code and application code in memory. Spawning a Rails
256
- application for the first time will still be slow, but subsequent spawn
257
- attempts will be very fast. Furthermore, because the framework code is cached
258
- independently from the application code, spawning a different Rails application
259
- will also be very fast, as long as that application is using a Rails framework
260
- version that has already been cached.
261
-
262
- Another implication of the spawn server is that different Ruby on Rails will
263
- share memory with each other, thus solving problem #2. This is described in
264
- detail in <<spawn_server, the next section>>.
265
-
266
- But despite the caching of framework code and application code, spawning is
267
- still expensive compared to an HTTP request. We want to avoid spawning whenever
268
- possible. This is why we've introduced the *application pool*. Spawned
269
- application instances are kept alive, and their handles are stored into this
270
- pool, allowing each application instance to be reused later. Thus, Passenger
271
- has very good average case performance.
272
-
273
- The application pool is shared between different worker processes. Because the
274
- worker processes cannot share memory with each other, either shared memory must
275
- be used to implement the application pool, or a client/server architecture must
276
- be implemented. We've chosen the latter because it is easier
277
- to implement. The Apache control process acts like a server for the application
278
- pool. However, this does not mean that all HTTP request/response data go
279
- through the control process. A worker process queries the pool for a connection
280
- session with a Rails application. Once this session has been obtained, the
281
- worker process will communicate directly with the Rails application.
282
-
283
- The application pool is implemented inside 'mod_passenger'. One can find
284
- detailed documentation about it in
285
- link:cxxapi/index.html[+++the C++ API documentation+++],
286
- in particular the documentation about the `ApplicationPool`,
287
- `StandardApplicationPool` and `ApplicationPoolServer` classes.
288
-
289
- The application pool is responsible for spawning applications, caching
290
- spawned applications' handles, and cleaning up applications which have been
291
- idle for an extended period of time.
292
-
293
- [[spawn_server]]
294
- === The spawn server ===
295
-
296
- The spawn server is written in Ruby, and its code can be found in the directory
297
- 'lib/passenger'. Its main executable is 'bin/passenger-spawn-server'.
298
- link:rdoc/index.html[The spawn server's RDoc documentation] documents the
299
- implementation in detail.
300
-
301
- The spawn server consists of 3 logical layers:
302
-
303
- 1. *The spawn manager.* This is the topmost layer, and acts like a fascade for
304
- all the underlying layers. Clients who use the spawn server only communicate
305
- with this layer.
306
- 2. *The framework spawner server.* The spawn manager spawns a framework spawner
307
- server for each unique Ruby on Rails framework version. Each framework
308
- spawner server caches the code for exactly one Ruby on Rails framework
309
- version. A spawn request for an application is forwarded to the framework
310
- spawner server that contains the correct Ruby on Rails version for the
311
- application.
312
- 3. *The application spawner server.* This is to the framework spawner server
313
- what the framework spawner server is to the spawn manager. The framework
314
- spawner server spawns an application spawner server for each unique Ruby on
315
- Rails application (here ``application'' does not mean a running process, but
316
- a set of (source code) files). An application spawner server caches the
317
- code for exactly one application.
318
-
319
- image:images/spawn_server_architecture.png[The spawn server's architecture]
320
-
321
- As you can see, we have two layers of code caching: when the spawn server
322
- receives a request to spawn a new application instance, it will forward the
323
- request to the correct framework spawner server (and will spawn that framework
324
- spawner server if it doesn't already exist), which -- in turn -- will forward
325
- it to the correct application spawner server (which will, again, be created if
326
- it doesn't already exist).
327
-
328
- Each layer is only responsible for the layer directly below. The spawn manager
329
- only knows about framework spawner servers, and a framework spawner server only
330
- knows about its application spawner servers. The application spawner server is,
331
- however, not responsible for managing spawned application instances. If an
332
- application instance is spawned by mod_passenger, its information will be sent
333
- back to mod_passenger, which will be fully responsible for managing the
334
- application instance's life time (through the application pool).
335
-
336
- Also note that each layer is a seperate process. This is required because a
337
- single Ruby process can only load a single Ruby on Rails framework and a
338
- single application.
339
-
340
- ==== Memory sharing ====
341
-
342
- On most modern Unix operating systems, when a child process is created, it will
343
- share most of its memory with the parent process. Processes are not supposed to
344
- be able to access each others' memory, so the operating system makes a copy of
345
- a piece of memory when it is written to by the parent process or the child
346
- process. This is called copy-on-write (COW). Detailed background information
347
- can be found on link:http://www.rubyenterpriseedition.com/[Ruby Enterprise
348
- Edition's website].
349
-
350
- The spawn server makes use of this useful fact. Each layer shares its Ruby AST
351
- memory with all of its lower layers, as long as the AST nodes in question
352
- haven't been written to. This means that all spawned Rails applications will
353
- -- if possible -- share the Ruby on Rails framework's code, as well as its own
354
- application code, with each other. This results in a dramatic reduction in
355
- memory usage.
356
-
357
- [NOTE]
358
- ==================================================================
359
- Sharing memory only works if link:http://www.rubyenterpriseedition.com/[Ruby
360
- Enterprise Edition] is used. This is because the standard Ruby interpreter's
361
- garbage collector isn't copy-on-write friendly. Please visit the Ruby
362
- Enterprise Edition website for technical details.
363
-
364
- Passenger works fine with standard Ruby. You still get to enjoy reduced Rails
365
- startup times. You just won't be able to benefit from memory sharing.
366
- ==================================================================
367
-
368
- Note that link:http://rubini.us/[Rubinius]'s garbage collector is already
369
- copy-on-write friendly.
370
-
371
-
372
- [[concurrent_requests]]
373
- === Handling of concurrent requests ===
374
-
375
- As explained earlier, a single Rails application instance can only handle a
376
- single request at the same time. This is obviously undesirable. But before we
377
- dive into the solution, let us take a look how the ``competition'' solves this
378
- problem. PHP has similar problems: a single PHP script can also process only
379
- one HTTP request at a time.
380
-
381
- - mod_php ``solves'' this problem by using Apache's MPM. In other words,
382
- mod_php doesn't do anything by itself at all. A single Apache worker
383
- process/thread can only handle 1 PHP request at a time, but Apache spawns
384
- multiple worker processes/threads.
385
- - PHP-FastCGI solves the problem by spawning multiple persistent PHP servers.
386
- The number of PHP servers is independent from the number of Apache worker
387
- processes/threads. This approach is a lot like existing Rails setups, in
388
- which a frontend web server proxies requests to a persistent Mongrel cluster.
389
-
390
- Passenger cannot use the mod_php way because it would force us to spawn a new
391
- Rails application for each request, which is -- as explained earlier --
392
- unacceptably slow. Instead, Passenger uses the PHP-FastCGI approach. We
393
- maintain a pool of application instances, and whenever a request is received,
394
- we forward the request to one of the application instances in the pool. The
395
- size of the pool is configurable, which is useful for administrators of servers
396
- that are either heavily loaded or have little memory.
397
-
398
- The reader might also be interested in studying the application pool's
399
- algorithm, which is non-trivial. The algorithm is documented in detail in
400
- link:ApplicationPool%20algorithm.txt[ApplicationPool algorithm.txt].
401
-
402
-
403
- == Appendix A: About this document ==
404
-
405
- The text of this document is licensed under the
406
- link:http://creativecommons.org/licenses/by-sa/3.0/[Creative Commons
407
- Attribution-Share Alike 3.0 Unported License].
408
-
409
- image:images/by_sa.png[link="link:http://creativecommons.org/licenses/by-sa/3.0/"]
410
-