passenger 4.0.2 → 4.0.3

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 (79) hide show
  1. data.tar.gz.asc +7 -7
  2. data/NEWS +27 -0
  3. data/bin/passenger-config +6 -3
  4. data/bin/passenger-install-apache2-module +2 -2
  5. data/bin/passenger-install-nginx-module +16 -2
  6. data/build/agents.rb +4 -0
  7. data/build/apache2.rb +1 -1
  8. data/build/cplusplus_support.rb +1 -1
  9. data/build/cxx_tests.rb +3 -0
  10. data/build/packaging.rb +51 -8
  11. data/build/ruby_extension.rb +1 -1
  12. data/doc/Packaging.txt.md +20 -7
  13. data/doc/Users guide Apache.html +1 -1
  14. data/doc/Users guide Apache.txt +1 -1
  15. data/doc/Users guide Nginx.html +5 -4
  16. data/doc/Users guide Nginx.txt +1 -1
  17. data/doc/users_guide_snippets/installation.txt +5 -3
  18. data/ext/apache2/Configuration.cpp +12 -0
  19. data/ext/apache2/Configuration.hpp +7 -4
  20. data/ext/apache2/Hooks.cpp +29 -19
  21. data/ext/common/AgentsStarter.cpp +85 -57
  22. data/ext/common/AgentsStarter.h +570 -42
  23. data/ext/common/ApplicationPool2/DirectSpawner.h +5 -2
  24. data/ext/common/ApplicationPool2/Implementation.cpp +7 -1
  25. data/ext/common/ApplicationPool2/Pool.h +6 -3
  26. data/ext/common/ApplicationPool2/Process.h +12 -3
  27. data/ext/common/ApplicationPool2/SmartSpawner.h +2 -1
  28. data/ext/common/Constants.h +4 -1
  29. data/ext/common/EventedBufferedInput.h +139 -16
  30. data/ext/common/MultiLibeio.cpp +4 -2
  31. data/ext/common/SafeLibev.h +15 -62
  32. data/ext/common/ServerInstanceDir.h +10 -26
  33. data/ext/common/Utils.cpp +1 -3
  34. data/ext/common/Utils.h +1 -1
  35. data/ext/common/Utils/StrIntUtils.cpp +9 -0
  36. data/ext/common/Utils/StrIntUtils.h +5 -0
  37. data/ext/common/Utils/VariantMap.h +63 -14
  38. data/ext/common/agents/Base.cpp +50 -15
  39. data/ext/common/agents/HelperAgent/AgentOptions.h +20 -12
  40. data/ext/common/agents/HelperAgent/FileBackedPipe.h +1 -1
  41. data/ext/common/agents/HelperAgent/Main.cpp +5 -4
  42. data/ext/common/agents/HelperAgent/RequestHandler.h +1 -1
  43. data/ext/common/agents/LoggingAgent/Main.cpp +0 -1
  44. data/ext/common/agents/LoggingAgent/RemoteSender.h +2 -2
  45. data/ext/common/agents/SpawnPreparer.cpp +23 -5
  46. data/ext/common/agents/Watchdog/AgentWatcher.cpp +508 -0
  47. data/ext/common/agents/Watchdog/HelperAgentWatcher.cpp +93 -0
  48. data/ext/common/agents/Watchdog/LoggingAgentWatcher.cpp +68 -0
  49. data/ext/common/agents/Watchdog/Main.cpp +180 -802
  50. data/ext/common/agents/Watchdog/ServerInstanceDirToucher.cpp +111 -0
  51. data/ext/nginx/Configuration.c +107 -92
  52. data/ext/nginx/Configuration.h +1 -0
  53. data/ext/nginx/ContentHandler.c +6 -6
  54. data/ext/nginx/ContentHandler.h +1 -1
  55. data/ext/nginx/config +8 -2
  56. data/ext/nginx/ngx_http_passenger_module.c +54 -60
  57. data/ext/nginx/ngx_http_passenger_module.h +6 -6
  58. data/lib/phusion_passenger.rb +17 -10
  59. data/lib/phusion_passenger/admin_tools/server_instance.rb +2 -2
  60. data/lib/phusion_passenger/common_library.rb +0 -1
  61. data/lib/phusion_passenger/platform_info.rb +10 -1
  62. data/lib/phusion_passenger/platform_info/depcheck.rb +4 -4
  63. data/lib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb +2 -2
  64. data/lib/phusion_passenger/platform_info/ruby.rb +7 -0
  65. data/lib/phusion_passenger/request_handler.rb +119 -42
  66. data/lib/phusion_passenger/request_handler/thread_handler.rb +25 -22
  67. data/lib/phusion_passenger/standalone/command.rb +2 -0
  68. data/lib/phusion_passenger/standalone/runtime_installer.rb +4 -3
  69. data/lib/phusion_passenger/standalone/start_command.rb +49 -37
  70. data/resources/templates/nginx/pcre_checksum_could_not_be_verified.txt.erb +11 -0
  71. data/test/cxx/CxxTestMain.cpp +2 -0
  72. data/test/cxx/EventedBufferedInputTest.cpp +758 -0
  73. data/test/cxx/ServerInstanceDirTest.cpp +16 -31
  74. data/test/cxx/TestSupport.cpp +2 -1
  75. data/test/cxx/VariantMapTest.cpp +23 -11
  76. metadata +8 -4
  77. metadata.gz.asc +7 -7
  78. data/ext/common/AgentsStarter.hpp +0 -655
  79. data/lib/phusion_passenger/utils/robust_interruption.rb +0 -173
