passenger 4.0.36 → 4.0.37

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 (41) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/NEWS +51 -0
  5. data/README.md +3 -1
  6. data/build/integration_tests.rb +5 -1
  7. data/build/test_basics.rb +2 -2
  8. data/dev/run_travis.sh +3 -0
  9. data/doc/Users guide Nginx.txt +3 -3
  10. data/ext/common/ApplicationPool2/Group.h +2 -1
  11. data/ext/common/ApplicationPool2/Implementation.cpp +30 -1
  12. data/ext/common/ApplicationPool2/Pool.h +26 -3
  13. data/ext/common/ApplicationPool2/Process.h +39 -10
  14. data/ext/common/Constants.h +1 -1
  15. data/ext/common/MultiLibeio.cpp +4 -0
  16. data/ext/common/ServerInstanceDir.h +1 -1
  17. data/ext/common/Utils.cpp +29 -0
  18. data/ext/common/Utils.h +7 -1
  19. data/ext/common/Utils/BufferedIO.h +13 -0
  20. data/ext/common/agents/HelperAgent/Main.cpp +6 -2
  21. data/ext/common/agents/HelperAgent/RequestHandler.h +32 -1
  22. data/helper-scripts/meteor-loader.rb +126 -10
  23. data/helper-scripts/node-loader.js +5 -3
  24. data/helper-scripts/wsgi-loader.py +23 -11
  25. data/lib/phusion_passenger.rb +1 -1
  26. data/lib/phusion_passenger/config/detach_process_command.rb +96 -0
  27. data/lib/phusion_passenger/config/main.rb +1 -0
  28. data/lib/phusion_passenger/request_handler.rb +11 -4
  29. data/node_lib/phusion_passenger/httplib_emulation.js +20 -14
  30. data/test/cxx/RequestHandlerTest.cpp +80 -0
  31. data/test/integration_tests/apache2_tests.rb +57 -0
  32. data/test/integration_tests/nginx_tests.rb +62 -0
  33. data/test/node/httplib_emulation_spec.js +137 -5
  34. data/test/node/spec_helper.js +13 -0
  35. data/test/stub/node/app.js +125 -0
  36. data/test/stub/node/public/.gitignore +0 -0
  37. data/test/stub/node/tmp/.gitignore +0 -0
  38. data/test/stub/rack/config.ru +19 -0
  39. data/test/stub/wsgi/passenger_wsgi.py +37 -1
  40. metadata +6 -2
  41. metadata.gz.asc +7 -7
@@ -91,6 +91,52 @@ describe('HttplibEmulation', function() {
91
91
  return result;
92
92
  }
93
93
 
94
+ describe('the request object', function() {
95
+ beforeEach(function() {
96
+ var state = this.state;
97
+ state.setup = function(headers, callback) {
98
+ if (!callback) {
99
+ callback = headers;
100
+ headers = {};
101
+ }
102
+ state.headers = createHeaders(headers);
103
+ state.createSocket(function(serverSocket, client) {
104
+ state.req = HttplibEmulation.createIncomingMessage(
105
+ state.headers, serverSocket, "");
106
+ callback();
107
+ });
108
+ }
109
+ });
110
+
111
+ specify('.on() returns the request object', function(done) {
112
+ var state = this.state;
113
+ state.setup(function() {
114
+ var result = state.req.on('foo', function() {});
115
+ assert.strictEqual(result, state.req);
116
+ done();
117
+ });
118
+ });
119
+
120
+ it('sets no "upgrade" flag if there is no Upgrade header', function(done) {
121
+ var state = this.state;
122
+ state.setup(function() {
123
+ assert.ok(!state.req.upgrade);
124
+ done();
125
+ });
126
+ });
127
+
128
+ it('sets the "upgrade" flag if there is an Upgrade header', function(done) {
129
+ var state = this.state;
130
+ var headers = {
131
+ 'HTTP_UPGRADE': 'WebSocket'
132
+ };
133
+ state.setup(headers, function() {
134
+ assert.ok(state.req.upgrade);
135
+ done();
136
+ });
137
+ });
138
+ });
139
+
94
140
  describe('if the request may have a request body', function() {
95
141
  beforeEach(function() {
96
142
  var state = this.state;
@@ -105,7 +151,6 @@ describe('HttplibEmulation', function() {
105
151
  state.createSocket(function(serverSocket, client) {
106
152
  state.req = HttplibEmulation.createIncomingMessage(
107
153
  state.headers, serverSocket, "");
108
- assert.ok(state.req._mayHaveRequestBody);
109
154
  callback();
110
155
  });
111
156
  }
@@ -165,7 +210,7 @@ describe('HttplibEmulation', function() {
165
210
  });
166
211
  });
167
212
 
168
- it("sends data events as data is received", function(done) {
213
+ specify("the request object emits data events as data is received", function(done) {
169
214
  var state = this.state;
170
215
  var chunks = [];
171
216
 
@@ -182,7 +227,7 @@ describe('HttplibEmulation', function() {
182
227
  });
183
228
  });
184
229
 
185
- it("sends the end event after the client closes the socket", function(done) {
230
+ specify("the request object emits the end event after the client closes the socket", function(done) {
186
231
  var state = this.state;
187
232
  var finished;
188
233
 
@@ -214,7 +259,7 @@ describe('HttplibEmulation', function() {
214
259
  });
215
260
  });
216
261
 
217
- it("emits readable events upon receiving data", function(done) {
262
+ specify("the request object emits readable events upon receiving data", function(done) {
218
263
  var state = this.state;
219
264
  var readable = 0;
220
265
  state.req.on('readable', function() {
@@ -380,7 +425,6 @@ describe('HttplibEmulation', function() {
380
425
  state.createSocket(function(serverSocket, client) {
381
426
  state.req = HttplibEmulation.createIncomingMessage(
382
427
  state.headers, serverSocket, "");
383
- assert.ok(!state.req._mayHaveRequestBody);
384
428
  callback();
385
429
  });
386
430
  }
@@ -488,4 +532,92 @@ describe('HttplibEmulation', function() {
488
532
  });
489
533
  });
490
534
  });
535
+
536
+ describe('requests with Upgrade header', function() {
537
+ beforeEach(function() {
538
+ var state = this.state;
539
+ state.setup = function(headers, callback) {
540
+ if (!callback) {
541
+ callback = headers;
542
+ headers = {
543
+ 'HTTP_UPGRADE': 'websocket'
544
+ };
545
+ }
546
+ state.headers = createHeaders(headers);
547
+ state.createSocket(function(serverSocket, client) {
548
+ state.req = HttplibEmulation.createIncomingMessage(
549
+ state.headers, serverSocket, "");
550
+ callback();
551
+ });
552
+ }
553
+ });
554
+
555
+ specify('the request object emits no data events', function(done) {
556
+ var state = this.state;
557
+ state.setup(function() {
558
+ var hasData = false;
559
+
560
+ state.req.on('data', function(data) {
561
+ hasData = true;
562
+ });
563
+ state.client.write("hello");
564
+
565
+ Helper.shouldNeverHappen(50, function() {
566
+ return hasData;
567
+ }, done);
568
+ });
569
+ });
570
+
571
+ specify('the request object ends immediately', function(done) {
572
+ var state = this.state;
573
+ state.setup(function() {
574
+ var readable = false;
575
+ var readData;
576
+ var ended = false;
577
+
578
+ state.req.on('readable', function() {
579
+ readable = true;
580
+ readData = state.req.read(100);
581
+ });
582
+ state.req.on('end', function() {
583
+ ended = true;
584
+ });
585
+
586
+ Helper.eventually(50, function() {
587
+ return readable && ended;
588
+ }, function() {
589
+ assert.strictEqual(readData, null);
590
+ done();
591
+ });
592
+ });
593
+ });
594
+
595
+ specify('the socket emits data events as data is received', function(done) {
596
+ var state = this.state;
597
+ state.setup(function() {
598
+ var hasData = false;
599
+
600
+ state.req.socket.on('data', function(data) {
601
+ hasData = true;
602
+ });
603
+ state.client.write("hello");
604
+
605
+ Helper.eventually(50, function() {
606
+ return hasData;
607
+ }, done);
608
+ });
609
+ });
610
+
611
+ it('allows reading from the socket', function(done) {
612
+ var state = this.state;
613
+ state.setup(function() {
614
+ state.req.socket.on('readable', function() {
615
+ var chunk = state.req.socket.read(5).toString('utf-8');
616
+ chunk.should.eql("hello");
617
+ done();
618
+ });
619
+ state.client.write("hello");
620
+ });
621
+ });
622
+ });
491
623
  });
@@ -46,6 +46,19 @@ var Helper = {
46
46
  assert.fail("Something which should eventually happen never happened");
47
47
  }
48
48
  }, 10);
49
+ },
50
+
51
+ shouldNeverHappen: function(timeout, check, done) {
52
+ var startTime = new Date();
53
+ var id = setInterval(function() {
54
+ if (check()) {
55
+ clearInterval(id);
56
+ assert.fail("Something which should never happen, happened anyway");
57
+ } else if (new Date() - startTime > timeout) {
58
+ clearInterval(id);
59
+ done();
60
+ }
61
+ }, 10);
49
62
  }
50
63
  };
51
64
 
@@ -0,0 +1,125 @@
1
+ var fs = require('fs');
2
+ var url = require('url');
3
+ var express = require('express');
4
+ var app = express();
5
+ var bodyParser = express.bodyParser();
6
+
7
+ function textResponse(res, content) {
8
+ content = String(content);
9
+ res.setHeader("Content-Type", "text/plain");
10
+ res.setHeader("Content-Length", content.length);
11
+ res.end(content);
12
+ }
13
+
14
+ function fileExists(filename) {
15
+ try {
16
+ fs.statSync(filename);
17
+ return true;
18
+ } catch (e) {
19
+ return false;
20
+ }
21
+ }
22
+
23
+ if (process.env.PASSENGER_BASE_URI) {
24
+ app.use(process.env.PASSENGER_BASE_URI, app.router);
25
+ }
26
+
27
+ app.all('/', function(req, res) {
28
+ if (fileExists("front_page.txt")) {
29
+ textResponse(res, fs.readFileSync("front_page.txt"));
30
+ } else {
31
+ textResponse(res, "front page");
32
+ }
33
+ });
34
+
35
+ app.all('/parameters', function(req, res) {
36
+ bodyParser(req, res, function() {
37
+ var first = req.query.first || req.body.first;
38
+ var second = req.query.second || req.body.second;
39
+ textResponse(res, "Method: " + req.method + "\n" +
40
+ "First: " + first + "\n" +
41
+ "Second: " + second + "\n")
42
+ });
43
+ });
44
+
45
+ app.all('/chunked', function(req, res) {
46
+ res.setHeader("Content-Type", "text/plain");
47
+ res.setHeader("Transfer-Encoding", "chunked");
48
+ res.write("chunk1\n");
49
+ res.write("chunk2\n");
50
+ res.write("chunk3\n");
51
+ res.end();
52
+ });
53
+
54
+ app.all('/pid', function(req, res) {
55
+ textResponse(res, process.pid);
56
+ });
57
+
58
+ app.all(/^\/env/, function(req, res) {
59
+ var body = '';
60
+ var keys = [];
61
+ for (var key in req.cgiHeaders) {
62
+ keys.push(key);
63
+ }
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";
71
+ }
72
+ textResponse(res, body);
73
+ });
74
+
75
+ app.all('/touch_file', function(req, res) {
76
+ bodyParser(req, res, function() {
77
+ var filename = req.query.file || req.body.file;
78
+ fs.writeFileSync(filename, "");
79
+ textResponse(res, "ok")
80
+ });
81
+ });
82
+
83
+ app.all('/extra_header', function(req, res) {
84
+ res.setHeader("Content-Type", "text/html");
85
+ res.setHeader("Content-Length", "2");
86
+ res.setHeader("X-Foo", "Bar");
87
+ res.end("ok");
88
+ });
89
+
90
+ app.all('/cached', function(req, res) {
91
+ textResponse(res, "This is the uncached version of /cached");
92
+ });
93
+
94
+ app.all('/upload_with_params', function(req, res) {
95
+ bodyParser(req, res, function() {
96
+ var name1 = req.query.name1 || req.body.name1;
97
+ var name2 = req.query.name2 || req.body.name2;
98
+ var data = fs.readFileSync(req.files.data.path);
99
+ var body =
100
+ "name 1 = " + name1 + "\n" +
101
+ "name 2 = " + name2 + "\n" +
102
+ "data = ";
103
+ var bodyBuffer = new Buffer(body);
104
+ res.setHeader("Content-Type", "text/plain");
105
+ res.setHeader("Content-Length", bodyBuffer.length + data.length);
106
+ res.write(bodyBuffer);
107
+ res.write(data);
108
+ res.end();
109
+ });
110
+ });
111
+
112
+ app.all('/raw_upload_to_file', function(req, res) {
113
+ var filename = req.headers['x-output'];
114
+ var stream = fs.createWriteStream(filename);
115
+ req.on('data', function(data) {
116
+ stream.write(data);
117
+ });
118
+ req.on('end', function() {
119
+ stream.end(function() {
120
+ textResponse(res, "ok");
121
+ });
122
+ });
123
+ });
124
+
125
+ app.listen(3000);
File without changes
File without changes
@@ -75,6 +75,25 @@ app = lambda do |env|
75
75
  sleep 0.1 # Give HelperAgent the time to process stdout first.
76
76
  STDERR.puts "hello stderr!"
77
77
  text_response("ok")
78
+ when '/switch_protocol'
79
+ if env['HTTP_UPGRADE'] != 'raw' || env['HTTP_CONNECTION'].downcase != 'upgrade'
80
+ return [500, { "Content-Type" => "text/plain" }, ["Invalid headers"]]
81
+ end
82
+ env['rack.hijack'].call
83
+ io = env['rack.hijack_io']
84
+ begin
85
+ io.write("Status: 101 Switching Protocols\r\n")
86
+ io.write("Upgrade: raw\r\n")
87
+ io.write("Connection: Upgrade\r\n")
88
+ io.write("\r\n")
89
+ while !io.eof?
90
+ line = io.readline
91
+ io.write("Echo: #{line}")
92
+ io.flush
93
+ end
94
+ ensure
95
+ io.close
96
+ end
78
97
  else
79
98
  [404, { "Content-Type" => "text/plain" }, ["Unknown URI"]]
80
99
  end
@@ -1,4 +1,4 @@
1
- import os, time, cgi
1
+ import os, sys, time, cgi
2
2
 
3
3
  def file_exist(filename):
4
4
  try:
@@ -7,6 +7,19 @@ def file_exist(filename):
7
7
  except OSError:
8
8
  return False
9
9
 
10
+ if sys.version_info[0] >= 3:
11
+ def bytes_to_str(b):
12
+ return b.decode()
13
+
14
+ def str_to_bytes(s):
15
+ return s.encode('latin-1')
16
+ else:
17
+ def bytes_to_str(b):
18
+ return b
19
+
20
+ def str_to_bytes(s):
21
+ return s
22
+
10
23
  def application(env, start_response):
11
24
  status = '200 OK'
12
25
  body = None
@@ -131,6 +144,29 @@ def application(env, start_response):
131
144
  elif path == '/oobw':
132
145
  start_response(status, [('Content-Type', 'text/plain'), ('X-Passenger-Request-OOB-Work', 'true')])
133
146
  return [str(os.getpid())]
147
+ elif path == '/switch_protocol':
148
+ if env['HTTP_UPGRADE'] != 'raw' or env['HTTP_CONNECTION'].lower() != 'upgrade':
149
+ status = '500 Internal Server Error'
150
+ body = str('Invalid headers')
151
+ start_response(status, [('Content-Type', 'text/plain'), ('Content-Length', len(body))])
152
+ return [body]
153
+ socket = env['passenger.hijack']()
154
+ io = socket.makefile()
155
+ socket.close()
156
+ try:
157
+ io.write(
158
+ b"HTTP/1.1 101 Switching Protocols\r\n" +
159
+ b"Upgrade: raw\r\n" +
160
+ b"Connection: Upgrade\r\n" +
161
+ b"\r\n")
162
+ io.flush()
163
+ line = io.readline()
164
+ while line != "":
165
+ io.write("Echo: " + line)
166
+ io.flush()
167
+ line = io.readline()
168
+ finally:
169
+ io.close()
134
170
  else:
135
171
  status = "404 Not Found"
136
172
  body = "Unknown URI"
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.36
4
+ version: 4.0.37
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-01-24 00:00:00.000000000 Z
11
+ date: 2014-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -2363,6 +2363,7 @@ files:
2363
2363
  - lib/phusion_passenger/common_library.rb
2364
2364
  - lib/phusion_passenger/config/about_command.rb
2365
2365
  - lib/phusion_passenger/config/command.rb
2366
+ - lib/phusion_passenger/config/detach_process_command.rb
2366
2367
  - lib/phusion_passenger/config/main.rb
2367
2368
  - lib/phusion_passenger/config/restart_app_command.rb
2368
2369
  - lib/phusion_passenger/config/utils.rb
@@ -2590,6 +2591,9 @@ files:
2590
2591
  - test/stub/nginx/mime.types
2591
2592
  - test/stub/nginx/nginx.conf.erb
2592
2593
  - test/stub/nginx/win-utf
2594
+ - test/stub/node/app.js
2595
+ - test/stub/node/public/.gitignore
2596
+ - test/stub/node/tmp/.gitignore
2593
2597
  - test/stub/rack/config.ru
2594
2598
  - test/stub/rack/public/.gitignore
2595
2599
  - test/stub/rack/start.rb