@@ -141,6 +141,8 @@ loadConfigFile() {
141
141
  static void
142
142
  abortHandler(int signo, siginfo_t *info, void *ctx) {
143
143
  // Stop itself so that we can attach it to gdb.
144
+ static const char message[] = "Crash handler called!\n";
145
+ write(STDERR_FILENO, message, sizeof(message) - 1);
144
146
  raise(SIGSTOP);
145
147
  // Run default signal handler.
146
148
  raise(signo);
@@ -0,0 +1,758 @@
1
+ #include <TestSupport.h>
2
+ #include <BackgroundEventLoop.h>
3
+ #include <EventedBufferedInput.h>
4
+ #include <Constants.h>
5
+ #include <Utils.h>
6
+ #include <Utils/IOUtils.h>
7
+ #include <Utils/StrIntUtils.h>
8
+
9
+ using namespace Passenger;
10
+ using namespace std;
11
+
12
+ namespace tut {
13
+ class MyEventedBufferedInput: public EventedBufferedInput<> {
14
+ public:
15
+ boost::mutex syncher;
16
+ int readError;
17
+ function<void ()> onAfterProcessingBuffer;
18
+
19
+ MyEventedBufferedInput(SafeLibev *libev, const FileDescriptor &fd)
20
+ : EventedBufferedInput<>(libev, fd)
21
+ {
22
+ readError = 0;
23
+ }
24
+
25
+ virtual ssize_t readSocket(void *buf, size_t n) {
26
+ int readError;
27
+ {
28
+ lock_guard<boost::mutex> l(syncher);
29
+ readError = this->readError;
30
+ }
31
+ if (readError == 0) {
32
+ return EventedBufferedInput<>::readSocket(buf, n);
33
+ } else {
34
+ errno = readError;
35
+ return -1;
36
+ }
37
+ }
38
+
39
+ void setReadError(int code) {
40
+ lock_guard<boost::mutex> l(syncher);
41
+ readError = code;
42
+ }
43
+
44
+ virtual void afterProcessingBuffer() {
45
+ function<void ()> onAfterProcessingBuffer;
46
+ {
47
+ lock_guard<boost::mutex> l(syncher);
48
+ onAfterProcessingBuffer = this->onAfterProcessingBuffer;
49
+ }
50
+ if (onAfterProcessingBuffer) {
51
+ onAfterProcessingBuffer();
52
+ }
53
+ }
54
+ };
55
+
56
+ struct EventedBufferedInputTest {
57
+ BackgroundEventLoop bg;
58
+ Pipe p;
59
+ shared_ptr<MyEventedBufferedInput> ebi;
60
+ boost::mutex syncher;
61
+ string log;
62
+ ssize_t toConsume;
63
+ unsigned int counter;
64
+
65
+ EventedBufferedInputTest() {
66
+ p = createPipe();
67
+ ebi = make_shared<MyEventedBufferedInput>(bg.safe.get(), p.first);
68
+ ebi->onData = onData;
69
+ ebi->onError = onError;
70
+ ebi->userData = this;
71
+ toConsume = -1;
72
+ counter = 0;
73
+ bg.start();
74
+ }
75
+
76
+ ~EventedBufferedInputTest() {
77
+ bg.stop();
78
+ setLogLevel(DEFAULT_LOG_LEVEL);
79
+ }
80
+
81
+ static size_t onData(const EventedBufferedInputPtr &input, const StaticString &data) {
82
+ EventedBufferedInputTest *self = (EventedBufferedInputTest *) input->userData;
83
+ lock_guard<boost::mutex> l(self->syncher);
84
+ self->counter++;
85
+ if (data.empty()) {
86
+ self->log.append("EOF\n");
87
+ } else {
88
+ self->log.append("Data: " + cEscapeString(data) + "\n");
89
+ }
90
+ if (self->toConsume == -1) {
91
+ return data.size();
92
+ } else {
93
+ return self->toConsume;
94
+ }
95
+ }
96
+
97
+ static void onError(const EventedBufferedInputPtr &input, const char *message, int code) {
98
+ EventedBufferedInputTest *self = (EventedBufferedInputTest *) input->userData;
99
+ lock_guard<boost::mutex> l(self->syncher);
100
+ self->log.append("Error: " + toString(code) + "\n");
101
+ }
102
+
103
+ unsigned int getCounter() {
104
+ lock_guard<boost::mutex> l(syncher);
105
+ return counter;
106
+ }
107
+
108
+ void startEbi() {
109
+ bg.safe->run(boost::bind(&EventedBufferedInputTest::realStartEbi, this));
110
+ }
111
+
112
+ void realStartEbi() {
113
+ ebi->start();
114
+ }
115
+
116
+ bool ebiIsStarted() {
117
+ bool result;
118
+ bg.safe->run(boost::bind(&EventedBufferedInputTest::realEbiIsStarted, this, &result));
119
+ return result;
120
+ }
121
+
122
+ void realEbiIsStarted(bool *result) {
123
+ *result = ebi->isStarted();
124
+ }
125
+
126
+ void logEbiIsStarted() {
127
+ lock_guard<boost::mutex> l(syncher);
128
+ log.append("isStarted: " + toString(ebi->isStarted()) + "\n");
129
+ log.append("isSocketStarted: " + toString(ebi->isSocketStarted()) + "\n");
130
+ }
131
+ };
132
+
133
+ #define LOCK() lock_guard<boost::mutex> l(syncher)
134
+
135
+ #define DEFINE_ON_DATA_METHOD(name, code) \
136
+ static size_t name(const EventedBufferedInputPtr &input, const StaticString &data) { \
137
+ EventedBufferedInputTest *self = (EventedBufferedInputTest *) input->userData; \
138
+ boost::mutex &syncher = self->syncher; \
139
+ string &log = self->log; \
140
+ shared_ptr<MyEventedBufferedInput> &ebi = self->ebi; \
141
+ /* Shut up compiler warning */ \
142
+ (void) syncher; \
143
+ (void) log; \
144
+ (void) ebi; \
145
+ code \
146
+ }
147
+
148
+ #define DEFINE_FINISH_METHOD(name, code) \
149
+ static void name(EventedBufferedInputTest *self) { \
150
+ boost::mutex &syncher = self->syncher; \
151
+ string &log = self->log; \
152
+ shared_ptr<MyEventedBufferedInput> &ebi = self->ebi; \
153
+ /* Shut up compiler warning */ \
154
+ (void) syncher; \
155
+ (void) log; \
156
+ (void) ebi; \
157
+ code \
158
+ }
159
+
160
+ DEFINE_TEST_GROUP(EventedBufferedInputTest);
161
+
162
+ TEST_METHOD(1) {
163
+ set_test_name("It emits socket data events upon receiving data");
164
+ startEbi();
165
+ writeExact(p.second, "aaabbb");
166
+ EVENTUALLY(5,
167
+ LOCK();
168
+ result = !log.empty();
169
+ );
170
+ LOCK();
171
+ ensure_equals(log, "Data: aaabbb\n");
172
+ }
173
+
174
+ TEST_METHOD(2) {
175
+ set_test_name("It emits socket end events upon receiving EOF");
176
+ startEbi();
177
+ p.second.close();
178
+ EVENTUALLY(5,
179
+ LOCK();
180
+ result = !log.empty();
181
+ );
182
+ LOCK();
183
+ ensure_equals(log, "EOF\n");
184
+ }
185
+
186
+ TEST_METHOD(3) {
187
+ set_test_name("It emits socket end events after all data has been consumed");
188
+ startEbi();
189
+
190
+ writeExact(p.second, "aaabbb");
191
+ EVENTUALLY(5,
192
+ LOCK();
193
+ result = !log.empty();
194
+ );
195
+ {
196
+ LOCK();
197
+ ensure_equals(log, "Data: aaabbb\n");
198
+ }
199
+
200
+ p.second.close();
201
+ EVENTUALLY(5,
202
+ LOCK();
203
+ result = log.find("EOF") != string::npos;
204
+ );
205
+ {
206
+ LOCK();
207
+ ensure_equals(log,
208
+ "Data: aaabbb\n"
209
+ "EOF\n");
210
+ }
211
+ }
212
+
213
+ TEST_METHOD(4) {
214
+ set_test_name("Considers ended sockets to be paused");
215
+ startEbi();
216
+ p.second.close();
217
+ EVENTUALLY(5,
218
+ LOCK();
219
+ result = !log.empty();
220
+ );
221
+ ensure(!ebiIsStarted());
222
+ }
223
+
224
+ TEST_METHOD(5) {
225
+ set_test_name("It emits error events upon encountering a socket error");
226
+ startEbi();
227
+ ebi->setReadError(EIO);
228
+ writeExact(p.second, "aaabbb");
229
+ EVENTUALLY(5,
230
+ LOCK();
231
+ result = !log.empty();
232
+ );
233
+ LOCK();
234
+ ensure_equals(log, "Error: " + toString(EIO) + "\n");
235
+ }
236
+
237
+ TEST_METHOD(6) {
238
+ set_test_name("It emits error events after all data has been consumed");
239
+ startEbi();
240
+
241
+ writeExact(p.second, "aaabbb");
242
+ EVENTUALLY(5,
243
+ LOCK();
244
+ result = !log.empty();
245
+ );
246
+
247
+ ebi->setReadError(EIO);
248
+ writeExact(p.second, "x");
249
+ EVENTUALLY(5,
250
+ LOCK();
251
+ result = log.find("Error") != string::npos;
252
+ );
253
+
254
+ LOCK();
255
+ ensure_equals(log,
256
+ "Data: aaabbb\n"
257
+ "Error: " + toString(EIO) + "\n");
258
+ }
259
+
260
+ TEST_METHOD(7) {
261
+ set_test_name("Considers error'ed sockets to be paused");
262
+ startEbi();
263
+ ebi->setReadError(EIO);
264
+ writeExact(p.second, "x");
265
+ EVENTUALLY(5,
266
+ LOCK();
267
+ result = !log.empty();
268
+ );
269
+ LOCK();
270
+ ensure(!ebiIsStarted());
271
+ }
272
+
273
+ DEFINE_ON_DATA_METHOD(on_data_8,
274
+ input->stop();
275
+ self->bg.safe->runLater(boost::bind(&EventedBufferedInputTest::logEbiIsStarted, self));
276
+ return 3;
277
+ )
278
+
279
+ TEST_METHOD(8) {
280
+ set_test_name("If the onData callback consumes everything and pauses the "
281
+ "EventedBufferedInput, then the EventedBufferedInput leaves the socket "
282
+ "in the paused state");
283
+
284
+ ebi->onData = on_data_8;
285
+ startEbi();
286
+ writeExact(p.second, "abc");
287
+ EVENTUALLY(5,
288
+ LOCK();
289
+ result = !log.empty();
290
+ );
291
+ LOCK();
292
+ ensure_equals(log,
293
+ "isStarted: 0\n"
294
+ "isSocketStarted: 0\n");
295
+ }
296
+
297
+ DEFINE_ON_DATA_METHOD(on_data_9,
298
+ input->start();
299
+ self->bg.safe->runLater(boost::bind(&EventedBufferedInputTest::logEbiIsStarted, self));
300
+ return 3;
301
+ )
302
+
303
+ TEST_METHOD(9) {
304
+ set_test_name("if the onData callback consumes everything and resumes the "
305
+ "EventedBufferedInput, then the EventedBufferedInput leaves the socket "
306
+ "in the resumed state");
307
+
308
+ ebi->onData = on_data_9;
309
+ startEbi();
310
+ writeExact(p.second, "abc");
311
+ EVENTUALLY(5,
312
+ LOCK();
313
+ result = !log.empty();
314
+ );
315
+ LOCK();
316
+ ensure_equals(log,
317
+ "isStarted: 1\n"
318
+ "isSocketStarted: 1\n");
319
+ }
320
+
321
+ DEFINE_ON_DATA_METHOD(on_data_10,
322
+ input->stop();
323
+ self->bg.safe->runLater(boost::bind(&EventedBufferedInputTest::logEbiIsStarted, self));
324
+ return 1;
325
+ )
326
+
327
+ TEST_METHOD(10) {
328
+ set_test_name("If the onData callback consumes partially and pauses the "
329
+ "EventedBufferedInput, then the EventedBufferedInput leaves the socket "
330
+ "at the paused state");
331
+
332
+ ebi->onData = on_data_10;
333
+ startEbi();
334
+ writeExact(p.second, "abc");
335
+ EVENTUALLY(5,
336
+ LOCK();
337
+ result = !log.empty();
338
+ );
339
+ LOCK();
340
+ ensure_equals(log,
341
+ "isStarted: 0\n"
342
+ "isSocketStarted: 0\n");
343
+ }
344
+
345
+ DEFINE_ON_DATA_METHOD(on_data_11,
346
+ input->start();
347
+ self->bg.safe->runLater(boost::bind(&EventedBufferedInputTest::logEbiIsStarted, self));
348
+ return 1;
349
+ )
350
+
351
+ TEST_METHOD(11) {
352
+ set_test_name("If the onData callback consumes partially and resumes the "
353
+ "EventedBufferedInput, then the EventedBufferedInput leaves the socket "
354
+ "at the resumed state");
355
+
356
+ ebi->onData = on_data_11;
357
+ startEbi();
358
+ writeExact(p.second, "ab");
359
+ EVENTUALLY(5,
360
+ LOCK();
361
+ result = log ==
362
+ "isStarted: 1\n"
363
+ "isSocketStarted: 0\n"
364
+ "isStarted: 1\n"
365
+ "isSocketStarted: 1\n";
366
+ );
367
+ }
368
+
369
+ DEFINE_ON_DATA_METHOD(on_data_12,
370
+ LOCK();
371
+ self->counter++;
372
+ if (self->counter == 2) {
373
+ input->stop();
374
+ self->bg.safe->runLater(boost::bind(&EventedBufferedInputTest::logEbiIsStarted, self));
375
+ }
376
+ return 2;
377
+ )
378
+
379
+ TEST_METHOD(12) {
380
+ set_test_name("If the onData callback first consumes partially, then "
381
+ "consumes everything and pauses the EventedBufferedInput, then the "
382
+ "EventedBufferedInput leaves the socket in the paused state");
383
+
384
+ ebi->onData = on_data_12;
385
+ startEbi();
386
+ writeExact(p.second, "aabb");
387
+ EVENTUALLY(5,
388
+ LOCK();
389
+ result = !log.empty();
390
+ );
391
+ LOCK();
392
+ ensure_equals(log,
393
+ "isStarted: 0\n"
394
+ "isSocketStarted: 0\n");
395
+ }
396
+
397
+ DEFINE_ON_DATA_METHOD(on_data_13,
398
+ LOCK();
399
+ self->counter++;
400
+ if (self->counter == 2) {
401
+ input->start();
402
+ self->bg.safe->runLater(boost::bind(&EventedBufferedInputTest::logEbiIsStarted, self));
403
+ }
404
+ return 2;
405
+ )
406
+
407
+ TEST_METHOD(13) {
408
+ set_test_name("If the onData callback first consumes partially, then "
409
+ "consumes everything and resumes the EventedBufferedInput, then the "
410
+ "EventedBufferedInput leaves the socket in the resumed state");
411
+
412
+ ebi->onData = on_data_13;
413
+ startEbi();
414
+ writeExact(p.second, "aabb");
415
+ EVENTUALLY(5,
416
+ LOCK();
417
+ result = !log.empty();
418
+ );
419
+ LOCK();
420
+ ensure_equals(log,
421
+ "isStarted: 1\n"
422
+ "isSocketStarted: 1\n");
423
+ }
424
+
425
+
426
+ /*** If the onData callback didn't consume everything... ***/
427
+
428
+ DEFINE_ON_DATA_METHOD(on_data_20,
429
+ LOCK();
430
+ self->counter++;
431
+ self->log.append("onData called; isSocketStarted: " +
432
+ toString(self->ebi->isSocketStarted()) + "\n");
433
+ self->log.append("Data: " + cEscapeString(data) + "\n");
434
+ if (self->counter == 1) {
435
+ return 3;
436
+ } else {
437
+ return 1;
438
+ }
439
+ )
440
+
441
+ static void on_after_processing_buffer_20(EventedBufferedInputTest *self) {
442
+ lock_guard<boost::mutex> l(self->syncher);
443
+ if (self->counter == 1) {
444
+ self->log.append("Finished first onData; isSocketStarted: " +
445
+ toString(self->ebi->isSocketStarted()) + "\n");
446
+ }
447
+ }
448
+
449
+ DEFINE_FINISH_METHOD(finish_20,
450
+ LOCK();
451
+ log.append("Finished; isSocketStarted: " +
452
+ toString(ebi->isSocketStarted()) + "\n");
453
+ );
454
+
455
+ TEST_METHOD(20) {
456
+ set_test_name("It pauses the socket, re-emits the remaining data in the next tick, "
457
+ "then resumes the socket when everything is consumed");
458
+
459
+ ebi->onData = on_data_20;
460
+ ebi->onAfterProcessingBuffer = boost::bind(on_after_processing_buffer_20, this);
461
+ startEbi();
462
+ writeExact(p.second, "aaabbb");
463
+ bg.safe->runAfterTS(10, boost::bind(finish_20, this));
464
+
465
+ EVENTUALLY(5,
466
+ LOCK();
467
+ result = log.find("Finished;") != string::npos;
468
+ );
469
+ LOCK();
470
+ ensure_equals(log,
471
+ "onData called; isSocketStarted: 1\n"
472
+ "Data: aaabbb\n"
473
+ "Finished first onData; isSocketStarted: 0\n"
474
+ "onData called; isSocketStarted: 0\n"
475
+ "Data: bbb\n"
476
+ "onData called; isSocketStarted: 0\n"
477
+ "Data: bb\n"
478
+ "onData called; isSocketStarted: 0\n"
479
+ "Data: b\n"
480
+ "Finished; isSocketStarted: 1\n");
481
+ }
482
+
483
+ /*** If pause() is called after the data handler... ***/
484
+
485
+ static void on_after_processing_buffer_21(EventedBufferedInputTest *self) {
486
+ if (self->getCounter() == 1) {
487
+ self->ebi->stop();
488
+ lock_guard<boost::mutex> l(self->syncher);
489
+ self->log.append("isSocketStarted: " +
490
+ toString(self->ebi->isSocketStarted()) + "\n");
491
+ }
492
+ }
493
+
494
+ TEST_METHOD(21) {
495
+ set_test_name("It pauses the socket and doesn't re-emit remaining data events");
496
+ toConsume = 1;
497
+ ebi->onAfterProcessingBuffer = boost::bind(on_after_processing_buffer_21, this);
498
+ startEbi();
499
+ writeExact(p.second, "aaabbb");
500
+ EVENTUALLY(5,
501
+ LOCK();
502
+ result = log.find("isSocketStarted") != string::npos;
503
+ );
504
+ LOCK();
505
+ ensure_equals(log,
506
+ "Data: aaabbb\n"
507
+ "isSocketStarted: 0\n");
508
+ }
509
+
510
+ static void on_after_processing_buffer_22(EventedBufferedInputTest *self) {
511
+ if (self->getCounter() == 1) {
512
+ self->ebi->stop();
513
+ {
514
+ lock_guard<boost::mutex> l(self->syncher);
515
+ self->log.append("Paused; isSocketStarted: " +
516
+ toString(self->ebi->isSocketStarted()) + "\n");
517
+ }
518
+ self->ebi->start();
519
+ {
520
+ lock_guard<boost::mutex> l(self->syncher);
521
+ self->log.append("Resumed; isSocketStarted: " +
522
+ toString(self->ebi->isSocketStarted()) + "\n");
523
+ }
524
+ }
525
+ }
526
+
527
+ DEFINE_FINISH_METHOD(finish_22,
528
+ LOCK();
529
+ log.append("Done; isSocketStarted: " +
530
+ toString(ebi->isStarted()) + "\n");
531
+ );
532
+
533
+ TEST_METHOD(22) {
534
+ set_test_name("It resumes the socket and re-emits remaining "
535
+ "data one tick after start() is called");
536
+ toConsume = 3;
537
+ ebi->onAfterProcessingBuffer = boost::bind(on_after_processing_buffer_22, this);
538
+ startEbi();
539
+ writeExact(p.second, "aaabbb");
540
+ bg.safe->runAfterTS(10, boost::bind(finish_22, this));
541
+ EVENTUALLY(5,
542
+ LOCK();
543
+ result = log.find("Done") != string::npos;
544
+ );
545
+ {
546
+ LOCK();
547
+ ensure_equals(log,
548
+ "Data: aaabbb\n"
549
+ "Paused; isSocketStarted: 0\n"
550
+ "Resumed; isSocketStarted: 0\n"
551
+ "Data: bbb\n"
552
+ "Done; isSocketStarted: 1\n");
553
+ }
554
+
555
+ bg.safe->runAfterTS(10, boost::bind(finish_22, this));
556
+ startEbi();
557
+ EVENTUALLY(5,
558
+ LOCK();
559
+ result = log.find("Done") != string::npos;
560
+ );
561
+ }
562
+
563
+ static void on_after_processing_buffer_23(EventedBufferedInputTest *self) {
564
+ if (self->getCounter() == 1) {
565
+ self->ebi->stop();
566
+ {
567
+ lock_guard<boost::mutex> l(self->syncher);
568
+ self->log.append("Paused; isSocketStarted: " +
569
+ toString(self->ebi->isSocketStarted()) + "\n");
570
+ }
571
+ self->ebi->start();
572
+ {
573
+ lock_guard<boost::mutex> l(self->syncher);
574
+ self->log.append("Resumed; isSocketStarted: " +
575
+ toString(self->ebi->isSocketStarted()) + "\n");
576
+ }
577
+ self->ebi->stop();
578
+ {
579
+ lock_guard<boost::mutex> l(self->syncher);
580
+ self->log.append("Paused again; isSocketStarted: " +
581
+ toString(self->ebi->isSocketStarted()) + "\n");
582
+ }
583
+ }
584
+ }
585
+
586
+ DEFINE_FINISH_METHOD(finish_23,
587
+ LOCK();
588
+ log.append("Timeout; isSocketStarted: " +
589
+ toString(ebi->isStarted()) + "\n");
590
+ );
591
+
592
+ TEST_METHOD(23) {
593
+ set_test_name("It doesn't re-emit remaining data if start() "
594
+ "is called, then stop() again");
595
+ toConsume = 3;
596
+ ebi->onAfterProcessingBuffer = boost::bind(on_after_processing_buffer_23, this);
597
+ startEbi();
598
+ writeExact(p.second, "aaabbb");
599
+ bg.safe->runAfterTS(10, boost::bind(finish_23, this));
600
+ EVENTUALLY(5,
601
+ LOCK();
602
+ result = log.find("Timeout") != string::npos;
603
+ );
604
+ LOCK();
605
+ ensure_equals(log,
606
+ "Data: aaabbb\n"
607
+ "Paused; isSocketStarted: 0\n"
608
+ "Resumed; isSocketStarted: 0\n"
609
+ "Paused again; isSocketStarted: 0\n"
610
+ "Timeout; isSocketStarted: 0\n");
611
+ }
612
+
613
+ /*** If pause() is called during the handler ***/
614
+
615
+ DEFINE_ON_DATA_METHOD(on_data_24,
616
+ {
617
+ LOCK();
618
+ self->counter++;
619
+ self->log.append("Data: " + cEscapeString(data) + "\n");
620
+ }
621
+ if (self->getCounter() == 1) {
622
+ input->stop();
623
+ }
624
+ return 1;
625
+ )
626
+
627
+ DEFINE_FINISH_METHOD(finish_24,
628
+ LOCK();
629
+ log.append("Timeout; isSocketStarted: " +
630
+ toString(ebi->isSocketStarted()) + "\n");
631
+ );
632
+
633
+ TEST_METHOD(24) {
634
+ set_test_name("It pauses the socket and doesn't re-emit remaining data");
635
+ ebi->onData = on_data_24;
636
+ startEbi();
637
+ writeExact(p.second, "aaabbb");
638
+ bg.safe->runAfterTS(10, boost::bind(finish_24, this));
639
+ EVENTUALLY(5,
640
+ LOCK();
641
+ result = log.find("Timeout") != string::npos;
642
+ );
643
+ LOCK();
644
+ ensure_equals(log,
645
+ "Data: aaabbb\n"
646
+ "Timeout; isSocketStarted: 0\n");
647
+ }
648
+
649
+ DEFINE_ON_DATA_METHOD(on_data_25,
650
+ {
651
+ LOCK();
652
+ self->counter++;
653
+ self->log.append("Data: " + cEscapeString(data) + "\n");
654
+ }
655
+ if (self->getCounter() == 1) {
656
+ input->stop();
657
+ input->start();
658
+ }
659
+ return 3;
660
+ )
661
+
662
+ static void on_after_processing_buffer_25(EventedBufferedInputTest *self) {
663
+ lock_guard<boost::mutex> l(self->syncher);
664
+ if (self->counter == 1) {
665
+ self->log.append("Handler done; isSocketStarted: " +
666
+ toString(self->ebi->isSocketStarted()) + "\n");
667
+ }
668
+ }
669
+
670
+ DEFINE_FINISH_METHOD(finish_25,
671
+ LOCK();
672
+ log.append("Timeout; isSocketStarted: " +
673
+ toString(ebi->isStarted()) + "\n");
674
+ );
675
+
676
+ TEST_METHOD(25) {
677
+ set_test_name("It re-emits remaining data one tick after start() is called");
678
+ ebi->onData = on_data_25;
679
+ ebi->onAfterProcessingBuffer = boost::bind(on_after_processing_buffer_25, this);
680
+ startEbi();
681
+ writeExact(p.second, "aaabbb");
682
+ bg.safe->runAfterTS(10, boost::bind(finish_25, this));
683
+ EVENTUALLY(5,
684
+ LOCK();
685
+ result = log.find("Timeout") != string::npos;
686
+ );
687
+ LOCK();
688
+ ensure_equals(log,
689
+ "Data: aaabbb\n"
690
+ "Handler done; isSocketStarted: 0\n"
691
+ "Data: bbb\n"
692
+ "Timeout; isSocketStarted: 1\n");
693
+ }
694
+
695
+ DEFINE_ON_DATA_METHOD(on_data_26,
696
+ {
697
+ LOCK();
698
+ self->counter++;
699
+ self->log.append("Data: " + cEscapeString(data) + "\n");
700
+ }
701
+ if (self->getCounter() == 1) {
702
+ input->stop();
703
+ input->start();
704
+ input->stop();
705
+ }
706
+ return 3;
707
+ )
708
+
709
+ static void on_after_processing_buffer_26(EventedBufferedInputTest *self) {
710
+ lock_guard<boost::mutex> l(self->syncher);
711
+ if (self->counter == 1) {
712
+ self->log.append("Handler done; isSocketStarted: " +
713
+ toString(self->ebi->isSocketStarted()) + "\n");
714
+ }
715
+ }
716
+
717
+ DEFINE_FINISH_METHOD(finish_26,
718
+ LOCK();
719
+ log.append("Timeout; isSocketStarted: " +
720
+ toString(ebi->isSocketStarted()) + "\n");
721
+ );
722
+
723
+ TEST_METHOD(26) {
724
+ set_test_name("It doesn't re-emit remaining data if start() is called, then stop() again");
725
+ ebi->onData = on_data_26;
726
+ ebi->onAfterProcessingBuffer = boost::bind(on_after_processing_buffer_26, this);
727
+ startEbi();
728
+ writeExact(p.second, "aaabbb");
729
+ bg.safe->runAfterTS(10, boost::bind(finish_26, this));
730
+ EVENTUALLY(5,
731
+ LOCK();
732
+ result = log.find("Timeout") != string::npos;
733
+ );
734
+ LOCK();
735
+ ensure_equals(log,
736
+ "Data: aaabbb\n"
737
+ "Handler done; isSocketStarted: 0\n"
738
+ "Timeout; isSocketStarted: 0\n");
739
+ }
740
+
741
+ /*** If the socket was disconnected ***/
742
+
743
+ TEST_METHOD(27) {
744
+ set_test_name("It doesn't re-emit the remaining data");
745
+ // TODO
746
+ }
747
+
748
+ TEST_METHOD(30) {
749
+ set_test_name("It pauses the underlying socket");
750
+ // TODO
751
+ }
752
+
753
+ TEST_METHOD(31) {
754
+ set_test_name("It doesn't emit data events if it's paused, but re-emits "
755
+ "previously unemitted data events after resume");
756
+ // TODO
757
+ }
758
+ }