~ubuntu-branches/ubuntu/natty/nginx/natty-updates

« back to all changes in this revision

Viewing changes to debian/patches/nginx-echo.diff

  • Committer: Bazaar Package Importer
  • Author(s): Michael Lustfield, Micheal Lustfield, Kartik Mistry
  • Date: 2011-03-03 23:39:07 UTC
  • mfrom: (4.2.29 sid)
  • Revision ID: james.westby@ubuntu.com-20110303233907-y48yifhfnn5qjuxz
Tags: 0.8.54-4
[Micheal Lustfield]
* debian/nginx-{full,light,extras}.default:
  + Added comment about alternative to ULIMIT.
* debian/nginx-{full,light,extras}.init.d:
  + Added quotes around a test variable. (Closes: #610946, LP: #699736)
* debian/patches/609343-log-time-iso8601.diff:
  + Added patch to add $time_iso8601 variable to logs. (Closes: #609343)
* Clean up old logrotate files. (Closes: #608983, Closes: #610289)
  + Added Files:
    - debian/nginx-common.preinst
  + Modified Files:
    - debian/rules
  + Moved debian/nginx-common.logrotate to debian/logrotate.
* Added common files to nginx-common package. (Closes: #610290)
  + Removed Files:
    - debian/nginx-full.dirs
    - debian/nginx-light.dirs
    - debian/nginx-full.install
    - debian/nginx-light.install
    - debian/nginx-extras.install
    - debian/nginx.*
  + Added Files:
    - debian/nginx-common.default
    - debian/nginx-common.dirs
    - debian/nginx-common.init.d
    - debian/nginx-common.install
    - debian/nginx-common.manpages
    - debian/logrotate
  + Modified Files:
    - debian/nginx-extras.dirs
    - debian/control
    - debian/rules
* debian/nginx-*.install: (Closes: #609797)
  + Removed NEWS.Debian from nginx-{full,light,extras}.install.
  + Added NEWS.Debian to nginx-common.install.
* nginx-common.postinst:
  + Enforce /var/log/nginx mode and user:group. (Closes: #610983)
  + Enforce /var/log/nginx/*.log mode and user:group. (Closes: #612832)
* debian/rules:
  + Added --with-file-aio to nginx-extras. (Closes: #613175)
  + Removed split clients and user id modules from nginx-light.
* debian/conf/sites-available/default:
  + Fixed a minor typo ( s/Quickstart/QuickStart/ ). (Closes: #613355)
* debian/conf/mime.types:
  + Changed xml type to application/xhtml+xml. (Closes: #613851)
* debian/help/docs/fcgiwrap:
  + Removed Ubuntu specific line in docs. (Closes: #614987)
* debian/conf/sites-available/default:
  + Fixed a pointer to a file. (Closes: #614980)

[Kartik Mistry]
* debian/*.lintian-overrides:
  + Add Lintian overrides for nginx man page. We've manpage in nginx-common
    binary

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
Description: Patch for nginx echo module
2
 
Author: Michael Lustfield <mtecknology@ubuntu.com>
3
 
 
4
 
Index: 0.8/modules/nginx-echo/LICENSE
5
 
===================================================================
6
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
7
 
+++ 0.8/modules/nginx-echo/LICENSE      2010-10-31 07:35:23.648338001 +0000
8
 
@@ -0,0 +1,30 @@
9
 
+Copyright (c) 2009, Taobao Inc., Alibaba Group ( http://www.taobao.com ).
10
 
+Copyright (c) 2009, agentzh <agentzh@gmail.com>.
11
 
+All rights reserved.
12
 
+
13
 
+Redistribution and use in source and binary forms, with or without
14
 
+modification, are permitted provided that the following conditions
15
 
+are met:
16
 
+
17
 
+    * Redistributions of source code must retain the above copyright
18
 
+    notice, this list of conditions and the following disclaimer.
19
 
+
20
 
+    * Redistributions in binary form must reproduce the above copyright
21
 
+    notice, this list of conditions and the following disclaimer in the
22
 
+    documentation and/or other materials provided with the distribution.
23
 
+
24
 
+    * Neither the name of the Taobao Inc. nor the names of its
25
 
+    contributors may be used to endorse or promote products derived from
26
 
+    this software without specific prior written permission.
27
 
+
28
 
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29
 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31
 
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32
 
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33
 
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
34
 
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
35
 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
36
 
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37
 
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38
 
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
 
Index: 0.8/modules/nginx-echo/README
40
 
===================================================================
41
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
42
 
+++ 0.8/modules/nginx-echo/README       2010-10-31 07:35:47.698338000 +0000
43
 
@@ -0,0 +1,1740 @@
44
 
+Name
45
 
+    ngx_echo - Brings "echo", "sleep", "time", "exec" and more shell-style
46
 
+    goodies to Nginx config file.
47
 
+
48
 
+    *This module is not distributed with the Nginx source.* See the
49
 
+    installation instructions.
50
 
+
51
 
+Version
52
 
+    This document describes echo-nginx-module v0.34
53
 
+    (<http://github.com/agentzh/echo-nginx-module/tarball/v0.34 >) released
54
 
+    on September 14, 2010.
55
 
+
56
 
+Synopsis
57
 
+      location /hello {
58
 
+        echo "hello, world!";
59
 
+      }
60
 
+
61
 
+      location /hello {
62
 
+        echo -n "hello, "
63
 
+        echo "world!";
64
 
+      }
65
 
+
66
 
+      location /timed_hello {
67
 
+        echo_reset_timer;
68
 
+        echo hello world;
69
 
+        echo "'hello world' takes about $echo_timer_elapsed sec.";
70
 
+        echo hiya igor;
71
 
+        echo "'hiya igor' takes about $echo_timer_elapsed sec.";
72
 
+      }
73
 
+
74
 
+      location /echo_with_sleep {
75
 
+        echo hello;
76
 
+        echo_flush;  # ensure the client can see previous output immediately
77
 
+        echo_sleep   2.5;  # in sec
78
 
+        echo world;
79
 
+      }
80
 
+
81
 
+      # in the following example, accessing /echo yields
82
 
+      #   hello
83
 
+      #   world
84
 
+      #   blah
85
 
+      #   hiya
86
 
+      #   igor
87
 
+      location /echo {
88
 
+          echo_before_body hello;
89
 
+          echo_before_body world;
90
 
+          proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more;
91
 
+          echo_after_body hiya;
92
 
+          echo_after_body igor;
93
 
+      }
94
 
+      location /echo/more {
95
 
+          echo blah;
96
 
+      }
97
 
+
98
 
+      # the output of /main might be
99
 
+      #   hello
100
 
+      #   world
101
 
+      #   took 0.000 sec for total.
102
 
+      # and the whole request would take about 2 sec to complete.
103
 
+      location /main {
104
 
+          echo_reset_timer;
105
 
+
106
 
+          # subrequests in parallel
107
 
+          echo_location_async /sub1;
108
 
+          echo_location_async /sub2;
109
 
+
110
 
+          echo "took $echo_timer_elapsed sec for total.";
111
 
+      }
112
 
+      location /sub1 {
113
 
+          echo_sleep 2;
114
 
+          echo hello;
115
 
+      }
116
 
+      location /sub2 {
117
 
+          echo_sleep 1;
118
 
+          echo world;
119
 
+      }
120
 
+
121
 
+      # the output of /main might be
122
 
+      #   hello
123
 
+      #   world
124
 
+      #   took 3.003 sec for total.
125
 
+      # and the whole request would take about 3 sec to complete.
126
 
+      location /main {
127
 
+          echo_reset_timer;
128
 
+
129
 
+          # subrequests in series (chained by CPS)
130
 
+          echo_location /sub1;
131
 
+          echo_location /sub2;
132
 
+
133
 
+          echo "took $echo_timer_elapsed sec for total.";
134
 
+      }
135
 
+      location /sub1 {
136
 
+          echo_sleep 2;
137
 
+          echo hello;
138
 
+      }
139
 
+      location /sub2 {
140
 
+          echo_sleep 1;
141
 
+          echo world;
142
 
+      }
143
 
+
144
 
+      # Accessing /dup gives
145
 
+      #   ------ END ------
146
 
+      location /dup {
147
 
+        echo_duplicate 3 "--";
148
 
+        echo_duplicate 1 " END ";
149
 
+        echo_duplicate 3 "--";
150
 
+        echo;
151
 
+      }
152
 
+
153
 
+      # /bighello will generate 1000,000,000 hello's.
154
 
+      location /bighello {
155
 
+        echo_duplicate 1000_000_000 'hello';
156
 
+      }
157
 
+
158
 
+      # echo back the client request
159
 
+      location /echoback {
160
 
+        echo_duplicate 1 $echo_client_request_headers;
161
 
+        echo "\r";
162
 
+
163
 
+        echo_read_request_body;
164
 
+
165
 
+        echo_request_body;
166
 
+      }
167
 
+
168
 
+      # GET /multi will yields
169
 
+      #   querystring: foo=Foo
170
 
+      #   method: POST
171
 
+      #   body: hi
172
 
+      #   content length: 2
173
 
+      #   ///
174
 
+      #   querystring: bar=Bar
175
 
+      #   method: PUT
176
 
+      #   body: hello
177
 
+      #   content length: 5
178
 
+      #   ///
179
 
+      location /multi {
180
 
+          echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi';
181
 
+          echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello';
182
 
+      }
183
 
+      location /sub {
184
 
+          echo "querystring: $query_string";
185
 
+          echo "method: $echo_request_method";
186
 
+          echo "body: $echo_request_body";
187
 
+          echo "content length: $http_content_length";
188
 
+          echo '///';
189
 
+      }
190
 
+
191
 
+      # GET /merge?/foo.js&/bar/blah.js&/yui/baz.js will merge the .js resources together
192
 
+      location /merge {
193
 
+          default_type 'text/javascript';
194
 
+          echo_foreach_split '&' $query_string;
195
 
+              echo "/* JS File $echo_it */";
196
 
+              echo_location_async $echo_it;
197
 
+              echo;
198
 
+          echo_end;
199
 
+      }
200
 
+
201
 
+      # accessing /if?val=abc yields the "hit" output
202
 
+      # while /if?val=bcd yields "miss":
203
 
+      location ^~ /if {
204
 
+          set $res miss;
205
 
+          if ($arg_val ~* '^a') {
206
 
+              set $res hit;
207
 
+              echo $res;
208
 
+          }
209
 
+          echo $res;
210
 
+      }
211
 
+
212
 
+Description
213
 
+    This module wraps lots of Nginx internal APIs for streaming input and
214
 
+    output, parallel/sequential subrequests, timers and sleeping, as well as
215
 
+    various meta data accessing.
216
 
+
217
 
+    Basically it provides various utilities that help testing and debugging
218
 
+    of other modules by trivially emulating different kinds of faked
219
 
+    subrequest locations.
220
 
+
221
 
+    People will also find it useful in real-world applications that need to
222
 
+
223
 
+    1.  serve static contents directly from memory (loading from the Nginx
224
 
+        config file).
225
 
+
226
 
+    2.  wrap the upstream response with custom header and footer (kinda like
227
 
+        the addition module but with contents read directly from the config
228
 
+        file and Nginx variables).
229
 
+
230
 
+    3.  merge contents of various "Nginx locations" (i.e., subrequests)
231
 
+        together in a single main request (using echo_location and its
232
 
+        friends).
233
 
+
234
 
+    This is a special dual-role module that can *lazily* serve as a content
235
 
+    handler or register itself as an output filter only upon demand. By
236
 
+    default, this module does not do anything at all.
237
 
+
238
 
+    Use of any of this module's directives (no matter content handler
239
 
+    directives or filter directives) will force the chunked encoding to be
240
 
+    used for the HTTP response due to the streaming nature of this module
241
 
+    (unless HTTP 1.0 is enforced by the client and the Content-Length header
242
 
+    will be set to the size of the first handler directive that generates
243
 
+    contents).
244
 
+
245
 
+    Technially, this module has also demonstrated the following techniques
246
 
+    that might be helpful for module writers:
247
 
+
248
 
+    1.  Issue parallel subreqeusts directly from content handler.
249
 
+
250
 
+    2.  Issue chained subrequests directly from content handler, by passing
251
 
+        continuation along the subrequest chain.
252
 
+
253
 
+    3.  Issue subrequests with all HTTP 1.1 methods and even an optional
254
 
+        faked HTTP request body.
255
 
+
256
 
+    4.  Interact with the Nginx event model directly from content handler
257
 
+        using custom events and timers, and resume the content handler back
258
 
+        if necessary.
259
 
+
260
 
+    5.  Dual-role module that can (lazily) serve as a content handler or an
261
 
+        output filter or both.
262
 
+
263
 
+    6.  Nginx config file variable creation and interpolation.
264
 
+
265
 
+    7.  Streaming output control using output_chain, flush and its friends.
266
 
+
267
 
+    8.  Read client request body from the content handler, and returns back
268
 
+        (asynchronously) to the content handler after completion.
269
 
+
270
 
+    9.  Use Perl-based declarative test suite to drive the development of
271
 
+        Nginx C modules.
272
 
+
273
 
+Content Handler Directives
274
 
+    Use of the following directives register this module to the current
275
 
+    Nginx location as a content handler. If you want to use another module,
276
 
+    like the standard proxy module, as the content handler, use the filter
277
 
+    directives provided by this module.
278
 
+
279
 
+    All the content handler directives can be mixed together in a single
280
 
+    Nginx location and they're supposed to run sequentially just as in the
281
 
+    Bash scripting language.
282
 
+
283
 
+    Every content handler directive supports variable interpolation in its
284
 
+    arguments (if any).
285
 
+
286
 
+    The MIME type set by the standard default_type directive is respected by
287
 
+    this module, as in:
288
 
+
289
 
+      location /hello {
290
 
+        default_type text/plain;
291
 
+        echo hello;
292
 
+      }
293
 
+
294
 
+    Then on the client side:
295
 
+
296
 
+      $ curl -I 'http://localhost/echo'
297
 
+      HTTP/1.1 200 OK
298
 
+      Server: nginx/0.8.20
299
 
+      Date: Sat, 17 Oct 2009 03:40:19 GMT
300
 
+      Content-Type: text/plain
301
 
+      Connection: keep-alive
302
 
+
303
 
+    Since the v0.22 release, all of the directives are allowed in the
304
 
+    rewrite module's if directive block, for instance:
305
 
+
306
 
+        location ^~ /if {
307
 
+            set $res miss;
308
 
+            if ($arg_val ~* '^a') {
309
 
+                set $res hit;
310
 
+                echo $res;
311
 
+            }
312
 
+            echo $res;
313
 
+        }
314
 
+
315
 
+  echo
316
 
+    syntax: *echo [options] <string>...*
317
 
+
318
 
+    default: *no*
319
 
+
320
 
+    context: *location*
321
 
+
322
 
+    Sends arguments joined by spaces, along with a trailing newline, out to
323
 
+    the client.
324
 
+
325
 
+    Note that the data might be buffered by Nginx's underlying buffer. To
326
 
+    force the output data flushed immediately, use the echo_flush command
327
 
+    just after "echo", as in
328
 
+
329
 
+       echo hello world;
330
 
+       echo_flush;
331
 
+
332
 
+    When no argument is specified, *echo* emits the trailing newline alone,
333
 
+    just like the *echo* command in shell.
334
 
+
335
 
+    Variables may appear in the arguments. An example is
336
 
+
337
 
+       echo The current request uri is $request_uri;
338
 
+
339
 
+    where $request_uri is a variable exposed by the [[NginxHttpCoreModule]].
340
 
+
341
 
+    This command can be used multiple times in a single location
342
 
+    configuration, as in
343
 
+
344
 
+        location /echo {
345
 
+            echo hello;
346
 
+            echo world;
347
 
+        }
348
 
+
349
 
+    The output on the client side looks like this
350
 
+
351
 
+        $ curl 'http://localhost/echo'
352
 
+        hello
353
 
+        world
354
 
+
355
 
+    Special characters like newlines ("\n") and tabs ("\t") can be escaped
356
 
+    using C-style escaping sequences. But a notable exception is the dollar
357
 
+    sign ("$"). As of Nginx 0.8.20, there's still no clean way to esacpe
358
 
+    this characters. (A work-around might be to use a $echo_dollor variable
359
 
+    that is always evaluated to the constant "$" character. This feature
360
 
+    will possibly be introduced in a future version of this module.)
361
 
+
362
 
+    As of the echo v0.28 release, one can suppress the trailing newline
363
 
+    character in the output by using the "-n" option, as in
364
 
+
365
 
+        location /echo {
366
 
+            echo -n "hello, ";
367
 
+            echo "world";
368
 
+        }
369
 
+
370
 
+    Accessing "/echo" gives
371
 
+
372
 
+        $ curl 'http://localhost/echo'
373
 
+        hello, world
374
 
+
375
 
+    Leading "-n" in variable values won't take effect and will be emitted
376
 
+    literally, as in
377
 
+
378
 
+        location /echo {
379
 
+            set $opt -n;
380
 
+            echo $opt "hello,";
381
 
+            echo "world";
382
 
+        }
383
 
+
384
 
+    This gives the following output
385
 
+
386
 
+        $ curl 'http://localhost/echo'
387
 
+        -n hello,
388
 
+        world
389
 
+
390
 
+    One can output leading "-n" literals and other options using the special
391
 
+    "--" option like this
392
 
+
393
 
+        location /echo {
394
 
+            echo -- -n is an option;
395
 
+        }
396
 
+
397
 
+    which yields
398
 
+
399
 
+        $ curl 'http://localhost/echo'
400
 
+        -n is an option
401
 
+
402
 
+  echo_duplicate
403
 
+    syntax: *echo_duplicate <count> <string>*
404
 
+
405
 
+    default: *no*
406
 
+
407
 
+    context: *location*
408
 
+
409
 
+    Outputs duplication of a string indicated by the second argument, using
410
 
+    the times specified in the first argument.
411
 
+
412
 
+    For instance,
413
 
+
414
 
+      location /dup {
415
 
+          echo_duplicate 3 "abc";
416
 
+      }
417
 
+
418
 
+    will lead to an output of "abcabcabc".
419
 
+
420
 
+    Underscores are allowed in the count number, just like in Perl. For
421
 
+    example, to emit 1000,000,000 instances of "hello, world":
422
 
+
423
 
+      location /many_hellos {
424
 
+          echo_duplicate 1000_000_000 "hello, world";
425
 
+      }
426
 
+
427
 
+    The "count" argument could be zero, but not negative. The second
428
 
+    "string" argument could be an empty string ("") likewise.
429
 
+
430
 
+    Unlike the echo directive, no trailing newline is appended to the
431
 
+    result. So it's possible to "abuse" this directive as a
432
 
+    no-trailing-newline version of echo by using "count" 1, as in
433
 
+
434
 
+      location /echo_art {
435
 
+          echo_duplicate 2 '---';
436
 
+          echo_duplicate 1 ' END ';  # we don't want a trailing newline here
437
 
+          echo_duplicate 2 '---';
438
 
+          echo;  # we want a trailing newline here...
439
 
+      }
440
 
+
441
 
+    You get
442
 
+
443
 
+      ------ END ------
444
 
+
445
 
+    This directive was first introduced in version 0.11.
446
 
+
447
 
+  echo_flush
448
 
+    syntax: *echo_flush*
449
 
+
450
 
+    default: *no*
451
 
+
452
 
+    context: *location*
453
 
+
454
 
+    Forces the data potentially buffered by underlying Nginx output filters
455
 
+    to send immediately to the client side via socket.
456
 
+
457
 
+    Note that techically the command just emits a ngx_buf_t object with
458
 
+    "flush" slot set to 1, so certain weird third-party output filter module
459
 
+    could still block it before it reaches Nginx's (last) write filter.
460
 
+
461
 
+    This directive does not take any argument.
462
 
+
463
 
+    Consider the following example:
464
 
+
465
 
+      location /flush {
466
 
+         echo hello;
467
 
+
468
 
+         echo_flush;
469
 
+
470
 
+         echo_sleep 1;
471
 
+         echo world;
472
 
+      }
473
 
+
474
 
+    Then on the client side, using curl to access "/flush", you'll see the
475
 
+    "hello" line immediately, but only after 1 second, the last "world"
476
 
+    line. Without calling "echo_flush" in the example above, you'll most
477
 
+    likely see no output until 1 second is elapsed due to the internal
478
 
+    buffering of Nginx.
479
 
+
480
 
+    This directive will fail to flush the output buffer in case of
481
 
+    subrequests get involved. Consider the following example:
482
 
+
483
 
+      location /main {
484
 
+          echo_location_async /sub;
485
 
+          echo hello;
486
 
+          echo_flush;
487
 
+      }
488
 
+      location /sub {
489
 
+          echo_sleep 1;
490
 
+      }
491
 
+
492
 
+    Then the client won't see "hello" appear even if "echo_flush" has been
493
 
+    executed before the subrequest to "/sub" has actually started executing.
494
 
+    The outputs of "/main" that are sent *after* echo_location_async will be
495
 
+    postponed and buffered firmly.
496
 
+
497
 
+    This does *not* apply to outputs sent before the subrequest initiated.
498
 
+    For a modified version of the example given above:
499
 
+
500
 
+      location /main {
501
 
+          echo hello;
502
 
+          echo_flush;
503
 
+          echo_location_async /sub;
504
 
+      }
505
 
+      location /sub {
506
 
+          echo_sleep 1;
507
 
+      }
508
 
+
509
 
+    The client will immediately see "hello" before "/sub" enters sleeping.
510
 
+
511
 
+    See also echo, echo_sleep, and echo_location_async.
512
 
+
513
 
+  echo_sleep
514
 
+    syntax: *echo_sleep <seconds>*
515
 
+
516
 
+    default: *no*
517
 
+
518
 
+    context: *location*
519
 
+
520
 
+    Sleeps for the time period specified by the argument, which is in
521
 
+    seconds.
522
 
+
523
 
+    This operation is non-blocking on server side, so unlike the
524
 
+    echo_blocking_sleep directive, it won't block the whole Nginx worker
525
 
+    process.
526
 
+
527
 
+    The period might takes three digits after the decimal point and must be
528
 
+    greater than 0.001.
529
 
+
530
 
+    An example is
531
 
+
532
 
+       location /echo_after_sleep {
533
 
+           echo_sleep 1.234;
534
 
+           echo resumed!;
535
 
+       }
536
 
+
537
 
+    Behind the scene, it sets up a per-request "sleep" ngx_event_t object,
538
 
+    and adds a timer using that custom event to the Nginx event model and
539
 
+    just waits for a timeout on that event. Because the "sleep" event is
540
 
+    per-request, this directive can work in parallel subrequests.
541
 
+
542
 
+  echo_blocking_sleep
543
 
+    syntax: *echo_blocking_sleep <seconds>*
544
 
+
545
 
+    default: *no*
546
 
+
547
 
+    context: *location*
548
 
+
549
 
+    This is a blocking version of the echo_sleep directive.
550
 
+
551
 
+    See the documentation of echo_sleep for more detail.
552
 
+
553
 
+    Behind the curtain, it calls the ngx_msleep macro provided by the Nginx
554
 
+    core which maps to usleep on POSIX-compliant systems.
555
 
+
556
 
+    Note that this directive will block the current Nginx worker process
557
 
+    completely while being executed, so never use it in production
558
 
+    environment.
559
 
+
560
 
+  echo_reset_timer
561
 
+    syntax: *echo_reset_timer*
562
 
+
563
 
+    default: *no*
564
 
+
565
 
+    context: *location*
566
 
+
567
 
+    Reset the timer begin time to *now*, i.e., the time when this command is
568
 
+    executed during request.
569
 
+
570
 
+    The timer begin time is default to the starting time of the current
571
 
+    request and can be overridden by this directive, potentially multiple
572
 
+    times in a single location. For example:
573
 
+
574
 
+      location /timed_sleep {
575
 
+          echo_sleep 0.03;
576
 
+          echo "$echo_timer_elapsed sec elapsed.";
577
 
+
578
 
+          echo_reset_timer;
579
 
+
580
 
+          echo_sleep 0.02;
581
 
+          echo "$echo_timer_elapsed sec elapsed.";
582
 
+      }
583
 
+
584
 
+    The output on the client side might be
585
 
+
586
 
+        $ curl 'http://localhost/timed_sleep'
587
 
+        0.032 sec elapsed.
588
 
+        0.020 sec elapsed.
589
 
+
590
 
+    The actual figures you get on your side may vary a bit due to your
591
 
+    system's current activities.
592
 
+
593
 
+    Invocation of this directive will force the underlying Nginx timer to
594
 
+    get updated to the current system time (regardless the timer resolution
595
 
+    specified elsewhere in the config file). Furthermore, references of the
596
 
+    $echo_timer_elapsed variable will also trigger timer update forcibly.
597
 
+
598
 
+    See also echo_sleep and $echo_timer_elapsed.
599
 
+
600
 
+  echo_read_request_body
601
 
+    Explicitly reads request body so that the $request_body variable will
602
 
+    always have non-empty values (unless the body is so big that it has been
603
 
+    saved by Nginx to a local temporary file).
604
 
+
605
 
+    Note that this might not be the original client request body because the
606
 
+    current request might be a subrequest with a "artificial" body specified
607
 
+    by its parent.
608
 
+
609
 
+    This directive does not generate any output itself, just like
610
 
+    echo_sleep.
611
 
+
612
 
+    Here's an example for echo'ing back the original HTTP client request
613
 
+    (both headers and body are included):
614
 
+
615
 
+      location /echoback {
616
 
+        echo_duplicate 1 $echo_client_request_headers;
617
 
+        echo "\r";
618
 
+        echo_read_request_body;
619
 
+        echo $request_body;
620
 
+      }
621
 
+
622
 
+    The content of "/echoback" looks like this on my side (I was using
623
 
+    Perl's LWP utility to access this location on the server):
624
 
+
625
 
+      $ (echo hello; echo world) | lwp-request -m POST 'http://localhost/echoback'
626
 
+      POST /echoback HTTP/1.1
627
 
+      TE: deflate,gzip;q=0.3
628
 
+      Connection: TE, close
629
 
+      Host: localhost
630
 
+      User-Agent: lwp-request/5.818 libwww-perl/5.820
631
 
+      Content-Length: 12
632
 
+      Content-Type: application/x-www-form-urlencoded
633
 
+
634
 
+      hello
635
 
+      world
636
 
+
637
 
+    Because "/echoback" is the main request, $request_body holds the
638
 
+    original client request body.
639
 
+
640
 
+    Before Nginx 0.7.56, it makes no sense to use this directive because
641
 
+    $request_body was first introduced in Nginx 0.7.58.
642
 
+
643
 
+    This directive itself was first introduced in the echo module's v0.14
644
 
+    release.
645
 
+
646
 
+  echo_location_async
647
 
+    syntax: *echo_location_async <location> [<url_args>]*
648
 
+
649
 
+    default: *no*
650
 
+
651
 
+    context: *location*
652
 
+
653
 
+    Issue GET subrequest to the location specified (first argument) with
654
 
+    optional url arguments specified in the second argument.
655
 
+
656
 
+    As of Nginx 0.8.20, the "location" argument does *not* support named
657
 
+    location, due to a limitation in the "ngx_http_subrequest" function. The
658
 
+    same is true for its brother, the echo_location directive.
659
 
+
660
 
+    A very simple example is
661
 
+
662
 
+        location /main {
663
 
+            echo_location_async /sub;
664
 
+            echo world;
665
 
+        }
666
 
+        location /sub {
667
 
+            echo hello;
668
 
+        }
669
 
+
670
 
+    Accessing "/main" gets
671
 
+
672
 
+      hello
673
 
+      world
674
 
+
675
 
+    Calling multiple locations in parallel is also possible:
676
 
+
677
 
+        location /main {
678
 
+            echo_reset_timer;
679
 
+            echo_location_async /sub1;
680
 
+            echo_location_async /sub2;
681
 
+            echo "took $echo_timer_elapsed sec for total.";
682
 
+        }
683
 
+        location /sub1 {
684
 
+            echo_sleep 2; # sleeps 2 sec
685
 
+            echo hello;
686
 
+        }
687
 
+        location /sub2 {
688
 
+            echo_sleep 1; # sleeps 1 sec
689
 
+            echo world;
690
 
+        }
691
 
+
692
 
+    Accessing "/main" yields
693
 
+
694
 
+      $ time curl 'http://localhost/main'
695
 
+      hello
696
 
+      world
697
 
+      took 0.000 sec for total.
698
 
+
699
 
+      real  0m2.006s
700
 
+      user  0m0.000s
701
 
+      sys   0m0.004s
702
 
+
703
 
+    You can see that the main handler "/main" does *not* wait the
704
 
+    subrequests "/sub1" and "/sub2" to complete and quickly goes on, hence
705
 
+    the "0.000 sec" timing result. The whole request, however takes
706
 
+    approximately 2 sec in total to complete because "/sub1" and "/sub2" run
707
 
+    in parallel (or "concurrently" to be more accurate).
708
 
+
709
 
+    If you use echo_blocking_sleep in the previous example instead, then
710
 
+    you'll get the same output, but with 3 sec total response time, because
711
 
+    "blocking sleep" blocks the whole Nginx worker process.
712
 
+
713
 
+    Locations can also take an optional querystring argument, for instance
714
 
+
715
 
+        location /main {
716
 
+            echo_location_async /sub 'foo=Foo&bar=Bar';
717
 
+        }
718
 
+        location /sub {
719
 
+            echo $arg_foo $arg_bar;
720
 
+        }
721
 
+
722
 
+    Accessing "/main" yields
723
 
+
724
 
+      $ curl 'http://localhost/main'
725
 
+      Foo Bar
726
 
+
727
 
+    Querystrings is *not* allowed to be concatenated onto the "location"
728
 
+    argument with "?" directly, for example, "/sub?foo=Foo&bar=Bar" is an
729
 
+    invalid location, and shouldn't be fed as the first argument to this
730
 
+    directive.
731
 
+
732
 
+    Due to an unknown bug in Nginx (it still exists in Nginx 0.8.20), the
733
 
+    standard SSI module is required to ensure that the contents of the
734
 
+    subrequests issued by this directive are correctly merged into the
735
 
+    output chains of the main one. Fortunately, the SSI module is enabled by
736
 
+    default during Nginx's "configure" process.
737
 
+
738
 
+    If calling this directive without SSI module enabled, you'll get
739
 
+    truncated response without contents of any subrequests and get an alert
740
 
+    message in your Nginx's "error.log", like this:
741
 
+
742
 
+      [alert] 24212#0: *1 the http output chain is empty, client: 127.0.0.1, ...
743
 
+
744
 
+    Technically speaking, this directive is an example that Nginx content
745
 
+    handler issues one or more subrequests directly. AFAIK, the fancyindex
746
 
+    module (<https://connectical.com/projects/ngx-fancyindex/wiki >) also
747
 
+    does such kind of things ;)
748
 
+
749
 
+    Nginx named locations like @foo is *not* supported here.
750
 
+
751
 
+    This directive is logically equivalent to the GET version of
752
 
+    echo_subrequest_async. For example,
753
 
+
754
 
+      echo_location_async /foo 'bar=Bar';
755
 
+
756
 
+    is logically equivalent to
757
 
+
758
 
+      echo_subrequest_async GET /foo -q 'bar=Bar';
759
 
+
760
 
+    But calling this directive is slightly faster than calling
761
 
+    echo_subrequest_async using "GET" because we don't have to parse the
762
 
+    HTTP method names like "GET" and options like "-q".
763
 
+
764
 
+    This directive is first introduced in version 0.09 of this module and
765
 
+    requires at least Nginx 0.7.46.
766
 
+
767
 
+  echo_location
768
 
+    syntax: *echo_location <location> [<url_args>]*
769
 
+
770
 
+    default: *no*
771
 
+
772
 
+    context: *location*
773
 
+
774
 
+    Just like the echo_location_async directive, but "echo_location" issues
775
 
+    subrequests *in series* rather than in parallel. That is, the content
776
 
+    handler directives following this directive won't be executed until the
777
 
+    subrequest issued by this directive completes.
778
 
+
779
 
+    The final response body is almost always equivalent to the case when
780
 
+    echo_location_async is used instead, only if timing variables is used in
781
 
+    the outputs.
782
 
+
783
 
+    Consider the following example:
784
 
+
785
 
+        location /main {
786
 
+            echo_reset_timer;
787
 
+            echo_location /sub1;
788
 
+            echo_location /sub2;
789
 
+            echo "took $echo_timer_elapsed sec for total.";
790
 
+        }
791
 
+        location /sub1 {
792
 
+            echo_sleep 2;
793
 
+            echo hello;
794
 
+        }
795
 
+        location /sub2 {
796
 
+            echo_sleep 1;
797
 
+            echo world;
798
 
+        }
799
 
+
800
 
+    The location "/main" above will take for total 3 sec to complete
801
 
+    (compared to 2 sec if echo_location_async is used instead here). Here's
802
 
+    the result in action on my machine:
803
 
+
804
 
+      $ curl 'http://localhost/main'
805
 
+      hello
806
 
+      world
807
 
+      took 3.003 sec for total.
808
 
+
809
 
+      real  0m3.027s
810
 
+      user  0m0.020s
811
 
+      sys   0m0.004s
812
 
+
813
 
+    This directive is logically equivalent to the GET version of
814
 
+    echo_subrequest. For example,
815
 
+
816
 
+      echo_location /foo 'bar=Bar';
817
 
+
818
 
+    is logically equivalent to
819
 
+
820
 
+      echo_subrequest GET /foo -q 'bar=Bar';
821
 
+
822
 
+    But calling this directive is slightly faster than calling
823
 
+    echo_subrequest using "GET" because we don't have to parse the HTTP
824
 
+    method names like "GET" and options like "-q".
825
 
+
826
 
+    Behind the scene, it creates an "ngx_http_post_subrequest_t" object as a
827
 
+    *continuation* and passes it into the "ngx_http_subrequest" function
828
 
+    call. Nginx will later reopen this "continuation" in the subrequest's
829
 
+    "ngx_http_finalize_request" function call. We resumes the execution of
830
 
+    the parent-request's content handler and starts to run the next
831
 
+    directive (command) if any.
832
 
+
833
 
+    Nginx named locations like @foo is *not* supported here.
834
 
+
835
 
+    This directive was first introduced in the release v0.12.
836
 
+
837
 
+    See also echo_location_async for more details about the meaning of the
838
 
+    arguments.
839
 
+
840
 
+  echo_subrequest_async
841
 
+    syntax: *echo_subrequest_async <HTTP_method> <location> [-q <url_args>]
842
 
+    [-b <request_body>]*
843
 
+
844
 
+    default: *no*
845
 
+
846
 
+    context: *location*
847
 
+
848
 
+    Initiate an asynchronous subrequest using HTTP method, an optional url
849
 
+    arguments (or querystring), and an option request body.
850
 
+
851
 
+    This directive is very much like a generalized version of the
852
 
+    echo_location_async directive.
853
 
+
854
 
+    Here's a small example demonstrating its usage:
855
 
+
856
 
+        location /multi {
857
 
+            echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi';
858
 
+            echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello';
859
 
+        }
860
 
+        location /sub {
861
 
+            echo "querystring: $query_string";
862
 
+            echo "method: $echo_request_method";
863
 
+            echo "body: $echo_request_body";
864
 
+            echo "content length: $http_content_length";
865
 
+            echo '///';
866
 
+        }
867
 
+
868
 
+    Then on the client side:
869
 
+
870
 
+      $ curl 'http://localhost/multi'
871
 
+      querystring: foo=Foo
872
 
+      method: POST
873
 
+      body: hi
874
 
+      content length: 2
875
 
+      ///
876
 
+      querystring: bar=Bar
877
 
+      method: PUT
878
 
+      body: hello
879
 
+      content length: 5
880
 
+      ///
881
 
+
882
 
+    Here's more funny example using the standard proxy module to handle the
883
 
+    subrequest:
884
 
+
885
 
+        location /main {
886
 
+            echo_subrequest_async POST /sub -b 'hello, world';
887
 
+        }
888
 
+        location /sub {
889
 
+            proxy_pass $scheme://127.0.0.1:$server_port/proxied;
890
 
+        }
891
 
+        location /proxied {
892
 
+            echo "method: $echo_request_method.";
893
 
+
894
 
+            # we need to read body explicitly here...or $echo_request_body
895
 
+            #   will evaluate to empty ("")
896
 
+            echo_read_request_body;
897
 
+
898
 
+            echo "body: $echo_request_body.";
899
 
+        }
900
 
+
901
 
+    Then on the client side, we can see that
902
 
+
903
 
+      $ curl 'http://localhost/main'
904
 
+      method: POST.
905
 
+      body: hello, world.
906
 
+
907
 
+    Nginx named locations like @foo is *not* supported here.
908
 
+
909
 
+    This directive was first introduced in the release v0.15.
910
 
+
911
 
+    See also the echo_subrequest and echo_location_async directives.
912
 
+
913
 
+  echo_subrequest
914
 
+    syntax: *echo_subrequest_async <HTTP_method> <location> [-q <url_args>]
915
 
+    [-b <request_body>]*
916
 
+
917
 
+    default: *no*
918
 
+
919
 
+    context: *location*
920
 
+
921
 
+    This is the synchronous version of the echo_subrequest_async directive.
922
 
+    And just like echo_location, it does not block the Nginx worker process
923
 
+    (while echo_blocking_sleep does), rather, it uses continuation to pass
924
 
+    control along the subrequest chain.
925
 
+
926
 
+    See echo_subrequest_async for more details.
927
 
+
928
 
+    Nginx named locations like @foo is *not* supported here.
929
 
+
930
 
+    This directive was first introduced in the release v0.15.
931
 
+
932
 
+  echo_foreach_split
933
 
+    syntax: *echo_foreach_split <delimiter> <string>*
934
 
+
935
 
+    default: *no*
936
 
+
937
 
+    context: *location*
938
 
+
939
 
+    Split the second argument "string" using the delimiter specified in the
940
 
+    first argument, and then iterate through the resulting items. For
941
 
+    instance:
942
 
+
943
 
+      location /loop {
944
 
+        echo_foreach_split ',' $arg_list;
945
 
+          echo "item: $echo_it";
946
 
+        echo_end;
947
 
+      }
948
 
+
949
 
+    Accessing /main yields
950
 
+
951
 
+      $ curl 'http://localhost/loop?list=cat,dog,mouse'
952
 
+      item: cat
953
 
+      item: dog
954
 
+      item: mouse
955
 
+
956
 
+    As seen in the previous example, this directive should always be
957
 
+    accompanied by an echo_end directive.
958
 
+
959
 
+    Parallel "echo_foreach_split" loops are allowed, but nested ones are
960
 
+    currently forbidden.
961
 
+
962
 
+    The "delimiter" argument could contain *multiple* arbitrary characters,
963
 
+    like
964
 
+
965
 
+      echo_foreach_split '-a-' 'cat-a-dog-a-mouse';
966
 
+        echo $echo_it;
967
 
+      echo_end;
968
 
+
969
 
+    Logically speaking, this looping structure is just the "foreach" loop
970
 
+    combined with a "split" function call in Perl (using the previous
971
 
+    example):
972
 
+
973
 
+       foreach (split ',', $arg_list) {
974
 
+           print "item $_\n";
975
 
+       }
976
 
+
977
 
+    People will also find it useful in merging multiple ".js" or ".css"
978
 
+    resources into a whole. Here's an example:
979
 
+
980
 
+      location /merge {
981
 
+          default_type 'text/javascript';
982
 
+
983
 
+          echo_foreach_split '&' $query_string;
984
 
+              echo "/* JS File $echo_it */";
985
 
+              echo_location_async $echo_it;
986
 
+              echo;
987
 
+          echo_end;
988
 
+      }
989
 
+
990
 
+    Then accessing /merge to merge the ".js" resources specified in the
991
 
+    query string:
992
 
+
993
 
+      $ curl 'http://localhost/merge?/foo/bar.js&/yui/blah.js&/baz.js'
994
 
+
995
 
+    One can also use third-party Nginx cache module to cache the merged
996
 
+    response generated by the "/merge" location in the previous example.
997
 
+
998
 
+    This directive was first introduced in the release v0.17.
999
 
+
1000
 
+  echo_end
1001
 
+    syntax: *echo_end*
1002
 
+
1003
 
+    default: *no*
1004
 
+
1005
 
+    context: *location*
1006
 
+
1007
 
+    This directive is used to terminate the body of looping and conditional
1008
 
+    control structures like echo_foreach_split.
1009
 
+
1010
 
+    This directive was first introduced in the release v0.17.
1011
 
+
1012
 
+  echo_request_body
1013
 
+    syntax: *echo_request_body*
1014
 
+
1015
 
+    default: *no*
1016
 
+
1017
 
+    context: *location*
1018
 
+
1019
 
+    Outputs the contents of the request body previous read.
1020
 
+
1021
 
+    Behind the scene, it's implemented roughly like this:
1022
 
+
1023
 
+      if (r->request_body && r->request_body->bufs) {
1024
 
+          return ngx_http_output_filter(r, r->request_body->bufs);
1025
 
+      }
1026
 
+
1027
 
+    Unlike the $echo_request_body and $request_body variables, this
1028
 
+    directive will show the whole request body even if some parts or all
1029
 
+    parts of it are saved in temporary files on the disk.
1030
 
+
1031
 
+    It is a "no-op" if no request body has been read yet.
1032
 
+
1033
 
+    This directive was first introduced in the release v0.18.
1034
 
+
1035
 
+    See also echo_read_request_body and the chunkin module.
1036
 
+
1037
 
+  echo_exec
1038
 
+    syntax: *echo_exec <location> [<query_string>]*
1039
 
+
1040
 
+    syntax: *echo_exec <named_location>*
1041
 
+
1042
 
+    default: *no*
1043
 
+
1044
 
+    context: *location*
1045
 
+
1046
 
+    Does an internal redirect to the location specified. An optional query
1047
 
+    string can be specified for normal locations, as in
1048
 
+
1049
 
+      location /foo {
1050
 
+          echo_exec /bar weight=5;
1051
 
+      }
1052
 
+      location /bar {
1053
 
+          echo $arg_weight;
1054
 
+      }
1055
 
+
1056
 
+    Or equivalently
1057
 
+
1058
 
+      location /foo {
1059
 
+          echo_exec /bar?weight=5;
1060
 
+      }
1061
 
+      location /bar {
1062
 
+          echo $arg_weight;
1063
 
+      }
1064
 
+
1065
 
+    Named locations are also supported. Here's an example:
1066
 
+
1067
 
+      location /foo {
1068
 
+          echo_exec @bar;
1069
 
+      }
1070
 
+      location @bar {
1071
 
+          # you'll get /foo rather than @bar
1072
 
+          #  due to a potential bug in nginx.
1073
 
+          echo $echo_request_uri;
1074
 
+      }
1075
 
+
1076
 
+    But query string (if any) will always be ignored for named location
1077
 
+    redirects due to a limitation in the "ngx_http_named_location" function.
1078
 
+
1079
 
+    Never try to echo things before the "echo_exec" directive or you won't
1080
 
+    see the proper response of the location you want to redirect to. Because
1081
 
+    any echoing will cause the original location handler to send HTTP
1082
 
+    headers before the redirection happens.
1083
 
+
1084
 
+    Technically speaking, this directive exposes the Nginx internal API
1085
 
+    functions "ngx_http_internal_redirect" and "ngx_http_named_location".
1086
 
+
1087
 
+    This directive was first introduced in the v0.21 release.
1088
 
+
1089
 
+Filter Directives
1090
 
+    Use of the following directives trigger the filter registration of this
1091
 
+    module. By default, no filter will be registered by this module.
1092
 
+
1093
 
+    Every filter directive supports variable interpolation in its arguments
1094
 
+    (if any).
1095
 
+
1096
 
+  echo_before_body
1097
 
+    syntax: *echo_before_body [options] [argument]...*
1098
 
+
1099
 
+    default: *no*
1100
 
+
1101
 
+    context: *location*
1102
 
+
1103
 
+    It's the filter version of the echo directive, and prepends its output
1104
 
+    to the beginning of the original outputs generated by the underlying
1105
 
+    content handler.
1106
 
+
1107
 
+    An example is
1108
 
+
1109
 
+        location /echo {
1110
 
+            echo_before_body hello;
1111
 
+            proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more;
1112
 
+        }
1113
 
+        location /echo/more {
1114
 
+            echo world
1115
 
+        }
1116
 
+
1117
 
+    Accessing "/echo" from the client side yields
1118
 
+
1119
 
+      hello
1120
 
+      world
1121
 
+
1122
 
+    In the previous sample, we borrow the standard proxy module to serve as
1123
 
+    the underlying content handler that generates the "main contents".
1124
 
+
1125
 
+    Multiple instances of this filter directive are also allowed, as in:
1126
 
+
1127
 
+        location /echo {
1128
 
+            echo_before_body hello;
1129
 
+            echo_before_body world;
1130
 
+            echo !;
1131
 
+        }
1132
 
+
1133
 
+    On the client side, the output is like
1134
 
+
1135
 
+      $ curl 'http://localhost/echo'
1136
 
+      hello
1137
 
+      world
1138
 
+      !
1139
 
+
1140
 
+    In this example, we also use the content handler directives provided by
1141
 
+    this module as the underlying content handler.
1142
 
+
1143
 
+    This directive also supports the "-n" and "--" options like the echo
1144
 
+    directive.
1145
 
+
1146
 
+    This directive can be mixed with its brother directive echo_after_body.
1147
 
+
1148
 
+  echo_after_body
1149
 
+    syntax: *echo_after_body [argument]...*
1150
 
+
1151
 
+    default: *no*
1152
 
+
1153
 
+    context: *location*
1154
 
+
1155
 
+    WARNING this directive does not work for nginx >= 0.7.65.
1156
 
+
1157
 
+    It's very much like the echo_before_body directive, but *appends* its
1158
 
+    output to the end of the original outputs generated by the underlying
1159
 
+    content handler.
1160
 
+
1161
 
+    Here's a simple example:
1162
 
+
1163
 
+        location /echo {
1164
 
+            echo_after_body hello;
1165
 
+            proxy_pass http://127.0.0.1:$server_port$request_uri/more;
1166
 
+        }
1167
 
+        location /echo/more {
1168
 
+            echo world
1169
 
+        }
1170
 
+
1171
 
+    Accessing "/echo" from the client side yields
1172
 
+
1173
 
+      world
1174
 
+      hello
1175
 
+
1176
 
+    Multiple instances are allowed, as in:
1177
 
+
1178
 
+        location /echo {
1179
 
+            echo_after_body hello;
1180
 
+            echo_after_body world;
1181
 
+            echo i;
1182
 
+            echo say;
1183
 
+        }
1184
 
+
1185
 
+    The output on the client side while accessing the "/echo" location looks
1186
 
+    like
1187
 
+
1188
 
+      i
1189
 
+      say
1190
 
+      hello
1191
 
+      world
1192
 
+
1193
 
+    This directive also supports the "-n" and "--" options like the echo
1194
 
+    directive.
1195
 
+
1196
 
+    When this directive is used in a location accessed by a subrequest, it
1197
 
+    replies on the "sync" flag set in a chain buffer to indicate the end of
1198
 
+    the output for nginx >= 0.8.7. This is a hack because Nginx does not
1199
 
+    provide a reliable way to determine the end of the output chain in a
1200
 
+    subrequest's output filter. Use it in subrequests with care.
1201
 
+
1202
 
+    This directive can be mixed with its brother directive echo_before_body.
1203
 
+
1204
 
+Variables
1205
 
+  $echo_it
1206
 
+    This is a "topic variable" used by echo_foreach_split, just like the $_
1207
 
+    variable in Perl.
1208
 
+
1209
 
+  $echo_timer_elapsed
1210
 
+    This variable holds the seconds elapsed since the start of the current
1211
 
+    request (might be a subrequest though) or the last invocation of the
1212
 
+    echo_reset_timer command.
1213
 
+
1214
 
+    The timing result takes three digits after the decimal point.
1215
 
+
1216
 
+    References of this variable will force the underlying Nginx timer to
1217
 
+    update to the current system time, regardless the timer resolution
1218
 
+    settings elsewhere in the config file, just like the echo_reset_timer
1219
 
+    directive.
1220
 
+
1221
 
+  $echo_request_body
1222
 
+    Evaluates to the current (sub)request's request body previously read if
1223
 
+    no part of the body has been saved to a temporary file. To always show
1224
 
+    the request body even if it's very large, use the echo_request_body
1225
 
+    directive.
1226
 
+
1227
 
+  $echo_request_method
1228
 
+    Evaluates to the HTTP request method of the current request (it can be a
1229
 
+    subrequest).
1230
 
+
1231
 
+    Behind the scene, it just takes the string data stored in
1232
 
+    "r->method_name".
1233
 
+
1234
 
+    Compare it to the $echo_client_request_method variable.
1235
 
+
1236
 
+    At least for Nginx 0.8.20 and older, the $request_method variable
1237
 
+    provided by the http core module is actually doing what our
1238
 
+    $echo_client_request_method is doing.
1239
 
+
1240
 
+    This variable was first introduced in our v0.15 release.
1241
 
+
1242
 
+  $echo_client_request_method
1243
 
+    Always evaluates to the main request's HTTP method even if the current
1244
 
+    request is a subrequest.
1245
 
+
1246
 
+    Behind the scene, it just takes the string data stored in
1247
 
+    "r->main->method_name".
1248
 
+
1249
 
+    Compare it to the $echo_request_method variable.
1250
 
+
1251
 
+    This variable was first introduced in our v0.15 release.
1252
 
+
1253
 
+  $echo_client_request_headers
1254
 
+    Evaluates to the original client request's headers.
1255
 
+
1256
 
+    Just as the name suggests, it will always take the main request (or the
1257
 
+    client request) even if it's currently executed in a subrequest.
1258
 
+
1259
 
+    A simple example is below:
1260
 
+
1261
 
+      location /echoback {
1262
 
+         echo "headers are:"
1263
 
+         echo $echo_client_request_headers;
1264
 
+      }
1265
 
+
1266
 
+    Accessing "/echoback" yields
1267
 
+
1268
 
+      $ curl 'http://localhost/echoback'
1269
 
+      headers are
1270
 
+      GET /echoback HTTP/1.1
1271
 
+      User-Agent: curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g
1272
 
+      Host: localhost:1984
1273
 
+      Accept: */*
1274
 
+
1275
 
+    Behind the scene, it recovers "r->main->header_in" on the C level and
1276
 
+    does not construct the headers itself by traversing parsed results in
1277
 
+    the request object, and strips the last (trailing) CRLF.
1278
 
+
1279
 
+    This variable was first introduced in version 0.15.
1280
 
+
1281
 
+  $echo_cacheable_request_uri
1282
 
+    Evaluates to the parsed form of the URI (usually led by "/") of the
1283
 
+    current (sub-)request. Unlike the $echo_request_uri variable, it is
1284
 
+    cacheable.
1285
 
+
1286
 
+    See $echo_request_uri for more details.
1287
 
+
1288
 
+    This variable was first introduced in version 0.17.
1289
 
+
1290
 
+  $echo_request_uri
1291
 
+    Evaluates to the parsed form of the URI (usually led by "/") of the
1292
 
+    current (sub-)request. Unlike the $echo_cacheable_request_uri variable,
1293
 
+    it is *not* cacheable.
1294
 
+
1295
 
+    This is quite different from the $request_uri variable exported by the
1296
 
+    [[NginxHttpCoreModule]], because $request_uri is the *unparsed* form of
1297
 
+    the current request's URI.
1298
 
+
1299
 
+    This variable was first introduced in version 0.17.
1300
 
+
1301
 
+  $echo_incr
1302
 
+    It is a counter that always generate the current counting number,
1303
 
+    starting from 1. The counter is always associated with the main request
1304
 
+    even if it is accessed within a subrequest.
1305
 
+
1306
 
+    Consider the following example
1307
 
+
1308
 
+        location /main {
1309
 
+            echo "main pre: $echo_incr";
1310
 
+            echo_location_async /sub;
1311
 
+            echo_location_async /sub;
1312
 
+            echo "main post: $echo_incr";
1313
 
+        }
1314
 
+        location /sub {
1315
 
+            echo "sub: $echo_incr";
1316
 
+        }
1317
 
+
1318
 
+    Accessing "/main" yields
1319
 
+
1320
 
+        main pre: 1
1321
 
+        sub: 3
1322
 
+        sub: 4
1323
 
+        main post: 2
1324
 
+
1325
 
+    This directive was first introduced in the v0.18 release.
1326
 
+
1327
 
+  $echo_response_status
1328
 
+    Evaluates to the status code of the current (sub)request, null if not
1329
 
+    any.
1330
 
+
1331
 
+    Behind the scene, it's just the textual representation of
1332
 
+    "r->headers_out->status".
1333
 
+
1334
 
+    This directive was first introduced in the v0.23 release.
1335
 
+
1336
 
+Installation
1337
 
+    Grab the nginx source code from nginx.net (<http://nginx.net/ >), for
1338
 
+    example, the version 0.8.41 (see nginx compatibility), and then build
1339
 
+    the source with this module:
1340
 
+
1341
 
+        $ wget 'http://sysoev.ru/nginx/nginx-0.8.41.tar.gz'
1342
 
+        $ tar -xzvf nginx-0.8.41.tar.gz
1343
 
+        $ cd nginx-0.8.41/
1344
 
+
1345
 
+        # Here we assume you would install you nginx under /opt/nginx/.
1346
 
+        $ ./configure --prefix=/opt/nginx \
1347
 
+            --add-module=/path/to/echo-nginx-module
1348
 
+
1349
 
+        $ make -j2
1350
 
+        $ make install
1351
 
+
1352
 
+    Download the latest version of the release tarball of this module from
1353
 
+    echo-nginx-module file list
1354
 
+    (<http://github.com/agentzh/echo-nginx-module/downloads >).
1355
 
+
1356
 
+Compatibility
1357
 
+    The following versions of Nginx should work with this module:
1358
 
+
1359
 
+    *   0.8.x (last tested version is 0.8.40)
1360
 
+
1361
 
+    *   0.7.x >= 0.7.21 (last tested version is 0.7.66)
1362
 
+
1363
 
+    In particular,
1364
 
+
1365
 
+    *   the directive echo_location_async and its brother
1366
 
+        echo_subrequest_async do *not* work with 0.7.x < 0.7.46.
1367
 
+
1368
 
+    *   the echo_after_body directive does *not* work at all with nginx <
1369
 
+        0.8.7.
1370
 
+
1371
 
+    *   the echo_sleep directive cannot be used after echo_location or
1372
 
+        echo_subrequest for nginx < 0.8.11.
1373
 
+
1374
 
+    Earlier versions of Nginx like 0.6.x and 0.5.x will *not* work at all.
1375
 
+
1376
 
+    If you find that any particular version of Nginx above 0.7.21 does not
1377
 
+    work with this module, please consider reporting a bug.
1378
 
+
1379
 
+Modules that use this module for testing
1380
 
+    The following modules take advantage of this "echo" module in their test
1381
 
+    suite:
1382
 
+
1383
 
+    *   The memc module that supports almost the whole memcached TCP
1384
 
+        protocol.
1385
 
+
1386
 
+    *   The chunkin module that adds HTTP 1.1 chunked input support to
1387
 
+        Nginx.
1388
 
+
1389
 
+    *   The headers_more module that allows you to add, set, and clear input
1390
 
+        and output headers under the conditions that you specify.
1391
 
+
1392
 
+    *   The "echo" module itself.
1393
 
+
1394
 
+    Please mail me other modules that use "echo" in any form and I'll add
1395
 
+    them to the list above :)
1396
 
+
1397
 
+Report Bugs
1398
 
+    Although a lot of effort has been put into testing and code tuning,
1399
 
+    there must be some serious bugs lurking somewhere in this module. So
1400
 
+    whenever you are bitten by any quirks, please don't hesitate to
1401
 
+
1402
 
+    1.  send a bug report or even patches to <agentzh@gmail.com>,
1403
 
+
1404
 
+    2.  or create a ticket on the issue tracking interface
1405
 
+        (<http://github.com/agentzh/echo-nginx-module/issues >) provided by
1406
 
+        GitHub.
1407
 
+
1408
 
+Source Repository
1409
 
+    Available on github at agentzh/echo-nginx-module
1410
 
+    (<http://github.com/agentzh/echo-nginx-module >).
1411
 
+
1412
 
+ChangeLog
1413
 
+  v0.34
1414
 
+    *   we no longer use the problematic "ngx_strXcmp" macros in our source
1415
 
+        because it may cause invalid reads and thus segmentation faults.
1416
 
+        thanks Piotr Sikora.
1417
 
+
1418
 
+  v0.33
1419
 
+    *   fixed compatibility with nginx 0.7.66+ because the ngx_time_update
1420
 
+        macro's parameter list has changed. Thanks Guang Feng (蔡镜明).
1421
 
+
1422
 
+  v0.32
1423
 
+    *   we should have used "ngx_calloc_buf" instead of "ngx_alloc_buf" for
1424
 
+        the last chunk generated for echo_after_body. thanks valgrind's
1425
 
+        memcheck tool.
1426
 
+
1427
 
+    *   we should initialize flags before feeding it into
1428
 
+        "ngx_http_parse_unsafe_uri". thanks valgrind's memcheck tool.
1429
 
+
1430
 
+    *   fixed a minor issue in the echo_location/echo_subrequest
1431
 
+        implementation, which used to have race conditions.
1432
 
+
1433
 
+  v0.31
1434
 
+    *   the echo wev handler should not proceed if it is still waiting for
1435
 
+        some sequential subrequest or has just processed one to avoid
1436
 
+        bouncing issues.
1437
 
+
1438
 
+    *   fixed a segfault for echo_exec for 0.7.x: we should check "r->done"
1439
 
+        before proceeding.
1440
 
+
1441
 
+    *   no longer explicitly set "r->write_event_handler" to
1442
 
+        "ngx_http_request_empty_handler" because it's totally wrong for the
1443
 
+        state machine.
1444
 
+
1445
 
+    *   fixed the sequential subrequest model bugs: we should ensure the
1446
 
+        "pr->write_event_handler" gets called immediately after the
1447
 
+        "post_subrequest" callback when the subrequest finalizes.
1448
 
+
1449
 
+  v0.30
1450
 
+    *   fixed the echo_exec directive for nginx >= 0.8.11. we didn't get the
1451
 
+        "r->main->count" right in the previous version.
1452
 
+
1453
 
+  v0.29
1454
 
+    *   refactored the core of this module. now the implementation of
1455
 
+        echo_location, echo_subrequest, echo_sleep, and
1456
 
+        echo_read_request_body finally fit well with the nginx event model
1457
 
+        and Igor Sysoev's way of thinking.
1458
 
+
1459
 
+  v0.28
1460
 
+    *   added support for the "-n" and "--" options to the echo,
1461
 
+        echo_before_body, and echo_after_body directives.
1462
 
+
1463
 
+  v0.27
1464
 
+    *   applied the patch from Sergey A. Osokin to work with nginx 0.8.35.
1465
 
+
1466
 
+  v0.26
1467
 
+    *   bug fix: we should bypass upstream filters in our echo filters. an
1468
 
+        output filter should ever call "ngx_http_output_filter" nor
1469
 
+        "ngx_http_send_special".
1470
 
+
1471
 
+  v0.25
1472
 
+    *   now we register a request cleanup handler to ensure our sleep
1473
 
+        event's timer will always get properly deleted even if the request
1474
 
+        is quit prematurely. this affects the echo_sleep directive.
1475
 
+
1476
 
+    *   use ngx_null_string whenever possible in the source.
1477
 
+
1478
 
+    *   sync'd the bundled test scaffold to Test::Nginx 0.07.
1479
 
+
1480
 
+  v0.24
1481
 
+    *   various source file name and coding style fixes. (the code now looks
1482
 
+        more like Igor Sysoev's.)
1483
 
+
1484
 
+  v0.23
1485
 
+    *   now the subrequest can read the client request body directly (for
1486
 
+        the main request) because we made subrequests inherit its parent's
1487
 
+        "r->header_in" as well. This affects the echo_read_request_body
1488
 
+        directive.
1489
 
+
1490
 
+    *   fixed echo_after_body in subrequests by using a hack (checking
1491
 
+        "cl->buf->sync" for the last buf) for nginx 0.8.7+ only.
1492
 
+
1493
 
+    *   added new varaible $echo_response_status to help testing the status
1494
 
+        code of a subrequest. (The memc module makes use of it.)
1495
 
+
1496
 
+    *   use the "ngx_calloc_buf" macro to allocate new bufs in the code
1497
 
+        rather than explicit "ngx_pcalloc" calls for safety.
1498
 
+
1499
 
+  v0.22
1500
 
+    *   Now we allowed all the directives appear in the rewrite module's if
1501
 
+        block. But so far I've only tested the echo directive.
1502
 
+
1503
 
+  v0.21
1504
 
+    *   Added a new directive named echo_exec which does internal redirect
1505
 
+        to other (named) locations.
1506
 
+
1507
 
+  v0.20
1508
 
+    *   Fixed a bug in echo_sleep's "r->main->count" handling for nginx
1509
 
+        0.8.x. This bug will cause the server to hang when proxing a
1510
 
+        location with echo_sleep.
1511
 
+
1512
 
+    *   Applied the "ngx_str3cmp", "ngx_str4cmp", and "ngx_str6cmp"
1513
 
+        optimizing macros to the "parse_method_name" function, as suggested
1514
 
+        by Marcus Clyne.
1515
 
+
1516
 
+    *   Added TODO items regarding $echo_random and "echo_repeat" suggested
1517
 
+        by Marcus Clyne.
1518
 
+
1519
 
+  v0.19
1520
 
+    *   Fixed the CPS-style chained subrequest model for the echo_location
1521
 
+        and echo_subrequest directives. they are now working perfectly and
1522
 
+        will not hang the server with the recent nginx 0.8.21 ~ 0.8.27
1523
 
+        releases. To be specifically, the chained subrequest should call
1524
 
+        "ngx_http_finalize_request" on its parent request if the content
1525
 
+        handler of the parent request does not return "NGX_DONE".
1526
 
+
1527
 
+    *   Undeprecated the echo_location and echo_subrequest directives.
1528
 
+
1529
 
+  v0.18
1530
 
+    *   Fixed the "zero size buf in output" alerts in error.log.
1531
 
+
1532
 
+    *   Added the new directive echo_request_body.
1533
 
+
1534
 
+    *   Now we use the "ngx_http_parse_unsafe_uri" function to check the
1535
 
+        locations to echo_location_async and its friends. Thanks Arvind
1536
 
+        Jayaprakash for suggesting this fix.
1537
 
+
1538
 
+    *   Deprecated the echo_location and echo_subrequest directives.
1539
 
+
1540
 
+    *   For HTTP 1.0 clients, use the buf length of the first chain link as
1541
 
+        the output header Content-Length.
1542
 
+
1543
 
+    *   Implemented new variable $echo_incr.
1544
 
+
1545
 
+  v0.17
1546
 
+    *   Added new directives echo_foreach_split and echo_end. Also
1547
 
+        introduced a "topic variable" named $echo_it.
1548
 
+
1549
 
+    *   Added new variables $echo_request_uri and
1550
 
+        $echo_cacheable_request_uri.
1551
 
+
1552
 
+  v0.16
1553
 
+    *   Now the subrequests issued by the echo_location_async and
1554
 
+        echo_location directives no longer inherit cached variable values
1555
 
+        from its parent request. (The underlying "ngx_http_subrequest"
1556
 
+        function, however, does automatic cachable variable value
1557
 
+        inheritance.)
1558
 
+
1559
 
+    *   Added an undocumented variable *echo_cached_request_uri* to help
1560
 
+        testing of this module.
1561
 
+
1562
 
+  v0.15
1563
 
+    *   Added new directives echo_subrequest and echo_subrequest_async for
1564
 
+        the full nginx subrequest API.
1565
 
+
1566
 
+    *   Removed the "echo_client_request_headers" directive, and provided
1567
 
+        the $echo_client_request_headers variable instead.
1568
 
+
1569
 
+    *   Added new variables $echo_request_method and
1570
 
+        $echo_client_request_method.
1571
 
+
1572
 
+  v0.14
1573
 
+    *   Added new directive echo_read_request_body to explicitly read client
1574
 
+        request body so that the [[NginxHttpCoreModule#$request_body]]
1575
 
+        variable will always have non-empty values.
1576
 
+
1577
 
+    *   Now we shuffer test cases automatically in .t files and fixed bugs
1578
 
+        in the tests themselves which are hidden by config reload fallback
1579
 
+        in failure.
1580
 
+
1581
 
+  v0.13
1582
 
+    *   Fixed the special cases when the outputs of a echo_duplicate
1583
 
+        directive is empty.
1584
 
+
1585
 
+    *   Now we explicitly clear content length and accept ranges headers in
1586
 
+        the content handler.
1587
 
+
1588
 
+  v0.12
1589
 
+    *   Implemented the echo_location directive, which can issue chained GET
1590
 
+        subrequests in the Continuation Passing Style (CPS), rather than the
1591
 
+        parallel subrequest issued by the echo_location_async directive.
1592
 
+
1593
 
+  v0.11
1594
 
+    *   Implemented the echo_duplicate directive to help generating large
1595
 
+        chunk of data for testing.
1596
 
+
1597
 
+  v0.10
1598
 
+    *   Fixed compilation regression against Nginx 0.7.21. This bug appears
1599
 
+        in version 0.09.
1600
 
+
1601
 
+    *   Refactored the codebase by splitting source into various small
1602
 
+        files.
1603
 
+
1604
 
+  v0.09
1605
 
+    *   Reimplement the echo_sleep directive using per-request event and
1606
 
+        timer; the old implementation uses the global connection's
1607
 
+        read/write event to register timer, so it will break horribly when
1608
 
+        multiple subrequests "sleep" at the same time.
1609
 
+
1610
 
+    *   Added the echo_location_async directive which can issue a GET
1611
 
+        subrequest and insert its contents herein.
1612
 
+
1613
 
+  v0.08
1614
 
+    *   echo_sleep: now we delete our "write event timer" in the
1615
 
+        "post_sleep" handle.
1616
 
+
1617
 
+    *   Added "doc/manpage.wiki" which tracks changes in the wiki page
1618
 
+        (<http://wiki.nginx.org/NginxHttpEchoModule >).
1619
 
+
1620
 
+    *   Added the "util/wiki2pod.pl" script to convert "doc/manpage.wiki" to
1621
 
+        "README".
1622
 
+
1623
 
+    *   Disabled the "DDEBUG" macro in the C source by default.
1624
 
+
1625
 
+Test Suite
1626
 
+    This module comes with a Perl-driven test suite. The test cases
1627
 
+    (<http://github.com/agentzh/echo-nginx-module/tree/master/test/t/ >) are
1628
 
+    declarative
1629
 
+    (<http://github.com/agentzh/echo-nginx-module/blob/master/test/t/echo.t >
1630
 
+    ) too. Thanks to the Test::Base
1631
 
+    (<http://search.cpan.org/perldoc?Test::Base >) module in the Perl world.
1632
 
+
1633
 
+    To run it on your side:
1634
 
+
1635
 
+        $ cd test
1636
 
+        $ PATH=/path/to/your/nginx-with-echo-module:$PATH prove -r t
1637
 
+
1638
 
+    You need to terminate any Nginx processes before running the test suite
1639
 
+    if you have changed the Nginx server binary.
1640
 
+
1641
 
+    At the moment, LWP::UserAgent
1642
 
+    (<http://search.cpan.org/perldoc?LWP::UserAgent >) is used by the test
1643
 
+    scaffold
1644
 
+    (<http://github.com/agentzh/echo-nginx-module/blob/master/test/lib/Test/
1645
 
+    Nginx/Echo.pm>) for simplicity and it's rather weak in testing
1646
 
+    *streaming* behavior of Nginx (I'm using "curl" to test these aspects
1647
 
+    manually for now). I'm considering coding up my own Perl HTTP client
1648
 
+    library based on IO::Select
1649
 
+    (<http://search.cpan.org/perldoc?IO::Select >) and IO::Socket
1650
 
+    (<http://search.cpan.org/perldoc?IO::Socket >) (there might be already
1651
 
+    one around?).
1652
 
+
1653
 
+    Because a single nginx server (by default, "localhost:1984") is used
1654
 
+    across all the test scripts (".t" files), it's meaningless to run the
1655
 
+    test suite in parallel by specifying "-jN" when invoking the "prove"
1656
 
+    utility.
1657
 
+
1658
 
+    Some parts of the test suite requires standard modules proxy, rewrite
1659
 
+    and SSI to be enabled as well when building Nginx.
1660
 
+
1661
 
+TODO
1662
 
+    *   Fix the echo_after_body directive in subrequests.
1663
 
+
1664
 
+    *   Add directives *echo_read_client_request_body* and
1665
 
+        *echo_request_headers*.
1666
 
+
1667
 
+    *   Add new directive *echo_log* to use Nginx's logging facility
1668
 
+        directly from the config file and specific loglevel can be
1669
 
+        specified, as in
1670
 
+
1671
 
+      echo_log debug "I am being called.";
1672
 
+
1673
 
+    *   Add support for options "-h" and "-t" to echo_subrequest_async and
1674
 
+        echo_subrequest. For example
1675
 
+
1676
 
+      echo_subrequest POST /sub -q 'foo=Foo&bar=Bar' -b 'hello' -t 'text/plan' -h 'X-My-Header: blah blah'
1677
 
+
1678
 
+    *   Add options to control whether a subrequest should inherit cached
1679
 
+        variables from its parent request (i.e. the current request that is
1680
 
+        calling the subrequest in question). Currently none of the
1681
 
+        subrequests issued by this module inherit the cached variables from
1682
 
+        the parent request.
1683
 
+
1684
 
+    *   Add new variable *$echo_active_subrequests* to show "r->main->count
1685
 
+        - 1".
1686
 
+
1687
 
+    *   Add the *echo_file* and *echo_cached_file* directives.
1688
 
+
1689
 
+    *   Add new varaible *$echo_request_headers* to accompany the existing
1690
 
+        $echo_client_request_headers variable.
1691
 
+
1692
 
+    *   Add new directive *echo_foreach*, as in
1693
 
+
1694
 
+      echo_foreach 'cat' 'dog' 'mouse';
1695
 
+        echo_location_async "/animals/$echo_it";
1696
 
+      echo_end;
1697
 
+
1698
 
+    *   Add new directive *echo_foreach_range*, as in
1699
 
+
1700
 
+      echo_foreach_range '[1..100]' '[a-zA-z0-9]';
1701
 
+        echo_location_async "/item/$echo_it";
1702
 
+      echo_end;
1703
 
+
1704
 
+    *   Add new directive *echo_repeat*, as in
1705
 
+
1706
 
+      echo_repeat 10 $i {
1707
 
+          echo "Page $i";
1708
 
+          echo_location "/path/to/page/$i";
1709
 
+      }
1710
 
+
1711
 
+    This is just another way of saying
1712
 
+
1713
 
+      echo_foreach_range $i [1..10];
1714
 
+          echo "Page $i";
1715
 
+          echo_location "/path/to/page/$i";
1716
 
+      echo_end;
1717
 
+
1718
 
+    Thanks Marcus Clyne for providing this idea.
1719
 
+
1720
 
+    *   Add new variable $echo_random which always returns a random
1721
 
+        non-negative integer with the lower/upper limit specified by the new
1722
 
+        directives "echo_random_min" and "echo_random_max". For example,
1723
 
+
1724
 
+      echo_random_min  10
1725
 
+      echo_random_max  200
1726
 
+      echo "random number: $echo_random";
1727
 
+
1728
 
+    Thanks Marcus Clyne for providing this idea.
1729
 
+
1730
 
+Getting involved
1731
 
+    You'll be very welcomed to submit patches to the author or just ask for
1732
 
+    a commit bit to the source repository on GitHub.
1733
 
+
1734
 
+Author
1735
 
+    agentzh (章亦春) *<agentzh@gmail.com>*
1736
 
+
1737
 
+    This wiki page is also maintained by the author himself, and everybody
1738
 
+    is encouraged to improve this page as well.
1739
 
+
1740
 
+Copyright & License
1741
 
+    Copyright (c) 2009, Taobao Inc., Alibaba Group ( http://www.taobao.com
1742
 
+    ).
1743
 
+
1744
 
+    Copyright (c) 2009, agentzh <agentzh@gmail.com>.
1745
 
+
1746
 
+    This module is licensed under the terms of the BSD license.
1747
 
+
1748
 
+    Redistribution and use in source and binary forms, with or without
1749
 
+    modification, are permitted provided that the following conditions are
1750
 
+    met:
1751
 
+
1752
 
+    *   Redistributions of source code must retain the above copyright
1753
 
+        notice, this list of conditions and the following disclaimer.
1754
 
+
1755
 
+    *   Redistributions in binary form must reproduce the above copyright
1756
 
+        notice, this list of conditions and the following disclaimer in the
1757
 
+        documentation and/or other materials provided with the distribution.
1758
 
+
1759
 
+    *   Neither the name of the Taobao Inc. nor the names of its
1760
 
+        contributors may be used to endorse or promote products derived from
1761
 
+        this software without specific prior written permission.
1762
 
+
1763
 
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
1764
 
+    IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1765
 
+    TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
1766
 
+    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1767
 
+    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1768
 
+    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
1769
 
+    TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1770
 
+    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
1771
 
+    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
1772
 
+    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1773
 
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1774
 
+
1775
 
+See Also
1776
 
+    *   The original blog post
1777
 
+        (<http://agentzh.spaces.live.com/blog/cns!FF3A735632E41548!478.entry
1778
 
+        >) about this module's initial development.
1779
 
+
1780
 
+    *   The standard addition filter module.
1781
 
+
1782
 
+    *   The standard proxy module.
1783
 
+
1784
 
Index: 0.8/modules/nginx-echo/config
1785
 
===================================================================
1786
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
1787
 
+++ 0.8/modules/nginx-echo/config       2010-10-31 07:35:23.648338001 +0000
1788
 
@@ -0,0 +1,5 @@
1789
 
+ngx_addon_name=ngx_http_echo_module
1790
 
+HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_echo_module"
1791
 
+NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_echo_module.c $ngx_addon_dir/src/ngx_http_echo_util.c $ngx_addon_dir/src/ngx_http_echo_timer.c $ngx_addon_dir/src/ngx_http_echo_var.c $ngx_addon_dir/src/ngx_http_echo_handler.c $ngx_addon_dir/src/ngx_http_echo_filter.c $ngx_addon_dir/src/ngx_http_echo_sleep.c $ngx_addon_dir/src/ngx_http_echo_location.c $ngx_addon_dir/src/ngx_http_echo_echo.c $ngx_addon_dir/src/ngx_http_echo_request_info.c $ngx_addon_dir/src/ngx_http_echo_subrequest.c $ngx_addon_dir/src/ngx_http_echo_foreach.c"
1792
 
+NGX_ADDON_DEPS="$NGX_ADDON_DEPS $ngx_addon_dir/src/ngx_http_echo_module.h $ngx_addon_dir/src/ddebug.h $ngx_addon_dir/src/ngx_http_echo_handler.h $ngx_addon_dir/src/ngx_http_echo_util.h $ngx_addon_dir/src/ngx_http_echo_sleep.h $ngx_addon_dir/src/ngx_http_echo_filter.h $ngx_addon_dir/src/ngx_http_echo_var.h $ngx_addon_dir/src/ngx_http_echo_location.h $ngx_addon_dir/src/ngx_http_echo_echo.h $ngx_addon_dir/src/ngx_http_echo_request_info.h $ngx_addon_dir/src/ngx_http_echo_subrequest.h $ngx_addon_dir/src/ngx_http_echo_foreach.h"
1793
 
+
1794
 
Index: 0.8/modules/nginx-echo/doc/manpage.wiki
1795
 
===================================================================
1796
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
1797
 
+++ 0.8/modules/nginx-echo/doc/manpage.wiki     2010-10-31 07:35:47.708338000 +0000
1798
 
@@ -0,0 +1,1545 @@
1799
 
+= Name =
1800
 
+
1801
 
+'''ngx_echo''' - Brings "echo", "sleep", "time", "exec" and more shell-style goodies to Nginx config file.
1802
 
+
1803
 
+''This module is not distributed with the Nginx source.'' See [[#Installation|the installation instructions]].
1804
 
+
1805
 
+= Version =
1806
 
+
1807
 
+This document describes echo-nginx-module [http://github.com/agentzh/echo-nginx-module/tarball/v0.34 v0.34] released on September 14, 2010.
1808
 
+
1809
 
+= Synopsis =
1810
 
+
1811
 
+<geshi lang="nginx">
1812
 
+  location /hello {
1813
 
+    echo "hello, world!";
1814
 
+  }
1815
 
+</geshi>
1816
 
+
1817
 
+<geshi lang="nginx">
1818
 
+  location /hello {
1819
 
+    echo -n "hello, "
1820
 
+    echo "world!";
1821
 
+  }
1822
 
+</geshi>
1823
 
+
1824
 
+<geshi lang="nginx">
1825
 
+  location /timed_hello {
1826
 
+    echo_reset_timer;
1827
 
+    echo hello world;
1828
 
+    echo "'hello world' takes about $echo_timer_elapsed sec.";
1829
 
+    echo hiya igor;
1830
 
+    echo "'hiya igor' takes about $echo_timer_elapsed sec.";
1831
 
+  }
1832
 
+</geshi>
1833
 
+
1834
 
+<geshi lang="nginx">
1835
 
+  location /echo_with_sleep {
1836
 
+    echo hello;
1837
 
+    echo_flush;  # ensure the client can see previous output immediately
1838
 
+    echo_sleep   2.5;  # in sec
1839
 
+    echo world;
1840
 
+  }
1841
 
+</geshi>
1842
 
+
1843
 
+<geshi lang="nginx">
1844
 
+  # in the following example, accessing /echo yields
1845
 
+  #   hello
1846
 
+  #   world
1847
 
+  #   blah
1848
 
+  #   hiya
1849
 
+  #   igor
1850
 
+  location /echo {
1851
 
+      echo_before_body hello;
1852
 
+      echo_before_body world;
1853
 
+      proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more;
1854
 
+      echo_after_body hiya;
1855
 
+      echo_after_body igor;
1856
 
+  }
1857
 
+  location /echo/more {
1858
 
+      echo blah;
1859
 
+  }
1860
 
+</geshi>
1861
 
+
1862
 
+<geshi lang="nginx">
1863
 
+  # the output of /main might be
1864
 
+  #   hello
1865
 
+  #   world
1866
 
+  #   took 0.000 sec for total.
1867
 
+  # and the whole request would take about 2 sec to complete.
1868
 
+  location /main {
1869
 
+      echo_reset_timer;
1870
 
+
1871
 
+      # subrequests in parallel
1872
 
+      echo_location_async /sub1;
1873
 
+      echo_location_async /sub2;
1874
 
+
1875
 
+      echo "took $echo_timer_elapsed sec for total.";
1876
 
+  }
1877
 
+  location /sub1 {
1878
 
+      echo_sleep 2;
1879
 
+      echo hello;
1880
 
+  }
1881
 
+  location /sub2 {
1882
 
+      echo_sleep 1;
1883
 
+      echo world;
1884
 
+  }
1885
 
+</geshi>
1886
 
+
1887
 
+<geshi lang="nginx">
1888
 
+  # the output of /main might be
1889
 
+  #   hello
1890
 
+  #   world
1891
 
+  #   took 3.003 sec for total.
1892
 
+  # and the whole request would take about 3 sec to complete.
1893
 
+  location /main {
1894
 
+      echo_reset_timer;
1895
 
+
1896
 
+      # subrequests in series (chained by CPS)
1897
 
+      echo_location /sub1;
1898
 
+      echo_location /sub2;
1899
 
+
1900
 
+      echo "took $echo_timer_elapsed sec for total.";
1901
 
+  }
1902
 
+  location /sub1 {
1903
 
+      echo_sleep 2;
1904
 
+      echo hello;
1905
 
+  }
1906
 
+  location /sub2 {
1907
 
+      echo_sleep 1;
1908
 
+      echo world;
1909
 
+  }
1910
 
+</geshi>
1911
 
+
1912
 
+<geshi lang="nginx">
1913
 
+  # Accessing /dup gives
1914
 
+  #   ------ END ------
1915
 
+  location /dup {
1916
 
+    echo_duplicate 3 "--";
1917
 
+    echo_duplicate 1 " END ";
1918
 
+    echo_duplicate 3 "--";
1919
 
+    echo;
1920
 
+  }
1921
 
+</geshi>
1922
 
+
1923
 
+<geshi lang="nginx">
1924
 
+  # /bighello will generate 1000,000,000 hello's.
1925
 
+  location /bighello {
1926
 
+    echo_duplicate 1000_000_000 'hello';
1927
 
+  }
1928
 
+</geshi>
1929
 
+
1930
 
+<geshi lang="nginx">
1931
 
+  # echo back the client request
1932
 
+  location /echoback {
1933
 
+    echo_duplicate 1 $echo_client_request_headers;
1934
 
+    echo "\r";
1935
 
+
1936
 
+    echo_read_request_body;
1937
 
+
1938
 
+    echo_request_body;
1939
 
+  }
1940
 
+</geshi>
1941
 
+
1942
 
+<geshi lang="nginx">
1943
 
+  # GET /multi will yields
1944
 
+  #   querystring: foo=Foo
1945
 
+  #   method: POST
1946
 
+  #   body: hi
1947
 
+  #   content length: 2
1948
 
+  #   ///
1949
 
+  #   querystring: bar=Bar
1950
 
+  #   method: PUT
1951
 
+  #   body: hello
1952
 
+  #   content length: 5
1953
 
+  #   ///
1954
 
+  location /multi {
1955
 
+      echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi';
1956
 
+      echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello';
1957
 
+  }
1958
 
+  location /sub {
1959
 
+      echo "querystring: $query_string";
1960
 
+      echo "method: $echo_request_method";
1961
 
+      echo "body: $echo_request_body";
1962
 
+      echo "content length: $http_content_length";
1963
 
+      echo '///';
1964
 
+  }
1965
 
+</geshi>
1966
 
+
1967
 
+<geshi lang="nginx">
1968
 
+  # GET /merge?/foo.js&/bar/blah.js&/yui/baz.js will merge the .js resources together
1969
 
+  location /merge {
1970
 
+      default_type 'text/javascript';
1971
 
+      echo_foreach_split '&' $query_string;
1972
 
+          echo "/* JS File $echo_it */";
1973
 
+          echo_location_async $echo_it;
1974
 
+          echo;
1975
 
+      echo_end;
1976
 
+  }
1977
 
+</geshi>
1978
 
+
1979
 
+<geshi lang="nginx">
1980
 
+  # accessing /if?val=abc yields the "hit" output
1981
 
+  # while /if?val=bcd yields "miss":
1982
 
+  location ^~ /if {
1983
 
+      set $res miss;
1984
 
+      if ($arg_val ~* '^a') {
1985
 
+          set $res hit;
1986
 
+          echo $res;
1987
 
+      }
1988
 
+      echo $res;
1989
 
+  }
1990
 
+</geshi>
1991
 
+
1992
 
+= Description =
1993
 
+
1994
 
+This module wraps lots of Nginx internal APIs for streaming input and output, parallel/sequential subrequests, timers and sleeping, as well as various meta data accessing.
1995
 
+
1996
 
+Basically it provides various utilities that help testing and debugging of other modules by trivially emulating different kinds of faked subrequest locations.
1997
 
+
1998
 
+People will also find it useful in real-world applications that need to
1999
 
+
2000
 
+# serve static contents directly from memory (loading from the Nginx config file).
2001
 
+# wrap the upstream response with custom header and footer (kinda like the [[NginxHttpAdditionModule|addition module]] but with contents read directly from the config file and Nginx variables).
2002
 
+# merge contents of various "Nginx locations" (i.e., subrequests) together in a single main request (using [[#echo_location|echo_location]] and its friends).
2003
 
+
2004
 
+This is a special dual-role module that can ''lazily'' serve as a content handler or register itself as an output filter only upon demand. By default, this module does not do anything at all.
2005
 
+
2006
 
+Use of any of this module's directives (no matter [[#Content Handler Directives|content handler directives]] or [[#Filter Directives|filter directives]]) will force the chunked encoding to be used for the HTTP response due to the streaming nature of this module (unless HTTP 1.0 is enforced by the client and the Content-Length header will be set to the size of the first handler directive that generates contents).
2007
 
+
2008
 
+Technially, this module has also demonstrated the following techniques that might be helpful for module writers:
2009
 
+
2010
 
+# Issue parallel subreqeusts directly from content handler.
2011
 
+# Issue chained subrequests directly from content handler, by passing continuation along the subrequest chain.
2012
 
+# Issue subrequests with all HTTP 1.1 methods and even an optional faked HTTP request body.
2013
 
+# Interact with the Nginx event model directly from content handler using custom events and timers, and resume the content handler back if necessary.
2014
 
+# Dual-role module that can (lazily) serve as a content handler or an output filter or both.
2015
 
+# Nginx config file variable creation and interpolation.
2016
 
+# Streaming output control using output_chain, flush and its friends.
2017
 
+# Read client request body from the content handler, and returns back (asynchronously) to the content handler after completion.
2018
 
+# Use Perl-based declarative [[#Test Suite|test suite]] to drive the development of Nginx C modules.
2019
 
+
2020
 
+= Content Handler Directives =
2021
 
+
2022
 
+Use of the following directives register this module to the current Nginx location as a content handler. If you want to use another module, like the [[NginxHttpProxyModule|standard proxy module]], as the content handler, use the [[#Filter Directives|filter directives]] provided by this module.
2023
 
+
2024
 
+All the content handler directives can be mixed together in a single Nginx location and they're supposed to run sequentially just as in the Bash scripting language.
2025
 
+
2026
 
+Every content handler directive supports variable interpolation in its arguments (if any).
2027
 
+
2028
 
+The MIME type set by the [[NginxHttpCoreModule#default_type|standard default_type directive]] is respected by this module, as in:
2029
 
+
2030
 
+<geshi lang="nginx">
2031
 
+  location /hello {
2032
 
+    default_type text/plain;
2033
 
+    echo hello;
2034
 
+  }
2035
 
+</geshi>
2036
 
+
2037
 
+Then on the client side:
2038
 
+
2039
 
+<geshi lang="bash">
2040
 
+  $ curl -I 'http://localhost/echo'
2041
 
+  HTTP/1.1 200 OK
2042
 
+  Server: nginx/0.8.20
2043
 
+  Date: Sat, 17 Oct 2009 03:40:19 GMT
2044
 
+  Content-Type: text/plain
2045
 
+  Connection: keep-alive
2046
 
+</geshi>
2047
 
+
2048
 
+Since the [[#v0.22|v0.22]] release, all of the directives are allowed in the [[NginxHttpRewriteModule|rewrite module]]'s [[NginxHttpRewriteModule#if|if]] directive block, for instance:
2049
 
+
2050
 
+<geshi lang="nginx">
2051
 
+    location ^~ /if {
2052
 
+        set $res miss;
2053
 
+        if ($arg_val ~* '^a') {
2054
 
+            set $res hit;
2055
 
+            echo $res;
2056
 
+        }
2057
 
+        echo $res;
2058
 
+    }
2059
 
+</geshi>
2060
 
+
2061
 
+== echo ==
2062
 
+'''syntax:''' ''echo [options] <string>...''
2063
 
+
2064
 
+'''default:''' ''no''
2065
 
+
2066
 
+'''context:''' ''location''
2067
 
+
2068
 
+Sends arguments joined by spaces, along with a trailing newline, out to the client.
2069
 
+
2070
 
+Note that the data might be buffered by Nginx's underlying buffer. To force the output data flushed immediately, use the [[#echo_flush|echo_flush]] command just after <code>echo</code>, as in
2071
 
+
2072
 
+<geshi lang="nginx">
2073
 
+   echo hello world;
2074
 
+   echo_flush;
2075
 
+</geshi>
2076
 
+
2077
 
+When no argument is specified, ''echo'' emits the trailing newline alone, just like the ''echo'' command in shell.
2078
 
+
2079
 
+Variables may appear in the arguments. An example is
2080
 
+
2081
 
+<geshi lang="nginx">
2082
 
+   echo The current request uri is $request_uri;
2083
 
+</geshi>
2084
 
+
2085
 
+where [[NginxHttpCoreModule#$request_uri|$request_uri]] is a variable exposed by the [[NginxHttpCoreModule]].
2086
 
+
2087
 
+This command can be used multiple times in a single location configuration, as in
2088
 
+
2089
 
+<geshi lang="nginx">
2090
 
+    location /echo {
2091
 
+        echo hello;
2092
 
+        echo world;
2093
 
+    }
2094
 
+</geshi>
2095
 
+
2096
 
+The output on the client side looks like this
2097
 
+
2098
 
+<geshi lang="bash">
2099
 
+    $ curl 'http://localhost/echo'
2100
 
+    hello
2101
 
+    world
2102
 
+</geshi>
2103
 
+
2104
 
+Special characters like newlines (<code>\n</code>) and tabs (<code>\t</code>) can be escaped using C-style escaping sequences. But a notable exception is the dollar sign (<code>$</code>). As of Nginx 0.8.20, there's still no clean way to esacpe this characters. (A work-around might be to use a <code>$echo_dollor</code> variable that is always evaluated to the constant <code>$</code> character. This feature will possibly be introduced in a future version of this module.)
2105
 
+
2106
 
+As of the echo [[#v0.28|v0.28]] release, one can suppress the trailing newline character in the output by using the <code>-n</code> option, as in
2107
 
+
2108
 
+<geshi lang="nginx">
2109
 
+    location /echo {
2110
 
+        echo -n "hello, ";
2111
 
+        echo "world";
2112
 
+    }
2113
 
+</geshi>
2114
 
+
2115
 
+Accessing <code>/echo</code> gives
2116
 
+
2117
 
+<geshi lang="bash">
2118
 
+    $ curl 'http://localhost/echo'
2119
 
+    hello, world
2120
 
+</geshi>
2121
 
+
2122
 
+Leading <code>-n</code> in variable values won't take effect and will be emitted literally, as in
2123
 
+
2124
 
+<geshi lang="nginx">
2125
 
+    location /echo {
2126
 
+        set $opt -n;
2127
 
+        echo $opt "hello,";
2128
 
+        echo "world";
2129
 
+    }
2130
 
+</geshi>
2131
 
+
2132
 
+This gives the following output
2133
 
+
2134
 
+<geshi lang="bash">
2135
 
+    $ curl 'http://localhost/echo'
2136
 
+    -n hello,
2137
 
+    world
2138
 
+</geshi>
2139
 
+
2140
 
+One can output leading <code>-n</code> literals and other options using the special <code>--</code> option like this
2141
 
+
2142
 
+<geshi lang="nginx">
2143
 
+    location /echo {
2144
 
+        echo -- -n is an option;
2145
 
+    }
2146
 
+</geshi>
2147
 
+
2148
 
+which yields
2149
 
+
2150
 
+<geshi lang="bash">
2151
 
+    $ curl 'http://localhost/echo'
2152
 
+    -n is an option
2153
 
+</geshi>
2154
 
+
2155
 
+== echo_duplicate ==
2156
 
+'''syntax:''' ''echo_duplicate <count> <string>''
2157
 
+
2158
 
+'''default:''' ''no''
2159
 
+
2160
 
+'''context:''' ''location''
2161
 
+
2162
 
+Outputs duplication of a string indicated by the second argument, using the times specified in the first argument.
2163
 
+
2164
 
+For instance,
2165
 
+
2166
 
+<geshi lang="nginx">
2167
 
+  location /dup {
2168
 
+      echo_duplicate 3 "abc";
2169
 
+  }
2170
 
+</geshi>
2171
 
+
2172
 
+will lead to an output of <code>"abcabcabc"</code>.
2173
 
+
2174
 
+Underscores are allowed in the count number, just like in Perl. For example, to emit 1000,000,000 instances of <code>"hello, world"</code>:
2175
 
+
2176
 
+<geshi lang="nginx">
2177
 
+  location /many_hellos {
2178
 
+      echo_duplicate 1000_000_000 "hello, world";
2179
 
+  }
2180
 
+</geshi>
2181
 
+
2182
 
+The <code>count</code> argument could be zero, but not negative. The second <code>string</code> argument could be an empty string ("") likewise.
2183
 
+
2184
 
+Unlike the [[#echo|echo]] directive, no trailing newline is appended to the result. So it's possible to "abuse" this directive as a no-trailing-newline version of [[#echo|echo]] by using "count" 1, as in
2185
 
+
2186
 
+<geshi lang="nginx">
2187
 
+  location /echo_art {
2188
 
+      echo_duplicate 2 '---';
2189
 
+      echo_duplicate 1 ' END ';  # we don't want a trailing newline here
2190
 
+      echo_duplicate 2 '---';
2191
 
+      echo;  # we want a trailing newline here...
2192
 
+  }
2193
 
+</geshi>
2194
 
+
2195
 
+You get
2196
 
+
2197
 
+  ------ END ------
2198
 
+
2199
 
+This directive was first introduced in [[#v0.11|version 0.11]].
2200
 
+
2201
 
+== echo_flush ==
2202
 
+'''syntax:''' ''echo_flush''
2203
 
+
2204
 
+'''default:''' ''no''
2205
 
+
2206
 
+'''context:''' ''location''
2207
 
+
2208
 
+Forces the data potentially buffered by underlying Nginx output filters to send immediately to the client side via socket.
2209
 
+
2210
 
+Note that techically the command just emits a ngx_buf_t object with <code>flush</code> slot set to 1, so certain weird third-party output filter module could still block it before it reaches Nginx's (last) write filter.
2211
 
+
2212
 
+This directive does not take any argument.
2213
 
+
2214
 
+Consider the following example:
2215
 
+
2216
 
+<geshi lang="nginx">
2217
 
+  location /flush {
2218
 
+     echo hello;
2219
 
+
2220
 
+     echo_flush;
2221
 
+
2222
 
+     echo_sleep 1;
2223
 
+     echo world;
2224
 
+  }
2225
 
+</geshi>
2226
 
+
2227
 
+Then on the client side, using curl to access <code>/flush</code>, you'll see the "hello" line immediately, but only after 1 second, the last "world" line. Without calling <code>echo_flush</code> in the example above, you'll most likely see no output until 1 second is elapsed due to the internal buffering of Nginx.
2228
 
+
2229
 
+This directive will fail to flush the output buffer in case of subrequests get involved. Consider the following example:
2230
 
+
2231
 
+  location /main {
2232
 
+      echo_location_async /sub;
2233
 
+      echo hello;
2234
 
+      echo_flush;
2235
 
+  }
2236
 
+  location /sub {
2237
 
+      echo_sleep 1;
2238
 
+  }
2239
 
+
2240
 
+Then the client won't see "hello" appear even if <code>echo_flush</code> has been executed before the subrequest to <code>/sub</code> has actually started executing. The outputs of <code>/main</code> that are sent ''after'' [[#echo_location_async|echo_location_async]] will be postponed and buffered firmly.
2241
 
+
2242
 
+This does ''not'' apply to outputs sent before the subrequest initiated. For a modified version of the example given above:
2243
 
+
2244
 
+  location /main {
2245
 
+      echo hello;
2246
 
+      echo_flush;
2247
 
+      echo_location_async /sub;
2248
 
+  }
2249
 
+  location /sub {
2250
 
+      echo_sleep 1;
2251
 
+  }
2252
 
+
2253
 
+The client will immediately see "hello" before <code>/sub</code> enters sleeping.
2254
 
+
2255
 
+See also [[#echo|echo]], [[#echo_sleep|echo_sleep]], and [[#echo_location_async|echo_location_async]].
2256
 
+
2257
 
+== echo_sleep ==
2258
 
+'''syntax:''' ''echo_sleep <seconds>''
2259
 
+
2260
 
+'''default:''' ''no''
2261
 
+
2262
 
+'''context:''' ''location''
2263
 
+
2264
 
+Sleeps for the time period specified by the argument, which is in seconds.
2265
 
+
2266
 
+This operation is non-blocking on server side, so unlike the [[#echo_blocking_sleep|echo_blocking_sleep]] directive, it won't block the whole Nginx worker process.
2267
 
+
2268
 
+The period might takes three digits after the decimal point and must be greater than 0.001.
2269
 
+
2270
 
+An example is
2271
 
+
2272
 
+<geshi lang="nginx">
2273
 
+   location /echo_after_sleep {
2274
 
+       echo_sleep 1.234;
2275
 
+       echo resumed!;
2276
 
+   }
2277
 
+</geshi>
2278
 
+
2279
 
+Behind the scene, it sets up a per-request "sleep" ngx_event_t object, and adds a timer using that custom event to the Nginx event model and just waits for a timeout on that event. Because the "sleep" event is per-request, this directive can work in parallel subrequests.
2280
 
+
2281
 
+== echo_blocking_sleep ==
2282
 
+'''syntax:''' ''echo_blocking_sleep <seconds>''
2283
 
+
2284
 
+'''default:''' ''no''
2285
 
+
2286
 
+'''context:''' ''location''
2287
 
+
2288
 
+This is a blocking version of the [[#echo_sleep|echo_sleep]] directive.
2289
 
+
2290
 
+See the documentation of [[#echo_sleep|echo_sleep]] for more detail.
2291
 
+
2292
 
+Behind the curtain, it calls the ngx_msleep macro provided by the Nginx core which maps to usleep on POSIX-compliant systems.
2293
 
+
2294
 
+Note that this directive will block the current Nginx worker process completely while being executed, so never use it in production environment.
2295
 
+
2296
 
+== echo_reset_timer ==
2297
 
+'''syntax:''' ''echo_reset_timer''
2298
 
+
2299
 
+'''default:''' ''no''
2300
 
+
2301
 
+'''context:''' ''location''
2302
 
+
2303
 
+Reset the timer begin time to ''now'', i.e., the time when this command is executed during request.
2304
 
+
2305
 
+The timer begin time is default to the starting time of the current request and can be overridden by this directive, potentially multiple times in a single location. For example:
2306
 
+
2307
 
+<geshi lang="nginx">
2308
 
+  location /timed_sleep {
2309
 
+      echo_sleep 0.03;
2310
 
+      echo "$echo_timer_elapsed sec elapsed.";
2311
 
+
2312
 
+      echo_reset_timer;
2313
 
+
2314
 
+      echo_sleep 0.02;
2315
 
+      echo "$echo_timer_elapsed sec elapsed.";
2316
 
+  }
2317
 
+</geshi>
2318
 
+
2319
 
+The output on the client side might be
2320
 
+
2321
 
+<geshi lang="bash">
2322
 
+    $ curl 'http://localhost/timed_sleep'
2323
 
+    0.032 sec elapsed.
2324
 
+    0.020 sec elapsed.
2325
 
+</geshi>
2326
 
+
2327
 
+The actual figures you get on your side may vary a bit due to your system's current activities.
2328
 
+
2329
 
+Invocation of this directive will force the underlying Nginx timer to get updated to the current system time (regardless the timer resolution specified elsewhere in the config file). Furthermore, references of the [[#$echo_timer_elapsed|$echo_timer_elapsed]] variable will also trigger timer update forcibly.
2330
 
+
2331
 
+See also [[#echo_sleep|echo_sleep]] and [[#$echo_timer_elapsed|$echo_timer_elapsed]].
2332
 
+
2333
 
+== echo_read_request_body ==
2334
 
+
2335
 
+Explicitly reads request body so that the [[NginxHttpCoreModule#$request_body|$request_body]] variable will always have non-empty values (unless the body is so big that it has been saved by Nginx to a local temporary file).
2336
 
+
2337
 
+Note that this might not be the original client request body because the current request might be a subrequest with a "artificial" body specified by its parent.
2338
 
+
2339
 
+This directive does not generate any output itself, just like [[#echo_sleep|echo_sleep]].
2340
 
+
2341
 
+Here's an example for echo'ing back the original HTTP client request (both headers and body are included):
2342
 
+
2343
 
+<geshi lang="nginx">
2344
 
+  location /echoback {
2345
 
+    echo_duplicate 1 $echo_client_request_headers;
2346
 
+    echo "\r";
2347
 
+    echo_read_request_body;
2348
 
+    echo $request_body;
2349
 
+  }
2350
 
+</geshi>
2351
 
+
2352
 
+The content of <code>/echoback</code> looks like this on my side (I was using Perl's LWP utility to access this location on the server):
2353
 
+
2354
 
+<geshi lang="bash">
2355
 
+  $ (echo hello; echo world) | lwp-request -m POST 'http://localhost/echoback'
2356
 
+  POST /echoback HTTP/1.1
2357
 
+  TE: deflate,gzip;q=0.3
2358
 
+  Connection: TE, close
2359
 
+  Host: localhost
2360
 
+  User-Agent: lwp-request/5.818 libwww-perl/5.820
2361
 
+  Content-Length: 12
2362
 
+  Content-Type: application/x-www-form-urlencoded
2363
 
+
2364
 
+  hello
2365
 
+  world
2366
 
+</geshi>
2367
 
+
2368
 
+Because <code>/echoback</code> is the main request, [[NginxHttpCoreModule#$request_body|$request_body]] holds the original client request body.
2369
 
+
2370
 
+Before Nginx 0.7.56, it makes no sense to use this directive because [[NginxHttpCoreModule#$request_body|$request_body]] was first introduced in Nginx 0.7.58.
2371
 
+
2372
 
+This directive itself was first introduced in the echo module's [[#v0.14|v0.14 release]].
2373
 
+
2374
 
+== echo_location_async ==
2375
 
+'''syntax:''' ''echo_location_async <location> [<url_args>]''
2376
 
+
2377
 
+'''default:''' ''no''
2378
 
+
2379
 
+'''context:''' ''location''
2380
 
+
2381
 
+Issue GET subrequest to the location specified (first argument) with optional url arguments specified in the second argument.
2382
 
+
2383
 
+As of Nginx 0.8.20, the <code>location</code> argument does ''not'' support named location, due to a limitation in the <code>ngx_http_subrequest</code> function. The same is true for its brother, the [[#echo_location|echo_location]] directive.
2384
 
+
2385
 
+A very simple example is
2386
 
+
2387
 
+<geshi lang="nginx">
2388
 
+    location /main {
2389
 
+        echo_location_async /sub;
2390
 
+        echo world;
2391
 
+    }
2392
 
+    location /sub {
2393
 
+        echo hello;
2394
 
+    }
2395
 
+</geshi>
2396
 
+
2397
 
+Accessing <code>/main</code> gets
2398
 
+
2399
 
+  hello
2400
 
+  world
2401
 
+
2402
 
+Calling multiple locations in parallel is also possible:
2403
 
+
2404
 
+<geshi lang="nginx">
2405
 
+    location /main {
2406
 
+        echo_reset_timer;
2407
 
+        echo_location_async /sub1;
2408
 
+        echo_location_async /sub2;
2409
 
+        echo "took $echo_timer_elapsed sec for total.";
2410
 
+    }
2411
 
+    location /sub1 {
2412
 
+        echo_sleep 2; # sleeps 2 sec
2413
 
+        echo hello;
2414
 
+    }
2415
 
+    location /sub2 {
2416
 
+        echo_sleep 1; # sleeps 1 sec
2417
 
+        echo world;
2418
 
+    }
2419
 
+</geshi>
2420
 
+
2421
 
+Accessing <code>/main</code> yields
2422
 
+
2423
 
+<geshi lang="bash">
2424
 
+  $ time curl 'http://localhost/main'
2425
 
+  hello
2426
 
+  world
2427
 
+  took 0.000 sec for total.
2428
 
+
2429
 
+  real 0m2.006s
2430
 
+  user 0m0.000s
2431
 
+  sys  0m0.004s
2432
 
+</geshi>
2433
 
+
2434
 
+You can see that the main handler <code>/main</code> does ''not'' wait the subrequests <code>/sub1</code> and <code>/sub2</code> to complete and quickly goes on, hence the "0.000 sec" timing result. The whole request, however takes approximately 2 sec in total to complete because <code>/sub1</code> and <code>/sub2</code> run in parallel (or "concurrently" to be more accurate).
2435
 
+
2436
 
+If you use [[#echo_blocking_sleep|echo_blocking_sleep]] in the previous example instead, then you'll get the same output, but with 3 sec total response time, because "blocking sleep" blocks the whole Nginx worker process.
2437
 
+
2438
 
+Locations can also take an optional querystring argument, for instance
2439
 
+
2440
 
+<geshi lang="nginx">
2441
 
+    location /main {
2442
 
+        echo_location_async /sub 'foo=Foo&bar=Bar';
2443
 
+    }
2444
 
+    location /sub {
2445
 
+        echo $arg_foo $arg_bar;
2446
 
+    }
2447
 
+</geshi>
2448
 
+
2449
 
+Accessing <code>/main</code> yields
2450
 
+
2451
 
+<geshi lang="bash">
2452
 
+  $ curl 'http://localhost/main'
2453
 
+  Foo Bar
2454
 
+</geshi>
2455
 
+
2456
 
+Querystrings is ''not'' allowed to be concatenated onto the <code>location</code> argument with "?" directly, for example, <code>/sub?foo=Foo&bar=Bar</code> is an invalid location, and shouldn't be fed as the first argument to this directive.
2457
 
+
2458
 
+Due to an unknown bug in Nginx (it still exists in Nginx 0.8.20), the [[NginxHttpSsiModule|standard SSI module]] is required to ensure that the contents of the subrequests issued by this directive are correctly merged into the output chains of the main one. Fortunately, the SSI module is enabled by default during Nginx's <code>configure</code> process.
2459
 
+
2460
 
+If calling this directive without SSI module enabled, you'll get truncated response without contents of any subrequests and get an alert message in your Nginx's <code>error.log</code>, like this:
2461
 
+
2462
 
+  [alert] 24212#0: *1 the http output chain is empty, client: 127.0.0.1, ...
2463
 
+
2464
 
+Technically speaking, this directive is an example that Nginx content handler issues one or more subrequests directly. AFAIK, the [https://connectical.com/projects/ngx-fancyindex/wiki fancyindex module] also does such kind of things ;)
2465
 
+
2466
 
+Nginx named locations like <code>@foo</code> is ''not'' supported here.
2467
 
+
2468
 
+This directive is logically equivalent to the GET version of [[#echo_subrequest_async|echo_subrequest_async]]. For example,
2469
 
+
2470
 
+  echo_location_async /foo 'bar=Bar';
2471
 
+
2472
 
+is logically equivalent to
2473
 
+
2474
 
+  echo_subrequest_async GET /foo -q 'bar=Bar';
2475
 
+
2476
 
+But calling this directive is slightly faster than calling [[#echo_subrequest_async|echo_subrequest_async]] using <code>GET</code> because we don't have to parse the HTTP method names like <code>GET</code> and options like <code>-q</code>.
2477
 
+
2478
 
+This directive is first introduced in [[#v0.09|version 0.09]] of this module and requires at least Nginx 0.7.46.
2479
 
+
2480
 
+== echo_location ==
2481
 
+'''syntax:''' ''echo_location <location> [<url_args>]''
2482
 
+
2483
 
+'''default:''' ''no''
2484
 
+
2485
 
+'''context:''' ''location''
2486
 
+
2487
 
+Just like the [[#echo_location_async|echo_location_async]] directive, but <code>echo_location</code> issues subrequests ''in series'' rather than in parallel. That is, the content handler directives following this directive won't be executed until the subrequest issued by this directive completes.
2488
 
+
2489
 
+The final response body is almost always equivalent to the case when [[#echo_location_async|echo_location_async]] is used instead, only if timing variables is used in the outputs.
2490
 
+
2491
 
+Consider the following example:
2492
 
+
2493
 
+    location /main {
2494
 
+        echo_reset_timer;
2495
 
+        echo_location /sub1;
2496
 
+        echo_location /sub2;
2497
 
+        echo "took $echo_timer_elapsed sec for total.";
2498
 
+    }
2499
 
+    location /sub1 {
2500
 
+        echo_sleep 2;
2501
 
+        echo hello;
2502
 
+    }
2503
 
+    location /sub2 {
2504
 
+        echo_sleep 1;
2505
 
+        echo world;
2506
 
+    }
2507
 
+
2508
 
+The location <code>/main</code> above will take for total 3 sec to complete (compared to 2 sec if [[#echo_location_async|echo_location_async]] is used instead here). Here's the result in action on my machine:
2509
 
+
2510
 
+  $ curl 'http://localhost/main'
2511
 
+  hello
2512
 
+  world
2513
 
+  took 3.003 sec for total.
2514
 
+
2515
 
+  real 0m3.027s
2516
 
+  user 0m0.020s
2517
 
+  sys  0m0.004s
2518
 
+
2519
 
+This directive is logically equivalent to the GET version of [[#echo_subrequest|echo_subrequest]]. For example,
2520
 
+
2521
 
+  echo_location /foo 'bar=Bar';
2522
 
+
2523
 
+is logically equivalent to
2524
 
+
2525
 
+  echo_subrequest GET /foo -q 'bar=Bar';
2526
 
+
2527
 
+But calling this directive is slightly faster than calling [[#echo_subrequest|echo_subrequest]] using <code>GET</code> because we don't have to parse the HTTP method names like <code>GET</code> and options like <code>-q</code>.
2528
 
+
2529
 
+Behind the scene, it creates an <code>ngx_http_post_subrequest_t</code> object as a ''continuation'' and passes it into the <code>ngx_http_subrequest</code> function call. Nginx will later reopen this "continuation" in the subrequest's <code>ngx_http_finalize_request</code> function call. We resumes the execution of the parent-request's content handler and starts to run the next directive (command) if any.
2530
 
+
2531
 
+Nginx named locations like <code>@foo</code> is ''not'' supported here.
2532
 
+
2533
 
+This directive was first introduced in the [[#v0.12|release v0.12]].
2534
 
+
2535
 
+See also [[#echo_location_async|echo_location_async]] for more details about the meaning of the arguments.
2536
 
+
2537
 
+== echo_subrequest_async ==
2538
 
+'''syntax:''' ''echo_subrequest_async <HTTP_method> <location> [-q <url_args>] [-b <request_body>]''
2539
 
+
2540
 
+'''default:''' ''no''
2541
 
+
2542
 
+'''context:''' ''location''
2543
 
+
2544
 
+Initiate an asynchronous subrequest using HTTP method, an optional url arguments (or querystring), and an option request body.
2545
 
+
2546
 
+This directive is very much like a generalized version of the [[#echo_location_async|echo_location_async]] directive.
2547
 
+
2548
 
+Here's a small example demonstrating its usage:
2549
 
+
2550
 
+<geshi lang="nginx">
2551
 
+    location /multi {
2552
 
+        echo_subrequest_async POST '/sub' -q 'foo=Foo' -b 'hi';
2553
 
+        echo_subrequest_async PUT '/sub' -q 'bar=Bar' -b 'hello';
2554
 
+    }
2555
 
+    location /sub {
2556
 
+        echo "querystring: $query_string";
2557
 
+        echo "method: $echo_request_method";
2558
 
+        echo "body: $echo_request_body";
2559
 
+        echo "content length: $http_content_length";
2560
 
+        echo '///';
2561
 
+    }
2562
 
+</geshi>
2563
 
+
2564
 
+Then on the client side:
2565
 
+
2566
 
+<geshi lang="bash">
2567
 
+  $ curl 'http://localhost/multi'
2568
 
+  querystring: foo=Foo
2569
 
+  method: POST
2570
 
+  body: hi
2571
 
+  content length: 2
2572
 
+  ///
2573
 
+  querystring: bar=Bar
2574
 
+  method: PUT
2575
 
+  body: hello
2576
 
+  content length: 5
2577
 
+  ///
2578
 
+</geshi>
2579
 
+
2580
 
+Here's more funny example using the standard [[#NginxHttpProxyModule|proxy module]] to handle the subrequest:
2581
 
+
2582
 
+<geshi lang="nginx">
2583
 
+    location /main {
2584
 
+        echo_subrequest_async POST /sub -b 'hello, world';
2585
 
+    }
2586
 
+    location /sub {
2587
 
+        proxy_pass $scheme://127.0.0.1:$server_port/proxied;
2588
 
+    }
2589
 
+    location /proxied {
2590
 
+        echo "method: $echo_request_method.";
2591
 
+
2592
 
+        # we need to read body explicitly here...or $echo_request_body
2593
 
+        #   will evaluate to empty ("")
2594
 
+        echo_read_request_body;
2595
 
+
2596
 
+        echo "body: $echo_request_body.";
2597
 
+    }
2598
 
+</geshi>
2599
 
+
2600
 
+Then on the client side, we can see that
2601
 
+
2602
 
+<geshi lang="bash">
2603
 
+  $ curl 'http://localhost/main'
2604
 
+  method: POST.
2605
 
+  body: hello, world.
2606
 
+</geshi>
2607
 
+
2608
 
+Nginx named locations like <code>@foo</code> is ''not'' supported here.
2609
 
+
2610
 
+This directive was first introduced in the [[#v0.15|release v0.15]].
2611
 
+
2612
 
+See also the [[#echo_subrequest|echo_subrequest]] and [[#echo_location_async|echo_location_async]] directives.
2613
 
+
2614
 
+== echo_subrequest ==
2615
 
+'''syntax:''' ''echo_subrequest_async <HTTP_method> <location> [-q <url_args>] [-b <request_body>]''
2616
 
+
2617
 
+'''default:''' ''no''
2618
 
+
2619
 
+'''context:''' ''location''
2620
 
+
2621
 
+This is the synchronous version of the [[#echo_subrequest_async|echo_subrequest_async]] directive. And just like [[#echo_location|echo_location]], it does not block the Nginx worker process (while [[#echo_blocking_sleep|echo_blocking_sleep]] does), rather, it uses continuation to pass control along the subrequest chain.
2622
 
+
2623
 
+See [[#echo_subrequest_async|echo_subrequest_async]] for more details.
2624
 
+
2625
 
+Nginx named locations like <code>@foo</code> is ''not'' supported here.
2626
 
+
2627
 
+This directive was first introduced in the [[#v0.15|release v0.15]].
2628
 
+
2629
 
+== echo_foreach_split ==
2630
 
+'''syntax:''' ''echo_foreach_split <delimiter> <string>''
2631
 
+
2632
 
+'''default:''' ''no''
2633
 
+
2634
 
+'''context:''' ''location''
2635
 
+
2636
 
+Split the second argument <code>string</code> using the delimiter specified in the first argument, and then iterate through the resulting items. For instance:
2637
 
+
2638
 
+<geshi lang="nginx">
2639
 
+  location /loop {
2640
 
+    echo_foreach_split ',' $arg_list;
2641
 
+      echo "item: $echo_it";
2642
 
+    echo_end;
2643
 
+  }
2644
 
+</geshi>
2645
 
+
2646
 
+Accessing /main yields
2647
 
+
2648
 
+<geshi lang="bash">
2649
 
+  $ curl 'http://localhost/loop?list=cat,dog,mouse'
2650
 
+  item: cat
2651
 
+  item: dog
2652
 
+  item: mouse
2653
 
+</geshi>
2654
 
+
2655
 
+As seen in the previous example, this directive should always be accompanied by an [[#echo_end|echo_end]] directive.
2656
 
+
2657
 
+Parallel <code>echo_foreach_split</code> loops are allowed, but nested ones are currently forbidden.
2658
 
+
2659
 
+The <code>delimiter</code> argument could contain ''multiple'' arbitrary characters, like
2660
 
+
2661
 
+<geshi lang="nginx">
2662
 
+  echo_foreach_split '-a-' 'cat-a-dog-a-mouse';
2663
 
+    echo $echo_it;
2664
 
+  echo_end;
2665
 
+</geshi>
2666
 
+
2667
 
+Logically speaking, this looping structure is just the <code>foreach</code> loop combined with a <code>split</code> function call in Perl (using the previous example):
2668
 
+
2669
 
+<geshi lang="perl">
2670
 
+   foreach (split ',', $arg_list) {
2671
 
+       print "item $_\n";
2672
 
+   }
2673
 
+</geshi>
2674
 
+
2675
 
+People will also find it useful in merging multiple <code>.js</code> or <code>.css</code> resources into a whole. Here's an example:
2676
 
+
2677
 
+<geshi lang="nginx">
2678
 
+  location /merge {
2679
 
+      default_type 'text/javascript';
2680
 
+
2681
 
+      echo_foreach_split '&' $query_string;
2682
 
+          echo "/* JS File $echo_it */";
2683
 
+          echo_location_async $echo_it;
2684
 
+          echo;
2685
 
+      echo_end;
2686
 
+  }
2687
 
+</geshi>
2688
 
+
2689
 
+Then accessing /merge to merge the <code>.js</code> resources specified in the query string:
2690
 
+
2691
 
+<geshi lang="bash">
2692
 
+  $ curl 'http://localhost/merge?/foo/bar.js&/yui/blah.js&/baz.js'
2693
 
+</geshi>
2694
 
+
2695
 
+One can also use third-party Nginx cache module to cache the merged response generated by the <code>/merge</code> location in the previous example.
2696
 
+
2697
 
+This directive was first introduced in the [[#v0.17|release v0.17]].
2698
 
+
2699
 
+== echo_end ==
2700
 
+'''syntax:''' ''echo_end''
2701
 
+
2702
 
+'''default:''' ''no''
2703
 
+
2704
 
+'''context:''' ''location''
2705
 
+
2706
 
+This directive is used to terminate the body of looping and conditional control structures like [[#echo_foreach_split|echo_foreach_split]].
2707
 
+
2708
 
+This directive was first introduced in the [[#v0.17|release v0.17]].
2709
 
+
2710
 
+== echo_request_body ==
2711
 
+'''syntax:''' ''echo_request_body''
2712
 
+
2713
 
+'''default:''' ''no''
2714
 
+
2715
 
+'''context:''' ''location''
2716
 
+
2717
 
+Outputs the contents of the request body previous read.
2718
 
+
2719
 
+Behind the scene, it's implemented roughly like this:
2720
 
+
2721
 
+<geshi lang="C">
2722
 
+  if (r->request_body && r->request_body->bufs) {
2723
 
+      return ngx_http_output_filter(r, r->request_body->bufs);
2724
 
+  }
2725
 
+</geshi>
2726
 
+
2727
 
+Unlike the [[#$echo_request_body|$echo_request_body]] and $request_body variables, this directive will show the whole request body even if some parts or all parts of it are saved in temporary files on the disk.
2728
 
+
2729
 
+It is a "no-op" if no request body has been read yet.
2730
 
+
2731
 
+This directive was first introduced in the [[#v0.18|release v0.18]].
2732
 
+
2733
 
+See also [[#echo_read_request_body|echo_read_request_body]] and the [[NginxHttpChunkinModule|chunkin module]].
2734
 
+
2735
 
+== echo_exec ==
2736
 
+'''syntax:''' ''echo_exec <location> [<query_string>]''
2737
 
+
2738
 
+'''syntax:''' ''echo_exec <named_location>''
2739
 
+
2740
 
+'''default:''' ''no''
2741
 
+
2742
 
+'''context:''' ''location''
2743
 
+
2744
 
+Does an internal redirect to the location specified. An optional query string can be specified for normal locations, as in
2745
 
+
2746
 
+<geshi lang="nginx">
2747
 
+  location /foo {
2748
 
+      echo_exec /bar weight=5;
2749
 
+  }
2750
 
+  location /bar {
2751
 
+      echo $arg_weight;
2752
 
+  }
2753
 
+</geshi>
2754
 
+
2755
 
+Or equivalently
2756
 
+
2757
 
+<geshi lang="nginx">
2758
 
+  location /foo {
2759
 
+      echo_exec /bar?weight=5;
2760
 
+  }
2761
 
+  location /bar {
2762
 
+      echo $arg_weight;
2763
 
+  }
2764
 
+</geshi>
2765
 
+
2766
 
+Named locations are also supported. Here's an example:
2767
 
+
2768
 
+<geshi lang="nginx">
2769
 
+  location /foo {
2770
 
+      echo_exec @bar;
2771
 
+  }
2772
 
+  location @bar {
2773
 
+      # you'll get /foo rather than @bar
2774
 
+      #  due to a potential bug in nginx.
2775
 
+      echo $echo_request_uri;
2776
 
+  }
2777
 
+</geshi>
2778
 
+
2779
 
+But query string (if any) will always be ignored for named location redirects due to a limitation in the <code>ngx_http_named_location</code> function.
2780
 
+
2781
 
+Never try to echo things before the <code>echo_exec</code> directive or you won't see the proper response of the location you want to redirect to. Because any echoing will cause the original location handler to send HTTP headers before the redirection happens.
2782
 
+
2783
 
+Technically speaking, this directive exposes the Nginx internal API functions <code>ngx_http_internal_redirect</code> and <code>ngx_http_named_location</code>.
2784
 
+
2785
 
+This directive was first introduced in the [[#v0.21|v0.21 release]].
2786
 
+
2787
 
+= Filter Directives =
2788
 
+
2789
 
+Use of the following directives trigger the filter registration of this module. By default, no filter will be registered by this module.
2790
 
+
2791
 
+Every filter directive supports variable interpolation in its arguments (if any).
2792
 
+
2793
 
+== echo_before_body ==
2794
 
+'''syntax:''' ''echo_before_body [options] [argument]...''
2795
 
+
2796
 
+'''default:''' ''no''
2797
 
+
2798
 
+'''context:''' ''location''
2799
 
+
2800
 
+It's the filter version of the [[#echo|echo]] directive, and prepends its output to the beginning of the original outputs generated by the underlying content handler.
2801
 
+
2802
 
+An example is
2803
 
+
2804
 
+<geshi lang="nginx">
2805
 
+    location /echo {
2806
 
+        echo_before_body hello;
2807
 
+        proxy_pass $scheme://127.0.0.1:$server_port$request_uri/more;
2808
 
+    }
2809
 
+    location /echo/more {
2810
 
+        echo world
2811
 
+    }
2812
 
+</geshi>
2813
 
+
2814
 
+Accessing <code>/echo</code> from the client side yields
2815
 
+
2816
 
+  hello
2817
 
+  world
2818
 
+
2819
 
+In the previous sample, we borrow the [[NginxHttpProxyModule|standard proxy module]] to serve as the underlying content handler that generates the "main contents".
2820
 
+
2821
 
+Multiple instances of this filter directive are also allowed, as in:
2822
 
+
2823
 
+<geshi lang="nginx">
2824
 
+    location /echo {
2825
 
+        echo_before_body hello;
2826
 
+        echo_before_body world;
2827
 
+        echo !;
2828
 
+    }
2829
 
+</geshi>
2830
 
+
2831
 
+On the client side, the output is like
2832
 
+
2833
 
+<geshi lang="bash">
2834
 
+  $ curl 'http://localhost/echo'
2835
 
+  hello
2836
 
+  world
2837
 
+  !
2838
 
+</geshi>
2839
 
+
2840
 
+In this example, we also use the [[#Content Handler Directives|content handler directives]] provided by this module as the underlying content handler.
2841
 
+
2842
 
+This directive also supports the <code>-n</code> and <code>--</code> options like the [[#echo|echo]] directive.
2843
 
+
2844
 
+This directive can be mixed with its brother directive [[#echo_after_body|echo_after_body]].
2845
 
+
2846
 
+== echo_after_body ==
2847
 
+'''syntax:''' ''echo_after_body [argument]...''
2848
 
+
2849
 
+'''default:''' ''no''
2850
 
+
2851
 
+'''context:''' ''location''
2852
 
+
2853
 
+'''WARNING''' this directive does not work for nginx >= 0.7.65.
2854
 
+
2855
 
+It's very much like the [[#echo_before_body|echo_before_body]] directive, but ''appends'' its output to the end of the original outputs generated by the underlying content handler.
2856
 
+
2857
 
+Here's a simple example:
2858
 
+
2859
 
+<geshi lang="nginx">
2860
 
+    location /echo {
2861
 
+        echo_after_body hello;
2862
 
+        proxy_pass http://127.0.0.1:$server_port$request_uri/more;
2863
 
+    }
2864
 
+    location /echo/more {
2865
 
+        echo world
2866
 
+    }
2867
 
+</geshi>
2868
 
+
2869
 
+Accessing <code>/echo</code> from the client side yields
2870
 
+
2871
 
+  world
2872
 
+  hello
2873
 
+
2874
 
+Multiple instances are allowed, as in:
2875
 
+
2876
 
+<geshi lang="nginx">
2877
 
+    location /echo {
2878
 
+        echo_after_body hello;
2879
 
+        echo_after_body world;
2880
 
+        echo i;
2881
 
+        echo say;
2882
 
+    }
2883
 
+</geshi>
2884
 
+
2885
 
+The output on the client side while accessing the <code>/echo</code> location looks like
2886
 
+
2887
 
+<geshi lang="bash">
2888
 
+  i
2889
 
+  say
2890
 
+  hello
2891
 
+  world
2892
 
+</geshi>
2893
 
+
2894
 
+This directive also supports the <code>-n</code> and <code>--</code> options like the [[#echo|echo]] directive.
2895
 
+
2896
 
+When this directive is used in a location accessed by a subrequest, it replies on the <code>sync</code> flag set in a chain buffer to indicate the end of the output for nginx >= 0.8.7. This is a hack because Nginx does not provide a reliable way to determine the end of the output chain in a subrequest's output filter. Use it in subrequests with care.
2897
 
+
2898
 
+This directive can be mixed with its brother directive [[#echo_before_body|echo_before_body]].
2899
 
+
2900
 
+= Variables =
2901
 
+
2902
 
+== $echo_it ==
2903
 
+
2904
 
+This is a "topic variable" used by [[#echo_foreach_split|echo_foreach_split]], just like the <code>$_</code> variable in Perl.
2905
 
+
2906
 
+== $echo_timer_elapsed ==
2907
 
+
2908
 
+This variable holds the seconds elapsed since the start of the current request (might be a subrequest though) or the last invocation of the [[#echo_reset_timer|echo_reset_timer]] command.
2909
 
+
2910
 
+The timing result takes three digits after the decimal point.
2911
 
+
2912
 
+References of this variable will force the underlying Nginx timer to update to the current system time, regardless the timer resolution settings elsewhere in the config file, just like the [[#echo_reset_timer|echo_reset_timer]] directive.
2913
 
+
2914
 
+== $echo_request_body ==
2915
 
+
2916
 
+Evaluates to the current (sub)request's request body previously read if no part of the body has been saved to a temporary file. To always show the request body even if it's very large, use the [[#echo_request_body|echo_request_body]] directive.
2917
 
+
2918
 
+== $echo_request_method ==
2919
 
+
2920
 
+Evaluates to the HTTP request method of the current request (it can be a subrequest).
2921
 
+
2922
 
+Behind the scene, it just takes the string data stored in <code>r->method_name</code>.
2923
 
+
2924
 
+Compare it to the [[#$echo_client_request_method|$echo_client_request_method]] variable.
2925
 
+
2926
 
+At least for Nginx 0.8.20 and older, the [[NginxHttpCoreModule#$request_method|$request_method]] variable provided by the [[NginxHttpCoreModule|http core module]] is actually doing what our [[#$echo_client_request_method|$echo_client_request_method]] is doing.
2927
 
+
2928
 
+This variable was first introduced in our [[#v0.15|v0.15 release]].
2929
 
+
2930
 
+== $echo_client_request_method ==
2931
 
+
2932
 
+Always evaluates to the main request's HTTP method even if the current request is a subrequest.
2933
 
+
2934
 
+Behind the scene, it just takes the string data stored in <code>r->main->method_name</code>.
2935
 
+
2936
 
+Compare it to the [[#$echo_request_method|$echo_request_method]] variable.
2937
 
+
2938
 
+This variable was first introduced in our [[#v0.15|v0.15 release]].
2939
 
+
2940
 
+== $echo_client_request_headers ==
2941
 
+
2942
 
+Evaluates to the original client request's headers.
2943
 
+
2944
 
+Just as the name suggests, it will always take the main request (or the client request) even if it's currently executed in a subrequest.
2945
 
+
2946
 
+A simple example is below:
2947
 
+
2948
 
+<geshi lang="nginx">
2949
 
+  location /echoback {
2950
 
+     echo "headers are:"
2951
 
+     echo $echo_client_request_headers;
2952
 
+  }
2953
 
+</geshi>
2954
 
+
2955
 
+Accessing <code>/echoback</code> yields
2956
 
+
2957
 
+<geshi lang="bash">
2958
 
+  $ curl 'http://localhost/echoback'
2959
 
+  headers are
2960
 
+  GET /echoback HTTP/1.1
2961
 
+  User-Agent: curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g
2962
 
+  Host: localhost:1984
2963
 
+  Accept: */*
2964
 
+</geshi>
2965
 
+
2966
 
+Behind the scene, it recovers <code>r->main->header_in</code> on the C level and does not construct the headers itself by traversing parsed results in the request object, and strips the last (trailing) CRLF.
2967
 
+
2968
 
+This variable was first introduced in [[#v0.15|version 0.15]].
2969
 
+
2970
 
+== $echo_cacheable_request_uri ==
2971
 
+
2972
 
+Evaluates to the parsed form of the URI (usually led by <code>/</code>) of the current (sub-)request. Unlike the [[#$echo_request_uri|$echo_request_uri]] variable, it is cacheable.
2973
 
+
2974
 
+See [[#$echo_request_uri|$echo_request_uri]] for more details.
2975
 
+
2976
 
+This variable was first introduced in [[#v0.17|version 0.17]].
2977
 
+
2978
 
+== $echo_request_uri ==
2979
 
+
2980
 
+Evaluates to the parsed form of the URI (usually led by <code>/</code>) of the current (sub-)request. Unlike the [[#$echo_cacheable_request_uri|$echo_cacheable_request_uri]] variable, it is ''not'' cacheable.
2981
 
+
2982
 
+This is quite different from the [[NginxHttpCoreModule#$request_uri|$request_uri]] variable exported by the [[NginxHttpCoreModule]], because <code>$request_uri</code> is the ''unparsed'' form of the current request's URI.
2983
 
+
2984
 
+This variable was first introduced in [[#v0.17|version 0.17]].
2985
 
+
2986
 
+== $echo_incr ==
2987
 
+
2988
 
+It is a counter that always generate the current counting number, starting from 1. The counter is always associated with the main request even if it is accessed within a subrequest.
2989
 
+
2990
 
+Consider the following example
2991
 
+
2992
 
+<geshi lang="Nginx">
2993
 
+    location /main {
2994
 
+        echo "main pre: $echo_incr";
2995
 
+        echo_location_async /sub;
2996
 
+        echo_location_async /sub;
2997
 
+        echo "main post: $echo_incr";
2998
 
+    }
2999
 
+    location /sub {
3000
 
+        echo "sub: $echo_incr";
3001
 
+    }
3002
 
+</geshi>
3003
 
+
3004
 
+Accessing <code>/main</code> yields
3005
 
+
3006
 
+    main pre: 1
3007
 
+    sub: 3
3008
 
+    sub: 4
3009
 
+    main post: 2
3010
 
+
3011
 
+This directive was first introduced in the [[#v0.18|v0.18 release]].
3012
 
+
3013
 
+== $echo_response_status ==
3014
 
+
3015
 
+Evaluates to the status code of the current (sub)request, null if not any.
3016
 
+
3017
 
+Behind the scene, it's just the textual representation of <code>r->headers_out->status</code>.
3018
 
+
3019
 
+This directive was first introduced in the [[#v0.23|v0.23 release]].
3020
 
+
3021
 
+= Installation =
3022
 
+
3023
 
+Grab the nginx source code from [http://nginx.net/ nginx.net], for example,
3024
 
+the version 0.8.41 (see [[#Compatibility|nginx compatibility]]), and then build the source with this module:
3025
 
+
3026
 
+<geshi lang="bash">
3027
 
+    $ wget 'http://sysoev.ru/nginx/nginx-0.8.41.tar.gz'
3028
 
+    $ tar -xzvf nginx-0.8.41.tar.gz
3029
 
+    $ cd nginx-0.8.41/
3030
 
+
3031
 
+    # Here we assume you would install you nginx under /opt/nginx/.
3032
 
+    $ ./configure --prefix=/opt/nginx \
3033
 
+        --add-module=/path/to/echo-nginx-module
3034
 
+
3035
 
+    $ make -j2
3036
 
+    $ make install
3037
 
+</geshi>
3038
 
+
3039
 
+Download the latest version of the release tarball of this module from [http://github.com/agentzh/echo-nginx-module/downloads echo-nginx-module file list].
3040
 
+
3041
 
+= Compatibility =
3042
 
+
3043
 
+The following versions of Nginx should work with this module:
3044
 
+
3045
 
+* '''0.8.x'''                       (last tested version is 0.8.40)
3046
 
+* '''0.7.x >= 0.7.21'''             (last tested version is 0.7.66)
3047
 
+
3048
 
+In particular,
3049
 
+
3050
 
+* the directive [[#echo_location_async|echo_location_async]] and its brother [[#echo_subrequest_async|echo_subrequest_async]] do ''not'' work with '''0.7.x < 0.7.46'''.
3051
 
+* the [[#echo_after_body|echo_after_body]] directive does ''not'' work at all with nginx '''< 0.8.7'''.
3052
 
+* the [[#echo_sleep|echo_sleep]] directive cannot be used after [[#echo_location|echo_location]] or [[#echo_subrequest|echo_subrequest]] for nginx '''< 0.8.11'''.
3053
 
+
3054
 
+Earlier versions of Nginx like 0.6.x and 0.5.x will ''not'' work at all.
3055
 
+
3056
 
+If you find that any particular version of Nginx above 0.7.21 does not work with this module, please consider [[#Report Bugs|reporting a bug]].
3057
 
+
3058
 
+= Modules that use this module for testing =
3059
 
+
3060
 
+The following modules take advantage of this <code>echo</code> module in their test suite:
3061
 
+
3062
 
+* The [[NginxHttpMemcModule|memc]] module that supports almost the whole memcached TCP protocol.
3063
 
+* The [[NginxHttpChunkinModule|chunkin]] module that adds HTTP 1.1 chunked input support to Nginx.
3064
 
+* The [[NginxHttpHeadersMoreModule|headers_more]] module that allows you to add, set, and clear input and output headers under the conditions that you specify.
3065
 
+* The <code>echo</code> module itself.
3066
 
+
3067
 
+Please mail me other modules that use <code>echo</code> in any form and I'll add them to the list above :)
3068
 
+
3069
 
+= Report Bugs =
3070
 
+
3071
 
+Although a lot of effort has been put into testing and code tuning, there must be some serious bugs lurking somewhere in this module. So whenever you are bitten by any quirks, please don't hesitate to
3072
 
+
3073
 
+# send a bug report or even patches to <agentzh@gmail.com>,
3074
 
+# or create a ticket on the [http://github.com/agentzh/echo-nginx-module/issues issue tracking interface] provided by GitHub.
3075
 
+
3076
 
+= Source Repository =
3077
 
+
3078
 
+Available on github at [http://github.com/agentzh/echo-nginx-module agentzh/echo-nginx-module].
3079
 
+
3080
 
+= ChangeLog =
3081
 
+
3082
 
+== v0.34 ==
3083
 
+* we no longer use the problematic <code>ngx_strXcmp</code> macros in our source because it may cause invalid reads and thus segmentation faults. thanks Piotr Sikora.
3084
 
+
3085
 
+== v0.33 ==
3086
 
+* fixed compatibility with nginx 0.7.66+ because the ngx_time_update macro's parameter list has changed. Thanks Guang Feng (蔡镜明).
3087
 
+
3088
 
+== v0.32 ==
3089
 
+* we should have used <code>ngx_calloc_buf</code> instead of <code>ngx_alloc_buf</code> for the last chunk generated for [[#echo_after_body|echo_after_body]]. thanks valgrind's memcheck tool.
3090
 
+* we should initialize flags before feeding it into <code>ngx_http_parse_unsafe_uri</code>. thanks valgrind's memcheck tool.
3091
 
+* fixed a minor issue in the [[#echo_location|echo_location]]/[[#echo_subrequest|echo_subrequest]] implementation, which used to have race conditions.
3092
 
+
3093
 
+== v0.31 ==
3094
 
+
3095
 
+* the echo wev handler should not proceed if it is still waiting for some sequential subrequest or has just processed one to avoid bouncing issues.
3096
 
+* fixed a segfault for echo_exec for 0.7.x: we should check <code>r->done</code> before proceeding.
3097
 
+* no longer explicitly set <code>r->write_event_handler</code> to <code>ngx_http_request_empty_handler</code> because it's totally wrong for the state machine.
3098
 
+* fixed the sequential subrequest model bugs: we should ensure the <code>pr->write_event_handler</code> gets called immediately after the <code>post_subrequest</code> callback when the subrequest finalizes.
3099
 
+
3100
 
+== v0.30 ==
3101
 
+
3102
 
+* fixed the [[#echo_exec|echo_exec]] directive for nginx >= 0.8.11. we didn't get the <code>r->main->count</code> right in the previous version.
3103
 
+
3104
 
+== v0.29 ==
3105
 
+
3106
 
+* refactored the core of this module. now the implementation of [[#echo_location|echo_location]], [[#echo_subrequest|echo_subrequest]], [[#echo_sleep|echo_sleep]], and [[#echo_read_request_body|echo_read_request_body]] finally fit well with the nginx event model and Igor Sysoev's way of thinking.
3107
 
+
3108
 
+== v0.28 ==
3109
 
+
3110
 
+* added support for the <code>-n</code> and <code>--</code> options to the [[#echo|echo]], [[#echo_before_body|echo_before_body]], and [[#echo_after_body|echo_after_body]] directives.
3111
 
+
3112
 
+== v0.27 ==
3113
 
+
3114
 
+* applied the patch from Sergey A. Osokin to work with nginx 0.8.35.
3115
 
+
3116
 
+== v0.26 ==
3117
 
+
3118
 
+* bug fix: we should bypass upstream filters in our echo filters. an output filter should ever call <code>ngx_http_output_filter</code> nor <code>ngx_http_send_special</code>.
3119
 
+
3120
 
+== v0.25 ==
3121
 
+
3122
 
+* now we register a request cleanup handler to ensure our sleep event's timer will always get properly deleted even if the request is quit prematurely. this affects the echo_sleep directive.
3123
 
+* use ngx_null_string whenever possible in the source.
3124
 
+* sync'd the bundled test scaffold to Test::Nginx 0.07.
3125
 
+
3126
 
+== v0.24 ==
3127
 
+
3128
 
+* various source file name and coding style fixes. (the code now looks more like Igor Sysoev's.)
3129
 
+
3130
 
+== v0.23 ==
3131
 
+
3132
 
+* now the subrequest can read the client request body directly (for the main request) because we made subrequests inherit its parent's <code>r->header_in</code> as well. This affects the [[#echo_read_request_body|echo_read_request_body]] directive.
3133
 
+* fixed [[#echo_after_body|echo_after_body]] in subrequests by using a hack (checking <code>cl->buf->sync</code> for the last buf) for nginx 0.8.7+ only.
3134
 
+* added new varaible [[#$echo_response_status|$echo_response_status]] to help testing the status code of a subrequest. (The [[NginxHttpMemcModule|memc]] module makes use of it.)
3135
 
+* use the <code>ngx_calloc_buf</code> macro to allocate new bufs in the code rather than explicit <code>ngx_pcalloc</code> calls for safety.
3136
 
+
3137
 
+== v0.22 ==
3138
 
+
3139
 
+* Now we allowed all the directives appear in the [[NginxHttpRewriteModule|rewrite module]]'s [[NginxHttpRewriteModule#if|if]] block. But so far I've only tested the [[#echo|echo]] directive.
3140
 
+
3141
 
+== v0.21 ==
3142
 
+
3143
 
+* Added a new directive named [[#echo_exec|echo_exec]] which does internal redirect to other (named) locations.
3144
 
+
3145
 
+== v0.20 ==
3146
 
+
3147
 
+* Fixed a bug in [[#echo_sleep|echo_sleep]]'s <code>r->main->count</code> handling for nginx 0.8.x. This bug will cause the server to hang when proxing a location with [[#echo_sleep|echo_sleep]].
3148
 
+* Applied the <code>ngx_str3cmp</code>, <code>ngx_str4cmp</code>, and <code>ngx_str6cmp</code> optimizing macros to the <code>parse_method_name</code> function, as suggested by Marcus Clyne.
3149
 
+* Added [[#TODO|TODO items]] regarding <code>$echo_random</code> and <code>echo_repeat</code> suggested by Marcus Clyne.
3150
 
+
3151
 
+== v0.19 ==
3152
 
+* Fixed the CPS-style chained subrequest model for the [[#echo_location|echo_location]] and [[#echo_subrequest|echo_subrequest]] directives. they are now working perfectly and will not hang the server with the recent nginx 0.8.21 ~ 0.8.27 releases. To be specifically, the chained subrequest should call <code>ngx_http_finalize_request</code> on its parent request if the content handler of the parent request does not return <code>NGX_DONE</code>.
3153
 
+* Undeprecated the [[#echo_location|echo_location]] and [[#echo_subrequest|echo_subrequest]] directives.
3154
 
+
3155
 
+== v0.18 ==
3156
 
+* Fixed the "zero size buf in output" alerts in error.log.
3157
 
+* Added the new directive [[#echo_request_body|echo_request_body]].
3158
 
+* Now we use the <code>ngx_http_parse_unsafe_uri</code> function to check the locations to [[#echo_location_async|echo_location_async]] and its friends. Thanks Arvind Jayaprakash for suggesting this fix.
3159
 
+* Deprecated the [[#echo_location|echo_location]] and [[#echo_subrequest|echo_subrequest]] directives.
3160
 
+* For HTTP 1.0 clients, use the buf length of the first chain link as the output header Content-Length.
3161
 
+* Implemented new variable [[#$echo_incr|$echo_incr]].
3162
 
+
3163
 
+== v0.17 ==
3164
 
+* Added new directives [[#echo_foreach_split|echo_foreach_split]] and [[#echo_end|echo_end]]. Also introduced a "topic variable" named [[#$echo_it|$echo_it]].
3165
 
+* Added new variables [[#$echo_request_uri|$echo_request_uri]] and [[#$echo_cacheable_request_uri|$echo_cacheable_request_uri]].
3166
 
+
3167
 
+== v0.16 ==
3168
 
+* Now the subrequests issued by the [[#echo_location|echo_location_async]] and [[#echo_location|echo_location]] directives no longer inherit cached variable values from its parent request. (The underlying <code>ngx_http_subrequest</code> function, however, does automatic cachable variable value inheritance.)
3169
 
+* Added an undocumented variable ''echo_cached_request_uri'' to help testing of this module.
3170
 
+
3171
 
+== v0.15 ==
3172
 
+
3173
 
+* Added new directives [[#echo_subrequest|echo_subrequest]] and [[#echo_subrequest_async|echo_subrequest_async]] for the full nginx subrequest API.
3174
 
+* Removed the <code>echo_client_request_headers</code> directive, and provided the [[#$echo_client_request_headers|$echo_client_request_headers]] variable instead.
3175
 
+* Added new variables [[#$echo_request_method|$echo_request_method]] and [[#$echo_client_request_method|$echo_client_request_method]].
3176
 
+
3177
 
+== v0.14 ==
3178
 
+
3179
 
+* Added new directive [[#echo_read_request_body|echo_read_request_body]] to explicitly read client request body so that the [[NginxHttpCoreModule#$request_body]] variable will always have non-empty values.
3180
 
+* Now we shuffer test cases automatically in .t files and fixed bugs in the tests themselves which are hidden by config reload fallback in failure.
3181
 
+
3182
 
+== v0.13 ==
3183
 
+
3184
 
+* Fixed the special cases when the outputs of a [[#echo_duplicate|echo_duplicate]] directive is empty.
3185
 
+* Now we explicitly clear content length and accept ranges headers in the content handler.
3186
 
+
3187
 
+== v0.12 ==
3188
 
+
3189
 
+* Implemented the [[#echo_location|echo_location]] directive, which can issue chained GET subrequests in the Continuation Passing Style (CPS), rather than the parallel subrequest issued by the [[#echo_location_async|echo_location_async]] directive.
3190
 
+
3191
 
+== v0.11 ==
3192
 
+
3193
 
+* Implemented the [[#echo_duplicate|echo_duplicate]] directive to help generating large chunk of data for testing.
3194
 
+
3195
 
+== v0.10 ==
3196
 
+
3197
 
+* Fixed compilation regression against Nginx 0.7.21. This bug appears in version 0.09.
3198
 
+* Refactored the codebase by splitting source into various small files.
3199
 
+
3200
 
+== v0.09 ==
3201
 
+
3202
 
+* Reimplement the [[#echo_sleep|echo_sleep]] directive using per-request event and timer; the old implementation uses the global connection's read/write event to register timer, so it will break horribly when multiple subrequests "sleep" at the same time.
3203
 
+* Added the [[#echo_location_async|echo_location_async]] directive which can issue a GET subrequest and insert its contents herein.
3204
 
+
3205
 
+== v0.08 ==
3206
 
+
3207
 
+* [[#echo_sleep|echo_sleep]]: now we delete our <code>write event timer</code> in the <code>post_sleep</code> handle.
3208
 
+* Added <code>doc/manpage.wiki</code> which tracks changes in the [http://wiki.nginx.org/NginxHttpEchoModule wiki page].
3209
 
+* Added the <code>util/wiki2pod.pl</code> script to convert <code>doc/manpage.wiki</code> to <code>README</code>.
3210
 
+* Disabled the <code>DDEBUG</code> macro in the C source by default.
3211
 
+
3212
 
+= Test Suite =
3213
 
+
3214
 
+This module comes with a Perl-driven test suite. The [http://github.com/agentzh/echo-nginx-module/tree/master/test/t/ test cases] are
3215
 
+[http://github.com/agentzh/echo-nginx-module/blob/master/test/t/echo.t declarative] too. Thanks to the [http://search.cpan.org/perldoc?Test::Base Test::Base] module in the Perl world.
3216
 
+
3217
 
+To run it on your side:
3218
 
+
3219
 
+<geshi lang="bash">
3220
 
+    $ cd test
3221
 
+    $ PATH=/path/to/your/nginx-with-echo-module:$PATH prove -r t
3222
 
+</geshi>
3223
 
+
3224
 
+You need to terminate any Nginx processes before running the test suite if you have changed the Nginx server binary.
3225
 
+
3226
 
+At the moment, [http://search.cpan.org/perldoc?LWP::UserAgent LWP::UserAgent] is used by the [http://github.com/agentzh/echo-nginx-module/blob/master/test/lib/Test/Nginx/Echo.pm test scaffold] for simplicity and it's rather weak in testing ''streaming'' behavior of Nginx (I'm using "curl" to test these aspects manually for now). I'm considering coding up my own Perl HTTP client library based on [http://search.cpan.org/perldoc?IO::Select IO::Select] and [http://search.cpan.org/perldoc?IO::Socket IO::Socket] (there might be already one around?).
3227
 
+
3228
 
+Because a single nginx server (by default, <code>localhost:1984</code>) is used across all the test scripts (<code>.t</code> files), it's meaningless to run the test suite in parallel by specifying <code>-jN</code> when invoking the <code>prove</code> utility.
3229
 
+
3230
 
+Some parts of the test suite requires standard modules [[NginxHttpProxyModule|proxy]], [[NginxHttpRewriteModule|rewrite]] and [[NginxHttpSsiModule|SSI]] to be enabled as well when building Nginx.
3231
 
+
3232
 
+= TODO =
3233
 
+
3234
 
+* Fix the [[#echo_after_body|echo_after_body]] directive in subrequests.
3235
 
+* Add directives ''echo_read_client_request_body'' and ''echo_request_headers''.
3236
 
+* Add new directive ''echo_log'' to use Nginx's logging facility directly from the config file and specific loglevel can be specified, as in
3237
 
+
3238
 
+<geshi lang="nginx">
3239
 
+  echo_log debug "I am being called.";
3240
 
+</geshi>
3241
 
+
3242
 
+* Add support for options <code>-h</code> and <code>-t</code> to [[#echo_subrequest_async|echo_subrequest_async]] and [[#echo_subrequest|echo_subrequest]]. For example
3243
 
+
3244
 
+<geshi lang="nginx">
3245
 
+  echo_subrequest POST /sub -q 'foo=Foo&bar=Bar' -b 'hello' -t 'text/plan' -h 'X-My-Header: blah blah'
3246
 
+</geshi>
3247
 
+
3248
 
+* Add options to control whether a subrequest should inherit cached variables from its parent request (i.e. the current request that is calling the subrequest in question). Currently none of the subrequests issued by this module inherit the cached variables from the parent request.
3249
 
+* Add new variable ''$echo_active_subrequests'' to show <code>r->main->count - 1</code>.
3250
 
+* Add the ''echo_file'' and ''echo_cached_file'' directives.
3251
 
+* Add new varaible ''$echo_request_headers'' to accompany the existing [[#$echo_client_request_headers|$echo_client_request_headers]] variable.
3252
 
+* Add new directive ''echo_foreach'', as in
3253
 
+
3254
 
+<geshi lang="nginx">
3255
 
+  echo_foreach 'cat' 'dog' 'mouse';
3256
 
+    echo_location_async "/animals/$echo_it";
3257
 
+  echo_end;
3258
 
+</geshi>
3259
 
+
3260
 
+* Add new directive ''echo_foreach_range'', as in
3261
 
+
3262
 
+<geshi lang="nginx">
3263
 
+  echo_foreach_range '[1..100]' '[a-zA-z0-9]';
3264
 
+    echo_location_async "/item/$echo_it";
3265
 
+  echo_end;
3266
 
+</geshi>
3267
 
+
3268
 
+* Add new directive ''echo_repeat'', as in
3269
 
+
3270
 
+<geshi lang="nginx">
3271
 
+  echo_repeat 10 $i {
3272
 
+      echo "Page $i";
3273
 
+      echo_location "/path/to/page/$i";
3274
 
+  }
3275
 
+</geshi>
3276
 
+
3277
 
+This is just another way of saying
3278
 
+
3279
 
+<geshi lang="nginx">
3280
 
+  echo_foreach_range $i [1..10];
3281
 
+      echo "Page $i";
3282
 
+      echo_location "/path/to/page/$i";
3283
 
+  echo_end;
3284
 
+</geshi>
3285
 
+
3286
 
+Thanks Marcus Clyne for providing this idea.
3287
 
+
3288
 
+* Add new variable $echo_random which always returns a random non-negative integer with the lower/upper limit specified by the new directives <code>echo_random_min</code> and <code>echo_random_max</code>. For example,
3289
 
+
3290
 
+<geshi lang="nginx">
3291
 
+  echo_random_min  10
3292
 
+  echo_random_max  200
3293
 
+  echo "random number: $echo_random";
3294
 
+</geshi>
3295
 
+
3296
 
+Thanks Marcus Clyne for providing this idea.
3297
 
+
3298
 
+= Getting involved =
3299
 
+
3300
 
+You'll be very welcomed to submit patches to the [[#Author|author]] or just ask for a commit bit to the [[#Source Repository|source repository]] on GitHub.
3301
 
+
3302
 
+
3303
 
+
3304
 
+= Author =
3305
 
+
3306
 
+agentzh (章亦春) ''<agentzh@gmail.com>''
3307
 
+
3308
 
+This wiki page is also maintained by the author himself, and everybody is encouraged to improve this page as well.
3309
 
+
3310
 
+= Copyright & License =
3311
 
+
3312
 
+Copyright (c) 2009, Taobao Inc., Alibaba Group ( http://www.taobao.com ).
3313
 
+
3314
 
+Copyright (c) 2009, agentzh <agentzh@gmail.com>.
3315
 
+
3316
 
+This module is licensed under the terms of the BSD license.
3317
 
+
3318
 
+Redistribution and use in source and binary forms, with or without
3319
 
+modification, are permitted provided that the following conditions
3320
 
+are met:
3321
 
+
3322
 
+* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
3323
 
+* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3324
 
+* Neither the name of the Taobao Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
3325
 
+
3326
 
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3327
 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3328
 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3329
 
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3330
 
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3331
 
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
3332
 
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
3333
 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
3334
 
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
3335
 
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
3336
 
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3337
 
+
3338
 
+= See Also =
3339
 
+
3340
 
+* The original [http://agentzh.spaces.live.com/blog/cns!FF3A735632E41548!478.entry blog post] about this module's initial development.
3341
 
+* The standard [[NginxHttpAdditionModule|addition filter module]].
3342
 
+* The standard [[NginxHttpProxyModule|proxy module]].
3343
 
+
3344
 
Index: 0.8/modules/nginx-echo/src/ddebug.h
3345
 
===================================================================
3346
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
3347
 
+++ 0.8/modules/nginx-echo/src/ddebug.h 2010-10-31 07:35:23.648338001 +0000
3348
 
@@ -0,0 +1,109 @@
3349
 
+#ifndef DDEBUG_H
3350
 
+#define DDEBUG_H
3351
 
+
3352
 
+#include <ngx_core.h>
3353
 
+#include <ngx_http.h>
3354
 
+
3355
 
+#if defined(DDEBUG) && (DDEBUG)
3356
 
+
3357
 
+#   if (NGX_HAVE_VARIADIC_MACROS)
3358
 
+
3359
 
+#       define dd(...) fprintf(stderr, "echo *** %s: ", __func__); \
3360
 
+            fprintf(stderr, __VA_ARGS__); \
3361
 
+            fprintf(stderr, " at %s line %d.\n", __FILE__, __LINE__)
3362
 
+
3363
 
+#   else
3364
 
+
3365
 
+#include <stdarg.h>
3366
 
+#include <stdio.h>
3367
 
+
3368
 
+#include <stdarg.h>
3369
 
+
3370
 
+static void dd(const char * fmt, ...) {
3371
 
+}
3372
 
+
3373
 
+#    endif
3374
 
+
3375
 
+#   if DDEBUG > 1
3376
 
+
3377
 
+#       define dd_enter() dd_enter_helper(r, __func__)
3378
 
+
3379
 
+static void dd_enter_helper(ngx_http_request_t *r, const char *func) {
3380
 
+    ngx_http_posted_request_t       *pr;
3381
 
+
3382
 
+    fprintf(stderr, ">enter %s %.*s %.*s?%.*s c:%d m:%p r:%p ar:%p pr:%p",
3383
 
+            func,
3384
 
+            (int) r->method_name.len, r->method_name.data,
3385
 
+            (int) r->uri.len, r->uri.data,
3386
 
+            (int) r->args.len, r->args.data,
3387
 
+            0/*(int) r->main->count*/, r->main,
3388
 
+            r, r->connection->data, r->parent);
3389
 
+
3390
 
+    if (r->posted_requests) {
3391
 
+        fprintf(stderr, " posted:");
3392
 
+
3393
 
+        for (pr = r->posted_requests; pr; pr = pr->next) {
3394
 
+            fprintf(stderr, "%p,", pr);
3395
 
+        }
3396
 
+    }
3397
 
+
3398
 
+    fprintf(stderr, "\n");
3399
 
+}
3400
 
+
3401
 
+#   else
3402
 
+
3403
 
+#       define dd_enter()
3404
 
+
3405
 
+#   endif
3406
 
+
3407
 
+#else
3408
 
+
3409
 
+#   if (NGX_HAVE_VARIADIC_MACROS)
3410
 
+
3411
 
+#       define dd(...)
3412
 
+
3413
 
+#       define dd_enter()
3414
 
+
3415
 
+#   else
3416
 
+
3417
 
+#include <stdarg.h>
3418
 
+
3419
 
+static void dd(const char * fmt, ...) {
3420
 
+}
3421
 
+
3422
 
+static void dd_enter() {
3423
 
+}
3424
 
+
3425
 
+#   endif
3426
 
+
3427
 
+#endif
3428
 
+
3429
 
+#if defined(DDEBUG) && (DDEBUG)
3430
 
+
3431
 
+#define dd_check_read_event_handler(r)   \
3432
 
+    dd("r->read_event_handler = %s", \
3433
 
+        r->read_event_handler == ngx_http_block_reading ? \
3434
 
+            "ngx_http_block_reading" : \
3435
 
+        r->read_event_handler == ngx_http_test_reading ? \
3436
 
+            "ngx_http_test_reading" : \
3437
 
+        r->read_event_handler == ngx_http_request_empty_handler ? \
3438
 
+            "ngx_http_request_empty_handler" : "UNKNOWN")
3439
 
+
3440
 
+#define dd_check_write_event_handler(r)   \
3441
 
+    dd("r->write_event_handler = %s", \
3442
 
+        r->write_event_handler == ngx_http_handler ? \
3443
 
+            "ngx_http_handler" : \
3444
 
+        r->write_event_handler == ngx_http_core_run_phases ? \
3445
 
+            "ngx_http_core_run_phases" : \
3446
 
+        r->write_event_handler == ngx_http_request_empty_handler ? \
3447
 
+            "ngx_http_request_empty_handler" : "UNKNOWN")
3448
 
+
3449
 
+#else
3450
 
+
3451
 
+#define dd_check_read_event_handler(r)
3452
 
+#define dd_check_write_event_handler(r)
3453
 
+
3454
 
+#endif
3455
 
+
3456
 
+#endif /* DDEBUG_H */
3457
 
+
3458
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_echo.c
3459
 
===================================================================
3460
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
3461
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_echo.c     2010-10-31 07:35:23.648338001 +0000
3462
 
@@ -0,0 +1,270 @@
3463
 
+#define DDEBUG 0
3464
 
+#include "ddebug.h"
3465
 
+
3466
 
+#include "ngx_http_echo_echo.h"
3467
 
+#include "ngx_http_echo_util.h"
3468
 
+#include "ngx_http_echo_filter.h"
3469
 
+
3470
 
+#include <nginx.h>
3471
 
+
3472
 
+static ngx_buf_t ngx_http_echo_space_buf;
3473
 
+
3474
 
+static ngx_buf_t ngx_http_echo_newline_buf;
3475
 
+
3476
 
+ngx_int_t
3477
 
+ngx_http_echo_echo_init(ngx_conf_t *cf)
3478
 
+{
3479
 
+    static u_char space_str[]   = " ";
3480
 
+    static u_char newline_str[] = "\n";
3481
 
+
3482
 
+    dd("global init...");
3483
 
+
3484
 
+    ngx_memzero(&ngx_http_echo_space_buf, sizeof(ngx_buf_t));
3485
 
+    ngx_http_echo_space_buf.memory = 1;
3486
 
+    ngx_http_echo_space_buf.start =
3487
 
+        ngx_http_echo_space_buf.pos =
3488
 
+            space_str;
3489
 
+    ngx_http_echo_space_buf.end =
3490
 
+        ngx_http_echo_space_buf.last =
3491
 
+            space_str + sizeof(space_str) - 1;
3492
 
+
3493
 
+    ngx_memzero(&ngx_http_echo_newline_buf, sizeof(ngx_buf_t));
3494
 
+    ngx_http_echo_newline_buf.memory = 1;
3495
 
+    ngx_http_echo_newline_buf.start =
3496
 
+        ngx_http_echo_newline_buf.pos =
3497
 
+            newline_str;
3498
 
+    ngx_http_echo_newline_buf.end =
3499
 
+        ngx_http_echo_newline_buf.last =
3500
 
+            newline_str + sizeof(newline_str) - 1;
3501
 
+
3502
 
+    return NGX_OK;
3503
 
+}
3504
 
+
3505
 
+
3506
 
+ngx_int_t
3507
 
+ngx_http_echo_exec_echo(ngx_http_request_t *r,
3508
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args,
3509
 
+        ngx_flag_t in_filter, ngx_array_t *opts)
3510
 
+{
3511
 
+    ngx_uint_t                  i;
3512
 
+
3513
 
+    ngx_buf_t                   *space_buf;
3514
 
+    ngx_buf_t                   *newline_buf;
3515
 
+    ngx_buf_t                   *buf;
3516
 
+
3517
 
+    ngx_str_t                   *computed_arg;
3518
 
+    ngx_str_t                   *computed_arg_elts;
3519
 
+    ngx_str_t                   *opt;
3520
 
+
3521
 
+    ngx_chain_t *cl  = NULL; /* the head of the chain link */
3522
 
+    ngx_chain_t **ll = &cl;  /* always point to the address of the last link */
3523
 
+
3524
 
+
3525
 
+    dd_enter();
3526
 
+
3527
 
+    if (computed_args == NULL) {
3528
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
3529
 
+    }
3530
 
+
3531
 
+    computed_arg_elts = computed_args->elts;
3532
 
+    for (i = 0; i < computed_args->nelts; i++) {
3533
 
+        computed_arg = &computed_arg_elts[i];
3534
 
+
3535
 
+        if (computed_arg->len == 0) {
3536
 
+            buf = NULL;
3537
 
+
3538
 
+        } else {
3539
 
+            buf = ngx_calloc_buf(r->pool);
3540
 
+            if (buf == NULL) {
3541
 
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
3542
 
+            }
3543
 
+
3544
 
+            buf->start = buf->pos = computed_arg->data;
3545
 
+            buf->last = buf->end = computed_arg->data +
3546
 
+                computed_arg->len;
3547
 
+
3548
 
+            buf->memory = 1;
3549
 
+        }
3550
 
+
3551
 
+        if (cl == NULL) {
3552
 
+            cl = ngx_alloc_chain_link(r->pool);
3553
 
+            if (cl == NULL) {
3554
 
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
3555
 
+            }
3556
 
+            cl->buf  = buf;
3557
 
+            cl->next = NULL;
3558
 
+            ll = &cl->next;
3559
 
+        } else {
3560
 
+            /* append a space first */
3561
 
+            *ll = ngx_alloc_chain_link(r->pool);
3562
 
+
3563
 
+            if (*ll == NULL) {
3564
 
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
3565
 
+            }
3566
 
+
3567
 
+            space_buf = ngx_calloc_buf(r->pool);
3568
 
+
3569
 
+            if (space_buf == NULL) {
3570
 
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
3571
 
+            }
3572
 
+
3573
 
+            /* nginx clears buf flags at the end of each request handling,
3574
 
+             * so we have to make a clone here. */
3575
 
+            *space_buf = ngx_http_echo_space_buf;
3576
 
+
3577
 
+            (*ll)->buf = space_buf;
3578
 
+            (*ll)->next = NULL;
3579
 
+
3580
 
+            ll = &(*ll)->next;
3581
 
+
3582
 
+            /* then append the buf only if it's non-empty */
3583
 
+            if (buf) {
3584
 
+                *ll = ngx_alloc_chain_link(r->pool);
3585
 
+                if (*ll == NULL) {
3586
 
+                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
3587
 
+                }
3588
 
+                (*ll)->buf  = buf;
3589
 
+                (*ll)->next = NULL;
3590
 
+
3591
 
+                ll = &(*ll)->next;
3592
 
+            }
3593
 
+        }
3594
 
+    } /* end for */
3595
 
+
3596
 
+    if (opts && opts->nelts > 0) {
3597
 
+        opt = opts->elts;
3598
 
+        if (opt[0].len == 1 && opt[0].data[0] == 'n') {
3599
 
+            goto done;
3600
 
+        }
3601
 
+    }
3602
 
+
3603
 
+    /* append the newline character */
3604
 
+
3605
 
+    if (cl && cl->buf == NULL) {
3606
 
+        cl = cl->next;
3607
 
+    }
3608
 
+
3609
 
+    newline_buf = ngx_calloc_buf(r->pool);
3610
 
+
3611
 
+    if (newline_buf == NULL) {
3612
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
3613
 
+    }
3614
 
+
3615
 
+    *newline_buf = ngx_http_echo_newline_buf;
3616
 
+
3617
 
+    if (cl == NULL) {
3618
 
+        cl = ngx_alloc_chain_link(r->pool);
3619
 
+
3620
 
+        if (cl == NULL) {
3621
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
3622
 
+        }
3623
 
+
3624
 
+        cl->buf = newline_buf;
3625
 
+        cl->next = NULL;
3626
 
+        /* ll = &cl->next; */
3627
 
+
3628
 
+    } else {
3629
 
+        *ll = ngx_alloc_chain_link(r->pool);
3630
 
+
3631
 
+        if (*ll == NULL) {
3632
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
3633
 
+        }
3634
 
+
3635
 
+        (*ll)->buf  = newline_buf;
3636
 
+        (*ll)->next = NULL;
3637
 
+        /* ll = &(*ll)->next; */
3638
 
+    }
3639
 
+
3640
 
+done:
3641
 
+
3642
 
+    if (cl == NULL || cl->buf == NULL) {
3643
 
+        return NGX_OK;
3644
 
+    }
3645
 
+
3646
 
+    if (in_filter) {
3647
 
+        return ngx_http_echo_next_body_filter(r, cl);
3648
 
+    }
3649
 
+
3650
 
+    return ngx_http_echo_send_chain_link(r, ctx, cl);
3651
 
+}
3652
 
+
3653
 
+
3654
 
+ngx_int_t
3655
 
+ngx_http_echo_exec_echo_flush(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx)
3656
 
+{
3657
 
+    return ngx_http_send_special(r, NGX_HTTP_FLUSH);
3658
 
+}
3659
 
+
3660
 
+
3661
 
+ngx_int_t
3662
 
+ngx_http_echo_exec_echo_request_body(ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx)
3663
 
+{
3664
 
+    if (r->request_body && r->request_body->bufs) {
3665
 
+        return ngx_http_echo_send_chain_link(r, ctx, r->request_body->bufs);
3666
 
+    }
3667
 
+
3668
 
+    return NGX_OK;
3669
 
+}
3670
 
+
3671
 
+
3672
 
+ngx_int_t
3673
 
+ngx_http_echo_exec_echo_duplicate(ngx_http_request_t *r,
3674
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
3675
 
+{
3676
 
+    ngx_str_t                   *computed_arg;
3677
 
+    ngx_str_t                   *computed_arg_elts;
3678
 
+    ssize_t                     i, count;
3679
 
+    ngx_str_t                   *str;
3680
 
+    u_char                      *p;
3681
 
+    ngx_int_t                   rc;
3682
 
+
3683
 
+    ngx_buf_t                   *buf;
3684
 
+    ngx_chain_t                 *cl;
3685
 
+
3686
 
+
3687
 
+    dd_enter();
3688
 
+
3689
 
+    computed_arg_elts = computed_args->elts;
3690
 
+
3691
 
+    computed_arg = &computed_arg_elts[0];
3692
 
+
3693
 
+    count = ngx_http_echo_atosz(computed_arg->data, computed_arg->len);
3694
 
+
3695
 
+    if (count == NGX_ERROR) {
3696
 
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
3697
 
+                   "invalid size specified: \"%V\"", computed_arg);
3698
 
+
3699
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
3700
 
+    }
3701
 
+
3702
 
+    str = &computed_arg_elts[1];
3703
 
+
3704
 
+    if (count == 0 || str->len == 0) {
3705
 
+        rc = ngx_http_echo_send_header_if_needed(r, ctx);
3706
 
+        if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
3707
 
+            return rc;
3708
 
+        }
3709
 
+        return NGX_OK;
3710
 
+    }
3711
 
+
3712
 
+    buf = ngx_create_temp_buf(r->pool, count * str->len);
3713
 
+    if (buf == NULL) {
3714
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
3715
 
+    }
3716
 
+
3717
 
+    p = buf->pos;
3718
 
+    for (i = 0; i < count; i++) {
3719
 
+        p = ngx_copy(p, str->data, str->len);
3720
 
+    }
3721
 
+    buf->last = p;
3722
 
+
3723
 
+    cl = ngx_alloc_chain_link(r->pool);
3724
 
+    if (cl == NULL) {
3725
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
3726
 
+    }
3727
 
+    cl->next = NULL;
3728
 
+    cl->buf = buf;
3729
 
+
3730
 
+    return ngx_http_echo_send_chain_link(r, ctx, cl);
3731
 
+}
3732
 
+
3733
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_echo.h
3734
 
===================================================================
3735
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
3736
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_echo.h     2010-10-31 07:35:23.648338001 +0000
3737
 
@@ -0,0 +1,22 @@
3738
 
+#ifndef ECHO_ECHO_H
3739
 
+#define ECHO_ECHO_H
3740
 
+
3741
 
+#include "ngx_http_echo_module.h"
3742
 
+
3743
 
+ngx_int_t ngx_http_echo_echo_init(ngx_conf_t *cf);
3744
 
+
3745
 
+ngx_int_t ngx_http_echo_exec_echo(ngx_http_request_t *r,
3746
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args,
3747
 
+        ngx_flag_t in_filter, ngx_array_t *opts);
3748
 
+
3749
 
+ngx_int_t ngx_http_echo_exec_echo_request_body(ngx_http_request_t *r,
3750
 
+        ngx_http_echo_ctx_t *ctx);
3751
 
+
3752
 
+ngx_int_t ngx_http_echo_exec_echo_flush(ngx_http_request_t *r,
3753
 
+        ngx_http_echo_ctx_t *ctx);
3754
 
+
3755
 
+ngx_int_t ngx_http_echo_exec_echo_duplicate(ngx_http_request_t *r,
3756
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args);
3757
 
+
3758
 
+#endif /* ECHO_ECHO_H */
3759
 
+
3760
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_filter.c
3761
 
===================================================================
3762
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
3763
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_filter.c   2010-10-31 07:35:23.648338001 +0000
3764
 
@@ -0,0 +1,240 @@
3765
 
+#define DDEBUG 0
3766
 
+
3767
 
+#include "ddebug.h"
3768
 
+#include "ngx_http_echo_filter.h"
3769
 
+#include "ngx_http_echo_util.h"
3770
 
+#include "ngx_http_echo_echo.h"
3771
 
+
3772
 
+#include <ngx_log.h>
3773
 
+
3774
 
+ngx_flag_t ngx_http_echo_filter_used = 0;
3775
 
+
3776
 
+ngx_http_output_header_filter_pt ngx_http_echo_next_header_filter;
3777
 
+
3778
 
+ngx_http_output_body_filter_pt ngx_http_echo_next_body_filter;
3779
 
+
3780
 
+static ngx_int_t ngx_http_echo_header_filter(ngx_http_request_t *r);
3781
 
+
3782
 
+static ngx_int_t ngx_http_echo_body_filter(ngx_http_request_t *r, ngx_chain_t *in);
3783
 
+
3784
 
+/* filter handlers */
3785
 
+static ngx_int_t ngx_http_echo_exec_filter_cmds(ngx_http_request_t *r,
3786
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *cmds, ngx_uint_t *iterator);
3787
 
+
3788
 
+
3789
 
+ngx_int_t
3790
 
+ngx_http_echo_filter_init (ngx_conf_t *cf)
3791
 
+{
3792
 
+    if (ngx_http_echo_filter_used) {
3793
 
+        dd("top header filter: %ld", (unsigned long) ngx_http_top_header_filter);
3794
 
+        ngx_http_echo_next_header_filter = ngx_http_top_header_filter;
3795
 
+        ngx_http_top_header_filter  = ngx_http_echo_header_filter;
3796
 
+
3797
 
+        dd("top body filter: %ld", (unsigned long) ngx_http_top_body_filter);
3798
 
+        ngx_http_echo_next_body_filter = ngx_http_top_body_filter;
3799
 
+        ngx_http_top_body_filter  = ngx_http_echo_body_filter;
3800
 
+    }
3801
 
+
3802
 
+    return NGX_OK;
3803
 
+}
3804
 
+
3805
 
+
3806
 
+static ngx_int_t
3807
 
+ngx_http_echo_header_filter(ngx_http_request_t *r)
3808
 
+{
3809
 
+    ngx_http_echo_loc_conf_t    *conf;
3810
 
+    ngx_http_echo_ctx_t         *ctx;
3811
 
+    ngx_int_t                   rc;
3812
 
+
3813
 
+    dd("We're in the header filter...");
3814
 
+
3815
 
+    ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module);
3816
 
+
3817
 
+    /* XXX we should add option to insert contents for responses
3818
 
+     * of non-200 status code here... */
3819
 
+    /*
3820
 
+    if (r->headers_out.status != NGX_HTTP_OK) {
3821
 
+        if (ctx != NULL) {
3822
 
+            ctx->skip_filter = 1;
3823
 
+        }
3824
 
+        return ngx_http_echo_next_header_filter(r);
3825
 
+    }
3826
 
+    */
3827
 
+
3828
 
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
3829
 
+    if (conf->before_body_cmds == NULL && conf->after_body_cmds == NULL) {
3830
 
+        if (ctx != NULL) {
3831
 
+            ctx->skip_filter = 1;
3832
 
+        }
3833
 
+        return ngx_http_echo_next_header_filter(r);
3834
 
+    }
3835
 
+
3836
 
+    if (ctx == NULL) {
3837
 
+        rc = ngx_http_echo_init_ctx(r, &ctx);
3838
 
+        if (rc != NGX_OK) {
3839
 
+            return NGX_ERROR;
3840
 
+        }
3841
 
+        ctx->headers_sent = 1;
3842
 
+        ngx_http_set_ctx(r, ctx, ngx_http_echo_module);
3843
 
+    }
3844
 
+
3845
 
+    /* enable streaming here (use chunked encoding) */
3846
 
+    ngx_http_clear_content_length(r);
3847
 
+    ngx_http_clear_accept_ranges(r);
3848
 
+
3849
 
+    return ngx_http_echo_next_header_filter(r);
3850
 
+}
3851
 
+
3852
 
+
3853
 
+static ngx_int_t
3854
 
+ngx_http_echo_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
3855
 
+{
3856
 
+    ngx_http_echo_ctx_t         *ctx;
3857
 
+    ngx_int_t                    rc;
3858
 
+    ngx_http_echo_loc_conf_t    *conf;
3859
 
+    ngx_flag_t                   last;
3860
 
+    ngx_chain_t                 *cl;
3861
 
+    ngx_buf_t                   *buf;
3862
 
+
3863
 
+    if (in == NULL || r->header_only) {
3864
 
+        return ngx_http_echo_next_body_filter(r, in);
3865
 
+    }
3866
 
+
3867
 
+    ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module);
3868
 
+
3869
 
+    if (ctx == NULL || ctx->skip_filter) {
3870
 
+        return ngx_http_echo_next_body_filter(r, in);
3871
 
+    }
3872
 
+
3873
 
+    conf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
3874
 
+
3875
 
+    if (!ctx->before_body_sent) {
3876
 
+        ctx->before_body_sent = 1;
3877
 
+
3878
 
+        if (conf->before_body_cmds != NULL) {
3879
 
+            rc = ngx_http_echo_exec_filter_cmds(r, ctx, conf->before_body_cmds,
3880
 
+                    &ctx->next_before_body_cmd);
3881
 
+            if (rc != NGX_OK) {
3882
 
+                return NGX_ERROR;
3883
 
+            }
3884
 
+        }
3885
 
+    }
3886
 
+
3887
 
+    if (conf->after_body_cmds == NULL) {
3888
 
+        ctx->skip_filter = 1;
3889
 
+        return ngx_http_echo_next_body_filter(r, in);
3890
 
+    }
3891
 
+
3892
 
+    last = 0;
3893
 
+
3894
 
+    for (cl = in; cl; cl = cl->next) {
3895
 
+        if (cl->buf->last_buf) {
3896
 
+            cl->buf->last_buf = 0;
3897
 
+            cl->buf->sync = 1;
3898
 
+            last = 1;
3899
 
+        } else if (r != r->main && cl->buf->sync) {
3900
 
+            dd("Found sync buf");
3901
 
+            last = 1;
3902
 
+        }
3903
 
+    }
3904
 
+
3905
 
+    rc = ngx_http_echo_next_body_filter(r, in);
3906
 
+
3907
 
+    if (rc == NGX_ERROR || !last) {
3908
 
+        return rc;
3909
 
+    }
3910
 
+
3911
 
+    dd("exec filter cmds for after body cmds");
3912
 
+    rc = ngx_http_echo_exec_filter_cmds(r, ctx, conf->after_body_cmds, &ctx->next_after_body_cmd);
3913
 
+    if (rc != NGX_OK) {
3914
 
+        dd("FAILED: exec filter cmds for after body cmds");
3915
 
+        return NGX_ERROR;
3916
 
+    }
3917
 
+
3918
 
+    ctx->skip_filter = 1;
3919
 
+
3920
 
+    dd("after body cmds executed...terminating...");
3921
 
+
3922
 
+    /* XXX we can NOT use
3923
 
+     * ngx_http_send_special(r, NGX_HTTP_LAST) here
3924
 
+     * because we should bypass the upstream filters. */
3925
 
+    if (r != r->main) {
3926
 
+        return NGX_OK;
3927
 
+    }
3928
 
+
3929
 
+    buf = ngx_calloc_buf(r->pool);
3930
 
+    buf->last_buf = 1;
3931
 
+
3932
 
+    cl = ngx_alloc_chain_link(r->pool);
3933
 
+    if (cl == NULL) {
3934
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
3935
 
+    }
3936
 
+
3937
 
+    cl->next = NULL;
3938
 
+    cl->buf = buf;
3939
 
+
3940
 
+    return ngx_http_echo_next_body_filter(r, cl);
3941
 
+}
3942
 
+
3943
 
+
3944
 
+static ngx_int_t
3945
 
+ngx_http_echo_exec_filter_cmds(ngx_http_request_t *r,
3946
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *cmds,
3947
 
+        ngx_uint_t *iterator)
3948
 
+{
3949
 
+    ngx_int_t                    rc;
3950
 
+    ngx_array_t                 *computed_args = NULL;
3951
 
+    ngx_http_echo_cmd_t         *cmd;
3952
 
+    ngx_http_echo_cmd_t         *cmd_elts;
3953
 
+    ngx_array_t                 *opts = NULL;
3954
 
+
3955
 
+    for (cmd_elts = cmds->elts; *iterator < cmds->nelts; (*iterator)++) {
3956
 
+        cmd = &cmd_elts[*iterator];
3957
 
+
3958
 
+        /* evaluate arguments for the current cmd (if any) */
3959
 
+        if (cmd->args) {
3960
 
+            computed_args = ngx_array_create(r->pool, cmd->args->nelts,
3961
 
+                    sizeof(ngx_str_t));
3962
 
+
3963
 
+            if (computed_args == NULL) {
3964
 
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
3965
 
+            }
3966
 
+
3967
 
+            opts = ngx_array_create(r->pool, 1, sizeof(ngx_str_t));
3968
 
+
3969
 
+            if (opts == NULL) {
3970
 
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
3971
 
+            }
3972
 
+
3973
 
+            rc = ngx_http_echo_eval_cmd_args(r, cmd, computed_args, opts);
3974
 
+
3975
 
+            if (rc != NGX_OK) {
3976
 
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
3977
 
+                        "Failed to evaluate arguments for "
3978
 
+                        "the directive.");
3979
 
+                return rc;
3980
 
+            }
3981
 
+        }
3982
 
+
3983
 
+        /* do command dispatch based on the opcode */
3984
 
+        switch (cmd->opcode) {
3985
 
+        case echo_opcode_echo_before_body:
3986
 
+        case echo_opcode_echo_after_body:
3987
 
+            dd("exec echo_before_body or echo_after_body...");
3988
 
+
3989
 
+            rc = ngx_http_echo_exec_echo(r, ctx, computed_args,
3990
 
+                    1 /* in filter */, opts);
3991
 
+
3992
 
+            if (rc != NGX_OK) {
3993
 
+                return rc;
3994
 
+            }
3995
 
+
3996
 
+            break;
3997
 
+        default:
3998
 
+            break;
3999
 
+        }
4000
 
+    }
4001
 
+
4002
 
+    return NGX_OK;
4003
 
+}
4004
 
+
4005
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_filter.h
4006
 
===================================================================
4007
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
4008
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_filter.h   2010-10-31 07:35:23.648338001 +0000
4009
 
@@ -0,0 +1,16 @@
4010
 
+#ifndef ECHO_FILTER_H
4011
 
+#define ECHO_FILTER_H
4012
 
+
4013
 
+#include "ngx_http_echo_module.h"
4014
 
+
4015
 
+extern ngx_flag_t ngx_http_echo_filter_used;
4016
 
+
4017
 
+extern ngx_http_output_header_filter_pt ngx_http_echo_next_header_filter;
4018
 
+
4019
 
+extern ngx_http_output_body_filter_pt ngx_http_echo_next_body_filter;
4020
 
+
4021
 
+
4022
 
+ngx_int_t ngx_http_echo_filter_init (ngx_conf_t *cf);
4023
 
+
4024
 
+#endif /* ECHO_FILTER_H */
4025
 
+
4026
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_foreach.c
4027
 
===================================================================
4028
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
4029
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_foreach.c  2010-10-31 07:35:23.648338001 +0000
4030
 
@@ -0,0 +1,174 @@
4031
 
+#define DDEBUG 0
4032
 
+#include "ddebug.h"
4033
 
+
4034
 
+#include "ngx_http_echo_foreach.h"
4035
 
+#include "ngx_http_echo_util.h"
4036
 
+
4037
 
+#include <nginx.h>
4038
 
+
4039
 
+ngx_int_t
4040
 
+ngx_http_echo_it_variable(ngx_http_request_t *r,
4041
 
+        ngx_http_variable_value_t *v, uintptr_t data)
4042
 
+{
4043
 
+    ngx_http_echo_ctx_t         *ctx;
4044
 
+    ngx_uint_t                  i;
4045
 
+    ngx_array_t                 *choices;
4046
 
+    ngx_str_t                   *choice_elts, *choice;
4047
 
+
4048
 
+    ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module);
4049
 
+
4050
 
+    if (ctx->foreach != NULL) {
4051
 
+        choices = ctx->foreach->choices;
4052
 
+        i = ctx->foreach->next_choice;
4053
 
+        if (i < choices->nelts) {
4054
 
+            choice_elts = choices->elts;
4055
 
+            choice = &choice_elts[i];
4056
 
+
4057
 
+            v->len = choice->len;
4058
 
+            v->data = choice->data;
4059
 
+            v->valid = 1;
4060
 
+            v->no_cacheable = 1;
4061
 
+            v->not_found = 0;
4062
 
+        }
4063
 
+
4064
 
+        return NGX_OK;
4065
 
+    }
4066
 
+
4067
 
+    v->not_found = 1;
4068
 
+
4069
 
+    return NGX_OK;
4070
 
+}
4071
 
+
4072
 
+
4073
 
+ngx_int_t
4074
 
+ngx_http_echo_exec_echo_foreach_split(ngx_http_request_t *r,
4075
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
4076
 
+{
4077
 
+    ngx_http_echo_loc_conf_t    *elcf;
4078
 
+    ngx_str_t                   *delimiter, *compound;
4079
 
+    u_char                      *pos, *last, *end;
4080
 
+    ngx_str_t                   *choice;
4081
 
+    ngx_str_t                   *computed_arg_elts;
4082
 
+    ngx_array_t                 *cmds;
4083
 
+    ngx_http_echo_cmd_t         *cmd;
4084
 
+    ngx_http_echo_cmd_t         *cmd_elts;
4085
 
+
4086
 
+    if (ctx->foreach != NULL) {
4087
 
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
4088
 
+                "Nested echo_foreach not supported yet.");
4089
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
4090
 
+    }
4091
 
+
4092
 
+    if (computed_args->nelts < 2) {
4093
 
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
4094
 
+                "echo_foreach should take at least two arguments. "
4095
 
+                "(if your delimiter starts with \"-\", preceding it with a "
4096
 
+                "\"--\".)");
4097
 
+
4098
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
4099
 
+    }
4100
 
+
4101
 
+    computed_arg_elts = computed_args->elts;
4102
 
+
4103
 
+    compound  = &computed_arg_elts[1];
4104
 
+
4105
 
+    dd("HEY coumpound len: %u", compound->len);
4106
 
+
4107
 
+    ctx->foreach = ngx_palloc(r->pool, sizeof(ngx_http_echo_foreach_ctx_t));
4108
 
+
4109
 
+    if (ctx->foreach == NULL) {
4110
 
+        return NGX_ERROR;
4111
 
+    }
4112
 
+
4113
 
+    ctx->foreach->cmd_index = ctx->next_handler_cmd;
4114
 
+
4115
 
+    ctx->foreach->next_choice = 0;
4116
 
+
4117
 
+    ctx->foreach->choices = ngx_array_create(r->pool, 10, sizeof(ngx_str_t));
4118
 
+    if (ctx->foreach->choices == NULL) {
4119
 
+        return NGX_ERROR;
4120
 
+    }
4121
 
+
4122
 
+    delimiter = &computed_arg_elts[0];
4123
 
+
4124
 
+    pos = compound->data;
4125
 
+    end = compound->data + compound->len;
4126
 
+    while ((last = ngx_http_echo_strlstrn(pos, end, delimiter->data, delimiter->len - 1))
4127
 
+                != NULL) {
4128
 
+        dd("entered the loop");
4129
 
+
4130
 
+        if (last == pos) {
4131
 
+            dd("!!! len == 0");
4132
 
+            pos = last + delimiter->len;
4133
 
+            continue;
4134
 
+        }
4135
 
+
4136
 
+        choice = ngx_array_push(ctx->foreach->choices);
4137
 
+        if (choice == NULL) {
4138
 
+            return NGX_ERROR;
4139
 
+        }
4140
 
+
4141
 
+        choice->data = pos;
4142
 
+        choice->len  = last - pos;
4143
 
+        pos = last + delimiter->len;
4144
 
+    }
4145
 
+
4146
 
+    if (pos < end) {
4147
 
+        choice = ngx_array_push(ctx->foreach->choices);
4148
 
+        if (choice == NULL) {
4149
 
+            return NGX_ERROR;
4150
 
+        }
4151
 
+
4152
 
+        choice->data = pos;
4153
 
+        choice->len  = end - pos;
4154
 
+    }
4155
 
+
4156
 
+    if (ctx->foreach->choices->nelts == 0) {
4157
 
+        /* skip the foreach body entirely */
4158
 
+        elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
4159
 
+        cmds = elcf->handler_cmds;
4160
 
+        cmd_elts = cmds->elts;
4161
 
+        for (; ctx->next_handler_cmd < cmds->nelts;
4162
 
+                ctx->next_handler_cmd++) {
4163
 
+            cmd = &cmd_elts[ctx->next_handler_cmd + 1];
4164
 
+            if (cmd->opcode == echo_opcode_echo_end) {
4165
 
+                return NGX_OK;
4166
 
+            }
4167
 
+        }
4168
 
+
4169
 
+    }
4170
 
+
4171
 
+    return NGX_OK;
4172
 
+}
4173
 
+
4174
 
+
4175
 
+ngx_int_t
4176
 
+ngx_http_echo_exec_echo_end(ngx_http_request_t *r,
4177
 
+        ngx_http_echo_ctx_t *ctx)
4178
 
+{
4179
 
+    if (ctx->foreach == NULL) {
4180
 
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
4181
 
+                "Found a echo_end that has no corresponding echo_foreach "
4182
 
+                "before it.");
4183
 
+        return NGX_ERROR;
4184
 
+    }
4185
 
+
4186
 
+    ctx->foreach->next_choice++;
4187
 
+
4188
 
+    if (ctx->foreach->next_choice >= ctx->foreach->choices->nelts) {
4189
 
+        /* TODO We need to explicitly free the foreach ctx from
4190
 
+         * the pool */
4191
 
+        ctx->foreach = NULL;
4192
 
+
4193
 
+        return NGX_OK;
4194
 
+    }
4195
 
+
4196
 
+    dd("echo_end: ++ next_choice (total: %u): %u", ctx->foreach->choices->nelts, ctx->foreach->next_choice);
4197
 
+
4198
 
+    /* the main handler dispatcher loop will increment
4199
 
+     *   ctx->next_handler_cmd for us anyway. */
4200
 
+    ctx->next_handler_cmd = ctx->foreach->cmd_index;
4201
 
+
4202
 
+    return NGX_OK;
4203
 
+}
4204
 
+
4205
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_foreach.h
4206
 
===================================================================
4207
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
4208
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_foreach.h  2010-10-31 07:35:23.648338001 +0000
4209
 
@@ -0,0 +1,16 @@
4210
 
+#ifndef ECHO_FOREACH_H
4211
 
+#define ECHO_FOREACH_H
4212
 
+
4213
 
+#include "ngx_http_echo_module.h"
4214
 
+
4215
 
+ngx_int_t ngx_http_echo_exec_echo_foreach_split(ngx_http_request_t *r,
4216
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args);
4217
 
+
4218
 
+ngx_int_t ngx_http_echo_exec_echo_end(ngx_http_request_t *r,
4219
 
+        ngx_http_echo_ctx_t *ctx);
4220
 
+
4221
 
+ngx_int_t ngx_http_echo_it_variable(ngx_http_request_t *r,
4222
 
+        ngx_http_variable_value_t *v, uintptr_t data);
4223
 
+
4224
 
+#endif /* ECHO_FOREACH_H */
4225
 
+
4226
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_handler.c
4227
 
===================================================================
4228
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
4229
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_handler.c  2010-10-31 07:35:23.648338001 +0000
4230
 
@@ -0,0 +1,351 @@
4231
 
+#define DDEBUG 0
4232
 
+
4233
 
+#include "ddebug.h"
4234
 
+
4235
 
+#include "ngx_http_echo_handler.h"
4236
 
+#include "ngx_http_echo_echo.h"
4237
 
+#include "ngx_http_echo_util.h"
4238
 
+#include "ngx_http_echo_sleep.h"
4239
 
+#include "ngx_http_echo_var.h"
4240
 
+#include "ngx_http_echo_timer.h"
4241
 
+#include "ngx_http_echo_location.h"
4242
 
+#include "ngx_http_echo_subrequest.h"
4243
 
+#include "ngx_http_echo_request_info.h"
4244
 
+#include "ngx_http_echo_foreach.h"
4245
 
+
4246
 
+#include <nginx.h>
4247
 
+#include <ngx_log.h>
4248
 
+
4249
 
+ngx_int_t
4250
 
+ngx_http_echo_handler_init(ngx_conf_t *cf)
4251
 
+{
4252
 
+    ngx_int_t         rc;
4253
 
+
4254
 
+    rc = ngx_http_echo_echo_init(cf);
4255
 
+    if (rc != NGX_OK) {
4256
 
+        return rc;
4257
 
+    }
4258
 
+
4259
 
+    return ngx_http_echo_add_variables(cf);
4260
 
+}
4261
 
+
4262
 
+
4263
 
+void
4264
 
+ngx_http_echo_wev_handler(ngx_http_request_t *r)
4265
 
+{
4266
 
+    ngx_int_t                    rc;
4267
 
+    ngx_http_echo_ctx_t         *ctx;
4268
 
+
4269
 
+    dd_enter();
4270
 
+
4271
 
+    ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module);
4272
 
+
4273
 
+    if (ctx == NULL) {
4274
 
+        ngx_http_finalize_request(r, NGX_ERROR);
4275
 
+        return;
4276
 
+    }
4277
 
+
4278
 
+    if (ctx->waiting && ! ctx->done) {
4279
 
+        if (r->main->posted_requests
4280
 
+                && r->main->posted_requests->request != r)
4281
 
+        {
4282
 
+            dd("HOT SPIN");
4283
 
+
4284
 
+#if defined(nginx_version) && nginx_version >= 8012
4285
 
+            ngx_http_post_request(r, NULL);
4286
 
+#else
4287
 
+            ngx_http_post_request(r);
4288
 
+#endif
4289
 
+
4290
 
+            return;
4291
 
+        }
4292
 
+    }
4293
 
+
4294
 
+    ctx->done = 0;
4295
 
+
4296
 
+    ctx->next_handler_cmd++;
4297
 
+
4298
 
+    rc = ngx_http_echo_run_cmds(r);
4299
 
+
4300
 
+    dd("rc: %d", (int) rc);
4301
 
+
4302
 
+    if (rc == NGX_DONE) {
4303
 
+        return;
4304
 
+    }
4305
 
+
4306
 
+    if (rc == NGX_AGAIN) {
4307
 
+        dd("mark busy %d", (int) ctx->next_handler_cmd);
4308
 
+        ctx->waiting = 1;
4309
 
+        ctx->done = 0;
4310
 
+
4311
 
+    } else {
4312
 
+        dd("mark ready %d", (int) ctx->next_handler_cmd);
4313
 
+        ctx->waiting = 0;
4314
 
+        ctx->done = 1;
4315
 
+
4316
 
+        dd("finalizing with rc %d", (int) rc);
4317
 
+
4318
 
+        dd("finalize request %.*s with %d", (int) r->uri.len, r->uri.data, (int) rc);
4319
 
+
4320
 
+        ngx_http_finalize_request(r, rc);
4321
 
+    }
4322
 
+}
4323
 
+
4324
 
+
4325
 
+ngx_int_t
4326
 
+ngx_http_echo_handler(ngx_http_request_t *r)
4327
 
+{
4328
 
+    ngx_int_t                    rc;
4329
 
+    ngx_http_echo_ctx_t         *ctx;
4330
 
+
4331
 
+    rc = ngx_http_echo_run_cmds(r);
4332
 
+
4333
 
+    if (rc == NGX_ERROR) {
4334
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
4335
 
+    }
4336
 
+
4337
 
+    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
4338
 
+        return rc;
4339
 
+    }
4340
 
+
4341
 
+    if (rc == NGX_DONE) {
4342
 
+        return NGX_DONE;
4343
 
+    }
4344
 
+
4345
 
+    if (rc == NGX_AGAIN) {
4346
 
+#if defined(nginx_version) && nginx_version >= 8011
4347
 
+        r->main->count++;
4348
 
+#endif
4349
 
+
4350
 
+        /* XXX we need this for 0.7.x and 0.8.x < 0.8.11 */
4351
 
+        dd("%d", r->connection->destroyed);
4352
 
+        dd("%d", r->done);
4353
 
+
4354
 
+        ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module);
4355
 
+        if (ctx) {
4356
 
+            dd("mark busy %d", (int) ctx->next_handler_cmd);
4357
 
+            ctx->waiting = 1;
4358
 
+            ctx->done = 0;
4359
 
+        }
4360
 
+
4361
 
+        return NGX_DONE;
4362
 
+    }
4363
 
+
4364
 
+    return NGX_OK;
4365
 
+}
4366
 
+
4367
 
+
4368
 
+ngx_int_t
4369
 
+ngx_http_echo_run_cmds(ngx_http_request_t *r)
4370
 
+{
4371
 
+    ngx_http_echo_loc_conf_t    *elcf;
4372
 
+    ngx_http_echo_ctx_t         *ctx;
4373
 
+    ngx_int_t                    rc;
4374
 
+    ngx_array_t                 *cmds;
4375
 
+    ngx_array_t                 *computed_args = NULL;
4376
 
+    ngx_http_echo_cmd_t         *cmd;
4377
 
+    ngx_http_echo_cmd_t         *cmd_elts;
4378
 
+    ngx_array_t                 *opts = NULL;
4379
 
+
4380
 
+
4381
 
+    elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
4382
 
+    cmds = elcf->handler_cmds;
4383
 
+    if (cmds == NULL) {
4384
 
+        return NGX_DECLINED;
4385
 
+    }
4386
 
+
4387
 
+    ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module);
4388
 
+    if (ctx == NULL) {
4389
 
+        rc = ngx_http_echo_init_ctx(r, &ctx);
4390
 
+        if (rc != NGX_OK) {
4391
 
+            return rc;
4392
 
+        }
4393
 
+
4394
 
+        ngx_http_set_ctx(r, ctx, ngx_http_echo_module);
4395
 
+    }
4396
 
+
4397
 
+    dd("exec handler: %.*s: %i", (int) r->uri.len, r->uri.data,
4398
 
+            (int) ctx->next_handler_cmd);
4399
 
+
4400
 
+    cmd_elts = cmds->elts;
4401
 
+
4402
 
+    for (; ctx->next_handler_cmd < cmds->nelts; ctx->next_handler_cmd++) {
4403
 
+
4404
 
+        cmd = &cmd_elts[ctx->next_handler_cmd];
4405
 
+
4406
 
+        /* evaluate arguments for the current cmd (if any) */
4407
 
+        if (cmd->args) {
4408
 
+            computed_args = ngx_array_create(r->pool, cmd->args->nelts,
4409
 
+                    sizeof(ngx_str_t));
4410
 
+
4411
 
+            if (computed_args == NULL) {
4412
 
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
4413
 
+            }
4414
 
+
4415
 
+            opts = ngx_array_create(r->pool, 1, sizeof(ngx_str_t));
4416
 
+
4417
 
+            if (opts == NULL) {
4418
 
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
4419
 
+            }
4420
 
+
4421
 
+            rc = ngx_http_echo_eval_cmd_args(r, cmd, computed_args, opts);
4422
 
+            if (rc != NGX_OK) {
4423
 
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
4424
 
+                        "Failed to evaluate arguments for "
4425
 
+                        "the directive.");
4426
 
+                return rc;
4427
 
+            }
4428
 
+        }
4429
 
+
4430
 
+        /* do command dispatch based on the opcode */
4431
 
+        switch (cmd->opcode) {
4432
 
+        case echo_opcode_echo:
4433
 
+            /* XXX moved the following code to a separate
4434
 
+             * function */
4435
 
+            dd("found echo opcode");
4436
 
+            rc = ngx_http_echo_exec_echo(r, ctx, computed_args,
4437
 
+                    0 /* in filter */, opts);
4438
 
+            break;
4439
 
+
4440
 
+        case echo_opcode_echo_request_body:
4441
 
+            rc = ngx_http_echo_exec_echo_request_body(r, ctx);
4442
 
+            break;
4443
 
+
4444
 
+        case echo_opcode_echo_location_async:
4445
 
+            dd("found opcode echo location async...");
4446
 
+            rc = ngx_http_echo_exec_echo_location_async(r, ctx,
4447
 
+                    computed_args);
4448
 
+            break;
4449
 
+
4450
 
+        case echo_opcode_echo_location:
4451
 
+            return ngx_http_echo_exec_echo_location(r, ctx, computed_args);
4452
 
+            break;
4453
 
+
4454
 
+        case echo_opcode_echo_subrequest_async:
4455
 
+            dd("found opcode echo subrequest async...");
4456
 
+            rc = ngx_http_echo_exec_echo_subrequest_async(r, ctx,
4457
 
+                    computed_args);
4458
 
+            break;
4459
 
+
4460
 
+        case echo_opcode_echo_subrequest:
4461
 
+            return ngx_http_echo_exec_echo_subrequest(r, ctx, computed_args);
4462
 
+            break;
4463
 
+
4464
 
+        case echo_opcode_echo_sleep:
4465
 
+            return ngx_http_echo_exec_echo_sleep(r, ctx, computed_args);
4466
 
+            break;
4467
 
+
4468
 
+        case echo_opcode_echo_flush:
4469
 
+            rc = ngx_http_echo_exec_echo_flush(r, ctx);
4470
 
+            break;
4471
 
+
4472
 
+        case echo_opcode_echo_blocking_sleep:
4473
 
+            rc = ngx_http_echo_exec_echo_blocking_sleep(r, ctx,
4474
 
+                    computed_args);
4475
 
+            break;
4476
 
+
4477
 
+        case echo_opcode_echo_reset_timer:
4478
 
+            rc = ngx_http_echo_exec_echo_reset_timer(r, ctx);
4479
 
+            break;
4480
 
+
4481
 
+        case echo_opcode_echo_duplicate:
4482
 
+            rc = ngx_http_echo_exec_echo_duplicate(r, ctx, computed_args);
4483
 
+            break;
4484
 
+
4485
 
+        case echo_opcode_echo_read_request_body:
4486
 
+            ctx->wait_read_request_body = 0;
4487
 
+
4488
 
+            rc = ngx_http_echo_exec_echo_read_request_body(r, ctx);
4489
 
+
4490
 
+#if defined(nginx_version) && nginx_version >= 8011
4491
 
+            /* XXX read_client_request_body always increments the counter */
4492
 
+            r->main->count--;
4493
 
+#endif
4494
 
+
4495
 
+            dd("read request body: %d", (int) rc);
4496
 
+
4497
 
+            if (rc == NGX_OK) {
4498
 
+                continue;
4499
 
+            }
4500
 
+
4501
 
+            ctx->wait_read_request_body = 1;
4502
 
+
4503
 
+            /* r->write_event_handler = ngx_http_request_empty_handler; */
4504
 
+
4505
 
+            return rc;
4506
 
+            break;
4507
 
+
4508
 
+        case echo_opcode_echo_foreach_split:
4509
 
+            rc = ngx_http_echo_exec_echo_foreach_split(r, ctx, computed_args);
4510
 
+            break;
4511
 
+
4512
 
+        case echo_opcode_echo_end:
4513
 
+            rc = ngx_http_echo_exec_echo_end(r, ctx);
4514
 
+            break;
4515
 
+
4516
 
+        case echo_opcode_echo_exec:
4517
 
+            dd("echo_exec");
4518
 
+            return ngx_http_echo_exec_exec(r, ctx, computed_args);
4519
 
+            break;
4520
 
+
4521
 
+        default:
4522
 
+            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
4523
 
+                    "Unknown opcode: %d", cmd->opcode);
4524
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
4525
 
+            break;
4526
 
+        }
4527
 
+
4528
 
+        if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
4529
 
+            return rc;
4530
 
+        }
4531
 
+    }
4532
 
+
4533
 
+    rc = ngx_http_echo_send_chain_link(r, ctx, NULL /* indicate LAST */);
4534
 
+
4535
 
+    if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
4536
 
+        return rc;
4537
 
+    }
4538
 
+
4539
 
+    return NGX_OK;
4540
 
+}
4541
 
+
4542
 
+
4543
 
+ngx_int_t
4544
 
+ngx_http_echo_post_subrequest(ngx_http_request_t *r,
4545
 
+        void *data, ngx_int_t rc)
4546
 
+{
4547
 
+    ngx_http_request_t          *pr;
4548
 
+    ngx_http_echo_ctx_t         *pr_ctx;
4549
 
+
4550
 
+
4551
 
+    dd_enter();
4552
 
+
4553
 
+    pr = r->parent;
4554
 
+
4555
 
+    pr_ctx = ngx_http_get_module_ctx(pr, ngx_http_echo_module);
4556
 
+    if (pr_ctx == NULL) {
4557
 
+        return NGX_ERROR;
4558
 
+    }
4559
 
+
4560
 
+    dd("mark ready %d", (int) pr_ctx->next_handler_cmd);
4561
 
+
4562
 
+    pr_ctx->waiting = 0;
4563
 
+    pr_ctx->done = 1;
4564
 
+
4565
 
+    pr->write_event_handler = ngx_http_echo_wev_handler;
4566
 
+
4567
 
+    /* ensure that the parent request is (or will be)
4568
 
+     *  posted out the head of the r->posted_requests chain */
4569
 
+
4570
 
+    if (r->main->posted_requests
4571
 
+            && r->main->posted_requests->request != pr)
4572
 
+    {
4573
 
+        rc = ngx_http_echo_post_request_at_head(pr, NULL);
4574
 
+        if (rc != NGX_OK) {
4575
 
+            return NGX_ERROR;
4576
 
+        }
4577
 
+    }
4578
 
+
4579
 
+    return rc;
4580
 
+}
4581
 
+
4582
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_handler.h
4583
 
===================================================================
4584
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
4585
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_handler.h  2010-10-31 07:35:23.648338001 +0000
4586
 
@@ -0,0 +1,20 @@
4587
 
+#ifndef ECHO_HANDLER_H
4588
 
+#define ECHO_HANDLER_H
4589
 
+
4590
 
+#include "ngx_http_echo_module.h"
4591
 
+
4592
 
+
4593
 
+void ngx_http_echo_wev_handler(ngx_http_request_t *r);
4594
 
+
4595
 
+ngx_int_t ngx_http_echo_handler_init(ngx_conf_t *cf);
4596
 
+
4597
 
+ngx_int_t ngx_http_echo_handler(ngx_http_request_t *r);
4598
 
+
4599
 
+ngx_int_t ngx_http_echo_run_cmds(ngx_http_request_t *r);
4600
 
+
4601
 
+ngx_int_t ngx_http_echo_post_subrequest(ngx_http_request_t *r,
4602
 
+        void *data, ngx_int_t rc);
4603
 
+
4604
 
+
4605
 
+#endif /* ECHO_HANDLER_H */
4606
 
+
4607
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_location.c
4608
 
===================================================================
4609
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
4610
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_location.c 2010-10-31 07:35:23.648338001 +0000
4611
 
@@ -0,0 +1,183 @@
4612
 
+#define DDEBUG 0
4613
 
+#include "ddebug.h"
4614
 
+
4615
 
+#include "ngx_http_echo_util.h"
4616
 
+#include "ngx_http_echo_location.h"
4617
 
+#include "ngx_http_echo_handler.h"
4618
 
+
4619
 
+#include <nginx.h>
4620
 
+
4621
 
+
4622
 
+static ngx_int_t ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr);
4623
 
+
4624
 
+
4625
 
+ngx_int_t
4626
 
+ngx_http_echo_exec_echo_location_async(ngx_http_request_t *r,
4627
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
4628
 
+{
4629
 
+    ngx_int_t                    rc;
4630
 
+    ngx_http_request_t          *sr; /* subrequest object */
4631
 
+    ngx_str_t                   *computed_arg_elts;
4632
 
+    ngx_str_t                    location;
4633
 
+    ngx_str_t                   *url_args;
4634
 
+    ngx_str_t                    args;
4635
 
+    ngx_uint_t                   flags = 0;
4636
 
+
4637
 
+
4638
 
+    dd_enter();
4639
 
+
4640
 
+    computed_arg_elts = computed_args->elts;
4641
 
+
4642
 
+    location = computed_arg_elts[0];
4643
 
+
4644
 
+    if (location.len == 0) {
4645
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
4646
 
+    }
4647
 
+
4648
 
+    if (computed_args->nelts > 1) {
4649
 
+        url_args = &computed_arg_elts[1];
4650
 
+    } else {
4651
 
+        url_args = NULL;
4652
 
+    }
4653
 
+
4654
 
+    dd("location: %s", location.data);
4655
 
+    dd("location args: %s", (char*) (url_args ? url_args->data : (u_char*)"NULL"));
4656
 
+
4657
 
+    args.data = NULL;
4658
 
+    args.len = 0;
4659
 
+
4660
 
+    if (ngx_http_parse_unsafe_uri(r, &location, &args, &flags) != NGX_OK) {
4661
 
+        ctx->headers_sent = 1;
4662
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
4663
 
+    }
4664
 
+
4665
 
+    if (args.len > 0 && url_args == NULL) {
4666
 
+        url_args = &args;
4667
 
+    }
4668
 
+
4669
 
+    rc = ngx_http_echo_send_header_if_needed(r, ctx);
4670
 
+    if (r->header_only) {
4671
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
4672
 
+    }
4673
 
+
4674
 
+    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
4675
 
+        return rc;
4676
 
+    }
4677
 
+
4678
 
+    rc = ngx_http_subrequest(r, &location, url_args, &sr, NULL, 0);
4679
 
+
4680
 
+    if (rc != NGX_OK) {
4681
 
+        return NGX_ERROR;
4682
 
+    }
4683
 
+
4684
 
+    rc = ngx_http_echo_adjust_subrequest(sr);
4685
 
+    if (rc != NGX_OK) {
4686
 
+        return NGX_ERROR;
4687
 
+    }
4688
 
+
4689
 
+    return NGX_OK;
4690
 
+}
4691
 
+
4692
 
+
4693
 
+ngx_int_t
4694
 
+ngx_http_echo_exec_echo_location(ngx_http_request_t *r,
4695
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
4696
 
+{
4697
 
+    ngx_int_t                            rc;
4698
 
+    ngx_http_request_t                  *sr; /* subrequest object */
4699
 
+    ngx_str_t                           *computed_arg_elts;
4700
 
+    ngx_str_t                            location;
4701
 
+    ngx_str_t                           *url_args;
4702
 
+    ngx_http_post_subrequest_t          *psr;
4703
 
+    ngx_str_t                            args;
4704
 
+    ngx_uint_t                           flags = 0;
4705
 
+
4706
 
+
4707
 
+    dd_enter();
4708
 
+
4709
 
+    computed_arg_elts = computed_args->elts;
4710
 
+
4711
 
+    location = computed_arg_elts[0];
4712
 
+
4713
 
+    if (location.len == 0) {
4714
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
4715
 
+    }
4716
 
+
4717
 
+    if (computed_args->nelts > 1) {
4718
 
+        url_args = &computed_arg_elts[1];
4719
 
+    } else {
4720
 
+        url_args = NULL;
4721
 
+    }
4722
 
+
4723
 
+    args.data = NULL;
4724
 
+    args.len = 0;
4725
 
+
4726
 
+    if (ngx_http_parse_unsafe_uri(r, &location, &args, &flags) != NGX_OK) {
4727
 
+        ctx->headers_sent = 1;
4728
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
4729
 
+    }
4730
 
+
4731
 
+    if (args.len > 0 && url_args == NULL) {
4732
 
+        url_args = &args;
4733
 
+    }
4734
 
+
4735
 
+    rc = ngx_http_echo_send_header_if_needed(r, ctx);
4736
 
+
4737
 
+    if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
4738
 
+        return rc;
4739
 
+    }
4740
 
+
4741
 
+    psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
4742
 
+
4743
 
+    if (psr == NULL) {
4744
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
4745
 
+    }
4746
 
+
4747
 
+    psr->handler = ngx_http_echo_post_subrequest;
4748
 
+    psr->data = ctx;
4749
 
+
4750
 
+    rc = ngx_http_subrequest(r, &location, url_args, &sr, psr, 0);
4751
 
+
4752
 
+    if (rc != NGX_OK) {
4753
 
+        return NGX_ERROR;
4754
 
+    }
4755
 
+
4756
 
+    rc = ngx_http_echo_adjust_subrequest(sr);
4757
 
+
4758
 
+    if (rc != NGX_OK) {
4759
 
+        return NGX_ERROR;
4760
 
+    }
4761
 
+
4762
 
+    return NGX_AGAIN;
4763
 
+}
4764
 
+
4765
 
+
4766
 
+static ngx_int_t
4767
 
+ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr)
4768
 
+{
4769
 
+    ngx_http_core_main_conf_t   *cmcf;
4770
 
+    ngx_http_request_t          *r;
4771
 
+
4772
 
+
4773
 
+    /* we do not inherit the parent request's variables */
4774
 
+    cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module);
4775
 
+
4776
 
+    r = sr->parent;
4777
 
+
4778
 
+    sr->header_in = r->header_in;
4779
 
+
4780
 
+    /* XXX work-around a bug in ngx_http_subrequest */
4781
 
+    if (r->headers_in.headers.last == &r->headers_in.headers.part) {
4782
 
+        sr->headers_in.headers.last = &sr->headers_in.headers.part;
4783
 
+    }
4784
 
+
4785
 
+    sr->variables = ngx_pcalloc(sr->pool, cmcf->variables.nelts
4786
 
+                                        * sizeof(ngx_http_variable_value_t));
4787
 
+
4788
 
+    if (sr->variables == NULL) {
4789
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
4790
 
+    }
4791
 
+
4792
 
+    return NGX_OK;
4793
 
+}
4794
 
+
4795
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_location.h
4796
 
===================================================================
4797
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
4798
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_location.h 2010-10-31 07:35:23.648338001 +0000
4799
 
@@ -0,0 +1,13 @@
4800
 
+#ifndef ECHO_LOCATION_H
4801
 
+#define ECHO_LOCATION_H
4802
 
+
4803
 
+#include "ngx_http_echo_module.h"
4804
 
+
4805
 
+ngx_int_t ngx_http_echo_exec_echo_location_async(ngx_http_request_t *r,
4806
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args);
4807
 
+
4808
 
+ngx_int_t ngx_http_echo_exec_echo_location(ngx_http_request_t *r,
4809
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args);
4810
 
+
4811
 
+#endif /* ECHO_LOCATION_H */
4812
 
+
4813
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_module.c
4814
 
===================================================================
4815
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
4816
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_module.c   2010-10-31 07:35:23.648338001 +0000
4817
 
@@ -0,0 +1,587 @@
4818
 
+#define DDEBUG 0
4819
 
+#include "ddebug.h"
4820
 
+
4821
 
+#include "ngx_http_echo_handler.h"
4822
 
+#include "ngx_http_echo_filter.h"
4823
 
+#include "ngx_http_echo_request_info.h"
4824
 
+
4825
 
+#include <nginx.h>
4826
 
+#include <ngx_config.h>
4827
 
+#include <ngx_log.h>
4828
 
+
4829
 
+/* config init handler */
4830
 
+static void * ngx_http_echo_create_conf(ngx_conf_t *cf);
4831
 
+
4832
 
+/* config directive handlers */
4833
 
+static char * ngx_http_echo_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
4834
 
+
4835
 
+static char * ngx_http_echo_echo_request_body(ngx_conf_t *cf,
4836
 
+        ngx_command_t *cmd, void *conf);
4837
 
+
4838
 
+static char * ngx_http_echo_echo_sleep(ngx_conf_t *cf, ngx_command_t *cmd,
4839
 
+        void *conf);
4840
 
+
4841
 
+static char * ngx_http_echo_echo_flush(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
4842
 
+
4843
 
+static char * ngx_http_echo_echo_blocking_sleep(ngx_conf_t *cf,
4844
 
+        ngx_command_t *cmd, void *conf);
4845
 
+
4846
 
+static char * ngx_http_echo_echo_reset_timer(ngx_conf_t *cf,
4847
 
+        ngx_command_t *cmd, void *conf);
4848
 
+
4849
 
+static char * ngx_http_echo_echo_before_body(ngx_conf_t *cf,
4850
 
+        ngx_command_t *cmd, void *conf);
4851
 
+
4852
 
+static char * ngx_http_echo_echo_after_body(ngx_conf_t *cf,
4853
 
+        ngx_command_t *cmd, void *conf);
4854
 
+
4855
 
+static char * ngx_http_echo_echo_location_async(ngx_conf_t *cf,
4856
 
+        ngx_command_t *cmd, void *conf);
4857
 
+
4858
 
+static char * ngx_http_echo_echo_location(ngx_conf_t *cf,
4859
 
+        ngx_command_t *cmd, void *conf);
4860
 
+
4861
 
+static char * ngx_http_echo_echo_subrequest_async(ngx_conf_t *cf,
4862
 
+        ngx_command_t *cmd, void *conf);
4863
 
+
4864
 
+static char * ngx_http_echo_echo_subrequest(ngx_conf_t *cf,
4865
 
+        ngx_command_t *cmd, void *conf);
4866
 
+
4867
 
+static char * ngx_http_echo_echo_duplicate(ngx_conf_t *cf,
4868
 
+        ngx_command_t *cmd, void *conf);
4869
 
+
4870
 
+static char * ngx_http_echo_echo_read_request_body(ngx_conf_t *cf,
4871
 
+        ngx_command_t *cmd, void *conf);
4872
 
+
4873
 
+static char * ngx_http_echo_echo_foreach_split(ngx_conf_t *cf,
4874
 
+        ngx_command_t *cmd, void *conf);
4875
 
+
4876
 
+static char * ngx_http_echo_echo_end(ngx_conf_t *cf,
4877
 
+        ngx_command_t *cmd, void *conf);
4878
 
+
4879
 
+static char * ngx_http_echo_echo_abort_parent(ngx_conf_t *cf, ngx_command_t *cmd,
4880
 
+        void *conf);
4881
 
+
4882
 
+static char * ngx_http_echo_echo_exec(ngx_conf_t *cf,
4883
 
+        ngx_command_t *cmd, void *conf);
4884
 
+
4885
 
+static char * ngx_http_echo_helper(ngx_http_echo_opcode_t opcode,
4886
 
+        ngx_http_echo_cmd_category_t cat,
4887
 
+        ngx_conf_t *cf, ngx_command_t *cmd, void* conf);
4888
 
+
4889
 
+
4890
 
+static ngx_http_module_t ngx_http_echo_module_ctx = {
4891
 
+    /* TODO we could add our own variables here... */
4892
 
+    ngx_http_echo_handler_init,                 /* preconfiguration */
4893
 
+    ngx_http_echo_filter_init,                  /* postconfiguration */
4894
 
+
4895
 
+    NULL,                          /* create main configuration */
4896
 
+    NULL,                          /* init main configuration */
4897
 
+
4898
 
+    NULL,                          /* create server configuration */
4899
 
+    NULL,                          /* merge server configuration */
4900
 
+
4901
 
+    ngx_http_echo_create_conf, /* create location configuration */
4902
 
+    NULL                           /* merge location configuration */
4903
 
+};
4904
 
+
4905
 
+
4906
 
+static ngx_command_t  ngx_http_echo_commands[] = {
4907
 
+
4908
 
+    { ngx_string("echo"),
4909
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY,
4910
 
+      ngx_http_echo_echo,
4911
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4912
 
+      offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
4913
 
+      NULL },
4914
 
+
4915
 
+    { ngx_string("echo_request_body"),
4916
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
4917
 
+      ngx_http_echo_echo_request_body,
4918
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4919
 
+      offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
4920
 
+      NULL },
4921
 
+
4922
 
+    { ngx_string("echo_sleep"),
4923
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
4924
 
+      ngx_http_echo_echo_sleep,
4925
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4926
 
+      offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
4927
 
+      NULL },
4928
 
+
4929
 
+    { ngx_string("echo_flush"),
4930
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
4931
 
+      ngx_http_echo_echo_flush,
4932
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4933
 
+      offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
4934
 
+      NULL },
4935
 
+
4936
 
+    { ngx_string("echo_blocking_sleep"),
4937
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
4938
 
+      ngx_http_echo_echo_blocking_sleep,
4939
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4940
 
+      offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
4941
 
+      NULL },
4942
 
+
4943
 
+    { ngx_string("echo_reset_timer"),
4944
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
4945
 
+      ngx_http_echo_echo_reset_timer,
4946
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4947
 
+      offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
4948
 
+      NULL },
4949
 
+
4950
 
+    { ngx_string("echo_before_body"),
4951
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY,
4952
 
+      ngx_http_echo_echo_before_body,
4953
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4954
 
+      offsetof(ngx_http_echo_loc_conf_t, before_body_cmds),
4955
 
+      NULL },
4956
 
+
4957
 
+    { ngx_string("echo_after_body"),
4958
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY,
4959
 
+      ngx_http_echo_echo_after_body,
4960
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4961
 
+      offsetof(ngx_http_echo_loc_conf_t, after_body_cmds),
4962
 
+      NULL },
4963
 
+
4964
 
+    { ngx_string("echo_location_async"),
4965
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12,
4966
 
+      ngx_http_echo_echo_location_async,
4967
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4968
 
+      0,
4969
 
+      NULL },
4970
 
+
4971
 
+    { ngx_string("echo_location"),
4972
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12,
4973
 
+      ngx_http_echo_echo_location,
4974
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4975
 
+      0,
4976
 
+      NULL },
4977
 
+
4978
 
+    { ngx_string("echo_subrequest_async"),
4979
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE,
4980
 
+      ngx_http_echo_echo_subrequest_async,
4981
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4982
 
+      0,
4983
 
+      NULL },
4984
 
+
4985
 
+    { ngx_string("echo_subrequest"),
4986
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE,
4987
 
+      ngx_http_echo_echo_subrequest,
4988
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4989
 
+      0,
4990
 
+      NULL },
4991
 
+
4992
 
+    { ngx_string("echo_duplicate"),
4993
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE,
4994
 
+      ngx_http_echo_echo_duplicate,
4995
 
+      NGX_HTTP_LOC_CONF_OFFSET,
4996
 
+      0,
4997
 
+      NULL },
4998
 
+
4999
 
+    { ngx_string("echo_read_request_body"),
5000
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
5001
 
+      ngx_http_echo_echo_read_request_body,
5002
 
+      NGX_HTTP_LOC_CONF_OFFSET,
5003
 
+      offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
5004
 
+      NULL },
5005
 
+
5006
 
+    { ngx_string("echo_foreach_split"),
5007
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE,
5008
 
+      ngx_http_echo_echo_foreach_split,
5009
 
+      NGX_HTTP_LOC_CONF_OFFSET,
5010
 
+      offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
5011
 
+      NULL },
5012
 
+
5013
 
+    { ngx_string("echo_end"),
5014
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
5015
 
+      ngx_http_echo_echo_end,
5016
 
+      NGX_HTTP_LOC_CONF_OFFSET,
5017
 
+      offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
5018
 
+      NULL },
5019
 
+
5020
 
+    { ngx_string("echo_abort_parent"),
5021
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
5022
 
+      ngx_http_echo_echo_abort_parent,
5023
 
+      NGX_HTTP_LOC_CONF_OFFSET,
5024
 
+      offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
5025
 
+      NULL },
5026
 
+
5027
 
+    { ngx_string("echo_exec"),
5028
 
+      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12,
5029
 
+      ngx_http_echo_echo_exec,
5030
 
+      NGX_HTTP_LOC_CONF_OFFSET,
5031
 
+      offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
5032
 
+      NULL },
5033
 
+
5034
 
+      ngx_null_command
5035
 
+};
5036
 
+
5037
 
+
5038
 
+ngx_module_t ngx_http_echo_module = {
5039
 
+    NGX_MODULE_V1,
5040
 
+    &ngx_http_echo_module_ctx, /* module context */
5041
 
+    ngx_http_echo_commands,   /* module directives */
5042
 
+    NGX_HTTP_MODULE,               /* module type */
5043
 
+    NULL,                          /* init master */
5044
 
+    NULL,                          /* init module */
5045
 
+    NULL,                          /* init process */
5046
 
+    NULL,                          /* init thread */
5047
 
+    NULL,                          /* exit thread */
5048
 
+    NULL,                          /* exit process */
5049
 
+    NULL,                          /* exit master */
5050
 
+    NGX_MODULE_V1_PADDING
5051
 
+};
5052
 
+
5053
 
+
5054
 
+static void *
5055
 
+ngx_http_echo_create_conf(ngx_conf_t *cf)
5056
 
+{
5057
 
+    ngx_http_echo_loc_conf_t *conf;
5058
 
+    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t));
5059
 
+    if (conf == NULL) {
5060
 
+        return NGX_CONF_ERROR;
5061
 
+    }
5062
 
+
5063
 
+    return conf;
5064
 
+}
5065
 
+
5066
 
+
5067
 
+static char *
5068
 
+ngx_http_echo_helper(ngx_http_echo_opcode_t opcode,
5069
 
+        ngx_http_echo_cmd_category_t cat,
5070
 
+        ngx_conf_t *cf, ngx_command_t *cmd, void* conf)
5071
 
+{
5072
 
+    ngx_http_core_loc_conf_t        *clcf;
5073
 
+    /* ngx_http_echo_loc_conf_t        *ulcf = conf; */
5074
 
+    ngx_array_t                    **args_ptr;
5075
 
+    ngx_http_script_compile_t        sc;
5076
 
+    ngx_str_t                       *raw_args;
5077
 
+    ngx_http_echo_arg_template_t    *arg;
5078
 
+    ngx_array_t                    **cmds_ptr;
5079
 
+    ngx_http_echo_cmd_t             *echo_cmd;
5080
 
+    ngx_uint_t                       i, n;
5081
 
+
5082
 
+    /* cmds_ptr points to ngx_http_echo_loc_conf_t's
5083
 
+     * handler_cmds, before_body_cmds, or after_body_cmds
5084
 
+     * array, depending on the actual offset */
5085
 
+    cmds_ptr = (ngx_array_t**)(((u_char*)conf) + cmd->offset);
5086
 
+
5087
 
+    if (*cmds_ptr == NULL) {
5088
 
+        *cmds_ptr = ngx_array_create(cf->pool, 1,
5089
 
+                sizeof(ngx_http_echo_cmd_t));
5090
 
+
5091
 
+        if (*cmds_ptr == NULL) {
5092
 
+            return NGX_CONF_ERROR;
5093
 
+        }
5094
 
+
5095
 
+        if (cat == echo_handler_cmd) {
5096
 
+            dd("registering the content handler");
5097
 
+            /* register the content handler */
5098
 
+            clcf = ngx_http_conf_get_module_loc_conf(cf,
5099
 
+                    ngx_http_core_module);
5100
 
+
5101
 
+            dd("registering the content handler (2)");
5102
 
+            clcf->handler = ngx_http_echo_handler;
5103
 
+
5104
 
+        } else {
5105
 
+            dd("filter used = 1");
5106
 
+            ngx_http_echo_filter_used = 1;
5107
 
+        }
5108
 
+    }
5109
 
+
5110
 
+    echo_cmd = ngx_array_push(*cmds_ptr);
5111
 
+
5112
 
+    if (echo_cmd == NULL) {
5113
 
+        return NGX_CONF_ERROR;
5114
 
+    }
5115
 
+
5116
 
+    echo_cmd->opcode = opcode;
5117
 
+
5118
 
+    args_ptr = &echo_cmd->args;
5119
 
+    *args_ptr = ngx_array_create(cf->pool, 1,
5120
 
+            sizeof(ngx_http_echo_arg_template_t));
5121
 
+
5122
 
+    if (*args_ptr == NULL) {
5123
 
+        return NGX_CONF_ERROR;
5124
 
+    }
5125
 
+
5126
 
+    raw_args = cf->args->elts;
5127
 
+
5128
 
+    /* we skip the first arg and start from the second */
5129
 
+
5130
 
+    for (i = 1 ; i < cf->args->nelts; i++) {
5131
 
+        arg = ngx_array_push(*args_ptr);
5132
 
+
5133
 
+        if (arg == NULL) {
5134
 
+            return NGX_CONF_ERROR;
5135
 
+        }
5136
 
+
5137
 
+        arg->raw_value = raw_args[i];
5138
 
+
5139
 
+        dd("found raw arg %s", raw_args[i].data);
5140
 
+
5141
 
+        arg->lengths = NULL;
5142
 
+        arg->values  = NULL;
5143
 
+
5144
 
+        n = ngx_http_script_variables_count(&arg->raw_value);
5145
 
+
5146
 
+        if (n > 0) {
5147
 
+            ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
5148
 
+
5149
 
+            sc.cf = cf;
5150
 
+            sc.source = &arg->raw_value;
5151
 
+            sc.lengths = &arg->lengths;
5152
 
+            sc.values = &arg->values;
5153
 
+            sc.variables = n;
5154
 
+            sc.complete_lengths = 1;
5155
 
+            sc.complete_values = 1;
5156
 
+
5157
 
+            if (ngx_http_script_compile(&sc) != NGX_OK) {
5158
 
+                return NGX_CONF_ERROR;
5159
 
+            }
5160
 
+        }
5161
 
+    } /* end for */
5162
 
+
5163
 
+    return NGX_CONF_OK;
5164
 
+}
5165
 
+
5166
 
+
5167
 
+static char *
5168
 
+ngx_http_echo_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
5169
 
+{
5170
 
+    dd("in echo_echo...");
5171
 
+    return ngx_http_echo_helper(echo_opcode_echo,
5172
 
+            echo_handler_cmd,
5173
 
+            cf, cmd, conf);
5174
 
+}
5175
 
+
5176
 
+
5177
 
+static char *
5178
 
+ngx_http_echo_echo_request_body(ngx_conf_t *cf,
5179
 
+        ngx_command_t *cmd, void *conf)
5180
 
+{
5181
 
+    dd("in echo_echo_request_body...");
5182
 
+    return ngx_http_echo_helper(echo_opcode_echo_request_body,
5183
 
+            echo_handler_cmd,
5184
 
+            cf, cmd, conf);
5185
 
+}
5186
 
+
5187
 
+
5188
 
+static char *
5189
 
+ngx_http_echo_echo_sleep(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
5190
 
+{
5191
 
+    dd("in echo_sleep...");
5192
 
+    return ngx_http_echo_helper(echo_opcode_echo_sleep,
5193
 
+            echo_handler_cmd,
5194
 
+            cf, cmd, conf);
5195
 
+}
5196
 
+
5197
 
+
5198
 
+static char *
5199
 
+ngx_http_echo_echo_flush(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
5200
 
+{
5201
 
+    dd("in echo_flush...");
5202
 
+    return ngx_http_echo_helper(echo_opcode_echo_flush,
5203
 
+            echo_handler_cmd,
5204
 
+            cf, cmd, conf);
5205
 
+}
5206
 
+
5207
 
+
5208
 
+static char *
5209
 
+ngx_http_echo_echo_blocking_sleep(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
5210
 
+{
5211
 
+    dd("in echo_blocking_sleep...");
5212
 
+    return ngx_http_echo_helper(echo_opcode_echo_blocking_sleep,
5213
 
+            echo_handler_cmd,
5214
 
+            cf, cmd, conf);
5215
 
+}
5216
 
+
5217
 
+
5218
 
+static char *
5219
 
+ngx_http_echo_echo_reset_timer(ngx_conf_t *cf,
5220
 
+        ngx_command_t *cmd, void *conf)
5221
 
+{
5222
 
+    return ngx_http_echo_helper(echo_opcode_echo_reset_timer,
5223
 
+            echo_handler_cmd,
5224
 
+            cf, cmd, conf);
5225
 
+}
5226
 
+
5227
 
+
5228
 
+static char *
5229
 
+ngx_http_echo_echo_before_body(ngx_conf_t *cf,
5230
 
+        ngx_command_t *cmd, void *conf)
5231
 
+{
5232
 
+    dd("processing echo_before_body directive...");
5233
 
+    return ngx_http_echo_helper(echo_opcode_echo_before_body,
5234
 
+            echo_filter_cmd,
5235
 
+            cf, cmd, conf);
5236
 
+}
5237
 
+
5238
 
+
5239
 
+static char *
5240
 
+ngx_http_echo_echo_after_body(ngx_conf_t *cf,
5241
 
+        ngx_command_t *cmd, void *conf)
5242
 
+{
5243
 
+    return ngx_http_echo_helper(echo_opcode_echo_after_body,
5244
 
+            echo_filter_cmd,
5245
 
+            cf, cmd, conf);
5246
 
+}
5247
 
+
5248
 
+
5249
 
+static char *
5250
 
+ ngx_http_echo_echo_location_async(ngx_conf_t *cf, ngx_command_t *cmd,
5251
 
+         void *conf)
5252
 
+{
5253
 
+
5254
 
+#if ! defined(nginx_version)
5255
 
+
5256
 
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
5257
 
+            "Directive echo_location_async does not work with nginx "
5258
 
+            "versions ealier than 0.7.46.");
5259
 
+
5260
 
+    return NGX_CONF_ERROR;
5261
 
+
5262
 
+#else
5263
 
+
5264
 
+    return ngx_http_echo_helper(echo_opcode_echo_location_async,
5265
 
+            echo_handler_cmd,
5266
 
+            cf, cmd, conf);
5267
 
+
5268
 
+#endif
5269
 
+
5270
 
+}
5271
 
+
5272
 
+
5273
 
+static char *
5274
 
+ ngx_http_echo_echo_location(ngx_conf_t *cf, ngx_command_t *cmd,
5275
 
+         void *conf)
5276
 
+{
5277
 
+
5278
 
+#if ! defined(nginx_version)
5279
 
+
5280
 
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
5281
 
+            "Directive echo_location does not work with nginx "
5282
 
+            "versions ealier than 0.7.46.");
5283
 
+
5284
 
+    return NGX_CONF_ERROR;
5285
 
+
5286
 
+#else
5287
 
+
5288
 
+    return ngx_http_echo_helper(echo_opcode_echo_location,
5289
 
+            echo_handler_cmd,
5290
 
+            cf, cmd, conf);
5291
 
+
5292
 
+#endif
5293
 
+
5294
 
+}
5295
 
+
5296
 
+
5297
 
+static char *
5298
 
+ ngx_http_echo_echo_subrequest_async(ngx_conf_t *cf, ngx_command_t *cmd,
5299
 
+         void *conf)
5300
 
+{
5301
 
+
5302
 
+#if ! defined(nginx_version)
5303
 
+
5304
 
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
5305
 
+            "Directive echo_subrequest_async does not work with nginx "
5306
 
+            "versions ealier than 0.7.46.");
5307
 
+
5308
 
+    return NGX_CONF_ERROR;
5309
 
+
5310
 
+#else
5311
 
+
5312
 
+    return ngx_http_echo_helper(echo_opcode_echo_subrequest_async,
5313
 
+            echo_handler_cmd,
5314
 
+            cf, cmd, conf);
5315
 
+
5316
 
+#endif
5317
 
+
5318
 
+}
5319
 
+
5320
 
+
5321
 
+static char *
5322
 
+ ngx_http_echo_echo_subrequest(ngx_conf_t *cf, ngx_command_t *cmd,
5323
 
+         void *conf)
5324
 
+{
5325
 
+
5326
 
+#if ! defined(nginx_version)
5327
 
+
5328
 
+    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
5329
 
+            "Directive echo_subrequest does not work with nginx "
5330
 
+            "versions ealier than 0.7.46.");
5331
 
+
5332
 
+    return NGX_CONF_ERROR;
5333
 
+
5334
 
+#else
5335
 
+
5336
 
+    return ngx_http_echo_helper(echo_opcode_echo_subrequest,
5337
 
+            echo_handler_cmd,
5338
 
+            cf, cmd, conf);
5339
 
+
5340
 
+#endif
5341
 
+
5342
 
+}
5343
 
+
5344
 
+
5345
 
+static char *
5346
 
+ngx_http_echo_echo_duplicate(ngx_conf_t *cf,
5347
 
+        ngx_command_t *cmd, void *conf)
5348
 
+{
5349
 
+    return ngx_http_echo_helper(echo_opcode_echo_duplicate,
5350
 
+            echo_handler_cmd,
5351
 
+            cf, cmd, conf);
5352
 
+}
5353
 
+
5354
 
+
5355
 
+static char *
5356
 
+ngx_http_echo_echo_read_request_body(ngx_conf_t *cf,
5357
 
+        ngx_command_t *cmd, void *conf)
5358
 
+{
5359
 
+    return ngx_http_echo_helper(
5360
 
+            echo_opcode_echo_read_request_body,
5361
 
+            echo_handler_cmd,
5362
 
+            cf, cmd, conf);
5363
 
+}
5364
 
+
5365
 
+
5366
 
+static char *
5367
 
+ngx_http_echo_echo_foreach_split(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
5368
 
+{
5369
 
+    return ngx_http_echo_helper(
5370
 
+            echo_opcode_echo_foreach_split,
5371
 
+            echo_handler_cmd,
5372
 
+            cf, cmd, conf);
5373
 
+}
5374
 
+
5375
 
+
5376
 
+static char *
5377
 
+ngx_http_echo_echo_end(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
5378
 
+{
5379
 
+    return ngx_http_echo_helper(
5380
 
+            echo_opcode_echo_end,
5381
 
+            echo_handler_cmd,
5382
 
+            cf, cmd, conf);
5383
 
+}
5384
 
+
5385
 
+
5386
 
+static char *
5387
 
+ngx_http_echo_echo_abort_parent(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
5388
 
+{
5389
 
+    return ngx_http_echo_helper(
5390
 
+            echo_opcode_echo_abort_parent,
5391
 
+            echo_handler_cmd,
5392
 
+            cf, cmd, conf);
5393
 
+}
5394
 
+
5395
 
+
5396
 
+static char *
5397
 
+ngx_http_echo_echo_exec(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
5398
 
+{
5399
 
+    return ngx_http_echo_helper(
5400
 
+            echo_opcode_echo_exec,
5401
 
+            echo_handler_cmd,
5402
 
+            cf, cmd, conf);
5403
 
+}
5404
 
+
5405
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_module.h
5406
 
===================================================================
5407
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
5408
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_module.h   2010-10-31 07:35:23.648338001 +0000
5409
 
@@ -0,0 +1,118 @@
5410
 
+/* Copyright (C) by agentzh */
5411
 
+
5412
 
+#ifndef NGX_HTTP_ECHO_MODULE_H
5413
 
+#define NGX_HTTP_ECHO_MODULE_H
5414
 
+
5415
 
+#include <ngx_core.h>
5416
 
+#include <ngx_http.h>
5417
 
+#include <nginx.h>
5418
 
+
5419
 
+extern ngx_module_t ngx_http_echo_module;
5420
 
+
5421
 
+/* config directive's opcode */
5422
 
+typedef enum {
5423
 
+    echo_opcode_echo,
5424
 
+    echo_opcode_echo_request_body,
5425
 
+    echo_opcode_echo_sleep,
5426
 
+    echo_opcode_echo_flush,
5427
 
+    echo_opcode_echo_blocking_sleep,
5428
 
+    echo_opcode_echo_reset_timer,
5429
 
+    echo_opcode_echo_before_body,
5430
 
+    echo_opcode_echo_after_body,
5431
 
+    echo_opcode_echo_location_async,
5432
 
+    echo_opcode_echo_location,
5433
 
+    echo_opcode_echo_subrequest_async,
5434
 
+    echo_opcode_echo_subrequest,
5435
 
+    echo_opcode_echo_duplicate,
5436
 
+    echo_opcode_echo_read_request_body,
5437
 
+    echo_opcode_echo_foreach_split,
5438
 
+    echo_opcode_echo_end,
5439
 
+    echo_opcode_echo_abort_parent,
5440
 
+    echo_opcode_echo_exec
5441
 
+} ngx_http_echo_opcode_t;
5442
 
+
5443
 
+/* all the various config directives (or commands) are
5444
 
+ * divided into two categories: "handler commands",
5445
 
+ * and "filter commands". For instance, the "echo"
5446
 
+ * directive is a handler command while
5447
 
+ * "echo_before_body" is a filter one. */
5448
 
+typedef enum {
5449
 
+    echo_handler_cmd,
5450
 
+    echo_filter_cmd
5451
 
+
5452
 
+} ngx_http_echo_cmd_category_t;
5453
 
+
5454
 
+/* compiled form of a config directive argument's value */
5455
 
+typedef struct {
5456
 
+    /* holds the raw string of the argument value */
5457
 
+    ngx_str_t       raw_value;
5458
 
+
5459
 
+    /* fields "lengths" and "values" are set by
5460
 
+     * the function ngx_http_script_compile,
5461
 
+     * iff the argument value indeed contains
5462
 
+     * nginx variables like "$foo" */
5463
 
+    ngx_array_t     *lengths;
5464
 
+    ngx_array_t     *values;
5465
 
+
5466
 
+} ngx_http_echo_arg_template_t;
5467
 
+
5468
 
+/* represent a config directive (or command) like "echo". */
5469
 
+typedef struct {
5470
 
+    ngx_http_echo_opcode_t      opcode;
5471
 
+
5472
 
+    /* each argument is of type echo_arg_template_t: */
5473
 
+    ngx_array_t                 *args;
5474
 
+} ngx_http_echo_cmd_t;
5475
 
+
5476
 
+/* location config struct */
5477
 
+typedef struct {
5478
 
+    /* elements of the following arrays are of type
5479
 
+     * ngx_http_echo_cmd_t */
5480
 
+    ngx_array_t     *handler_cmds;
5481
 
+    ngx_array_t     *before_body_cmds;
5482
 
+    ngx_array_t     *after_body_cmds;
5483
 
+
5484
 
+} ngx_http_echo_loc_conf_t;
5485
 
+
5486
 
+typedef struct {
5487
 
+    ngx_array_t     *choices; /* items after splitting */
5488
 
+    ngx_uint_t      next_choice;  /* current item index */
5489
 
+    ngx_uint_t      cmd_index; /* cmd index for the echo_foreach direcitve */
5490
 
+} ngx_http_echo_foreach_ctx_t;
5491
 
+
5492
 
+/* context struct in the request handling cycle, holding
5493
 
+ * the current states of the command evaluator */
5494
 
+typedef struct {
5495
 
+    /* index of the next handler command in
5496
 
+     * ngx_http_echo_loc_conf_t's "handler_cmds" array. */
5497
 
+    ngx_uint_t       next_handler_cmd;
5498
 
+
5499
 
+    /* index of the next before-body filter command in
5500
 
+     * ngx_http_echo_loc_conf_t's "before_body_cmds" array. */
5501
 
+    ngx_uint_t       next_before_body_cmd;
5502
 
+
5503
 
+    /* index of the next after-body filter command in
5504
 
+     * ngx_http_echo_loc_conf_t's "after_body_cmds" array. */
5505
 
+    ngx_uint_t       next_after_body_cmd;
5506
 
+
5507
 
+    ngx_http_echo_foreach_ctx_t   *foreach;
5508
 
+
5509
 
+    ngx_flag_t       headers_sent;
5510
 
+    ngx_flag_t       before_body_sent;
5511
 
+    ngx_flag_t       skip_filter;
5512
 
+
5513
 
+    ngx_time_t       timer_begin;
5514
 
+
5515
 
+    ngx_event_t      sleep;
5516
 
+
5517
 
+    ngx_uint_t       counter;
5518
 
+
5519
 
+    ngx_flag_t       wait_read_request_body;
5520
 
+
5521
 
+    ngx_flag_t       waiting;
5522
 
+    ngx_flag_t       done;
5523
 
+} ngx_http_echo_ctx_t;
5524
 
+
5525
 
+
5526
 
+#endif /* NGX_HTTP_ECHO_MODULE_H */
5527
 
+
5528
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_request_info.c
5529
 
===================================================================
5530
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
5531
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_request_info.c     2010-10-31 07:35:23.648338001 +0000
5532
 
@@ -0,0 +1,269 @@
5533
 
+#define DDEBUG 0
5534
 
+#include "ddebug.h"
5535
 
+
5536
 
+#include "ngx_http_echo_request_info.h"
5537
 
+#include "ngx_http_echo_util.h"
5538
 
+#include "ngx_http_echo_handler.h"
5539
 
+
5540
 
+#include <nginx.h>
5541
 
+
5542
 
+
5543
 
+static void ngx_http_echo_post_read_request_body(ngx_http_request_t *r);
5544
 
+
5545
 
+ngx_int_t
5546
 
+ngx_http_echo_exec_echo_read_request_body(
5547
 
+        ngx_http_request_t* r, ngx_http_echo_ctx_t *ctx)
5548
 
+{
5549
 
+    return ngx_http_read_client_request_body(r, ngx_http_echo_post_read_request_body);
5550
 
+}
5551
 
+
5552
 
+
5553
 
+static void
5554
 
+ngx_http_echo_post_read_request_body(ngx_http_request_t *r)
5555
 
+{
5556
 
+    ngx_http_echo_ctx_t         *ctx;
5557
 
+
5558
 
+    ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module);
5559
 
+
5560
 
+    dd("wait read request body %d", (int) ctx->wait_read_request_body);
5561
 
+
5562
 
+    if (ctx->wait_read_request_body) {
5563
 
+        ctx->waiting = 0;
5564
 
+        ctx->done = 1;
5565
 
+
5566
 
+        r->write_event_handler = ngx_http_echo_wev_handler;
5567
 
+
5568
 
+        ngx_http_echo_wev_handler(r);
5569
 
+    }
5570
 
+}
5571
 
+
5572
 
+
5573
 
+/* this function's implementation is borrowed from nginx 0.8.20
5574
 
+ * and modified a bit to work with subrequests.
5575
 
+ * Copyrighted (C) by Igor Sysoev */
5576
 
+ngx_int_t
5577
 
+ngx_http_echo_request_method_variable(ngx_http_request_t *r,
5578
 
+        ngx_http_variable_value_t *v, uintptr_t data)
5579
 
+{
5580
 
+    if (r->method_name.data) {
5581
 
+        v->len = r->method_name.len;
5582
 
+        v->valid = 1;
5583
 
+        v->no_cacheable = 0;
5584
 
+        v->not_found = 0;
5585
 
+        v->data = r->method_name.data;
5586
 
+    } else {
5587
 
+        v->not_found = 1;
5588
 
+    }
5589
 
+
5590
 
+    return NGX_OK;
5591
 
+}
5592
 
+
5593
 
+
5594
 
+/* this function's implementation is borrowed from nginx 0.8.20
5595
 
+ * and modified a bit to work with subrequests.
5596
 
+ * Copyrighted (C) by Igor Sysoev */
5597
 
+ngx_int_t
5598
 
+ngx_http_echo_client_request_method_variable(ngx_http_request_t *r,
5599
 
+        ngx_http_variable_value_t *v, uintptr_t data)
5600
 
+{
5601
 
+    if (r->main->method_name.data) {
5602
 
+        v->len = r->main->method_name.len;
5603
 
+        v->valid = 1;
5604
 
+        v->no_cacheable = 0;
5605
 
+        v->not_found = 0;
5606
 
+        v->data = r->main->method_name.data;
5607
 
+    } else {
5608
 
+        v->not_found = 1;
5609
 
+    }
5610
 
+
5611
 
+    return NGX_OK;
5612
 
+}
5613
 
+
5614
 
+
5615
 
+/* this function's implementation is borrowed from nginx 0.8.20
5616
 
+ * and modified a bit to work with subrequests.
5617
 
+ * Copyrighted (C) by Igor Sysoev */
5618
 
+ngx_int_t
5619
 
+ngx_http_echo_request_body_variable(ngx_http_request_t *r,
5620
 
+        ngx_http_variable_value_t *v, uintptr_t data)
5621
 
+{
5622
 
+    u_char       *p;
5623
 
+    size_t        len;
5624
 
+    ngx_buf_t    *buf, *next;
5625
 
+    ngx_chain_t  *cl;
5626
 
+
5627
 
+#if 0
5628
 
+
5629
 
+    dd("rrr request_body null ? %d", r->request_body == NULL);
5630
 
+    if (r->request_body) {
5631
 
+        dd("rrr request_body bufs null ? %d", r->request_body->bufs == NULL);
5632
 
+        dd("rrr request_body temp file ? %d", r->request_body->temp_file != NULL);
5633
 
+    }
5634
 
+    dd("rrr request_body content length ? %ld", (long) r->headers_in.content_length_n);
5635
 
+
5636
 
+#endif
5637
 
+
5638
 
+    if (r->request_body == NULL
5639
 
+        || r->request_body->bufs == NULL
5640
 
+        || r->request_body->temp_file)
5641
 
+    {
5642
 
+        v->not_found = 1;
5643
 
+
5644
 
+        return NGX_OK;
5645
 
+    }
5646
 
+
5647
 
+    cl = r->request_body->bufs;
5648
 
+    buf = cl->buf;
5649
 
+
5650
 
+    if (cl->next == NULL) {
5651
 
+        v->len = buf->last - buf->pos;
5652
 
+        v->valid = 1;
5653
 
+        v->no_cacheable = 0;
5654
 
+        v->not_found = 0;
5655
 
+        v->data = buf->pos;
5656
 
+
5657
 
+        return NGX_OK;
5658
 
+    }
5659
 
+
5660
 
+    next = cl->next->buf;
5661
 
+    len = (buf->last - buf->pos) + (next->last - next->pos);
5662
 
+
5663
 
+    p = ngx_pnalloc(r->pool, len);
5664
 
+    if (p == NULL) {
5665
 
+        return NGX_ERROR;
5666
 
+    }
5667
 
+
5668
 
+    v->data = p;
5669
 
+
5670
 
+    p = ngx_cpymem(p, buf->pos, buf->last - buf->pos);
5671
 
+    ngx_memcpy(p, next->pos, next->last - next->pos);
5672
 
+
5673
 
+    v->len = len;
5674
 
+    v->valid = 1;
5675
 
+    v->no_cacheable = 0;
5676
 
+    v->not_found = 0;
5677
 
+
5678
 
+    return NGX_OK;
5679
 
+}
5680
 
+
5681
 
+
5682
 
+ngx_int_t
5683
 
+ngx_http_echo_client_request_headers_variable(ngx_http_request_t *r,
5684
 
+        ngx_http_variable_value_t *v, uintptr_t data)
5685
 
+{
5686
 
+    size_t                      size;
5687
 
+    u_char                      *p, *last;
5688
 
+    ngx_buf_t                   *header_in;
5689
 
+    ngx_flag_t                  just_seen_crlf;
5690
 
+
5691
 
+    if (r != r->main) {
5692
 
+        header_in = r->main->header_in;
5693
 
+    } else {
5694
 
+        header_in = r->header_in;
5695
 
+    }
5696
 
+
5697
 
+    if (header_in == NULL) {
5698
 
+        v->not_found = 1;
5699
 
+        return NGX_OK;
5700
 
+    }
5701
 
+
5702
 
+    size = header_in->pos - header_in->start;
5703
 
+
5704
 
+    v->data = ngx_palloc(r->pool, size);
5705
 
+    last = ngx_cpymem(v->data, header_in->start, size);
5706
 
+
5707
 
+    /* fix \0 introduced by the nginx header parser and
5708
 
+     * locate the end of the header */
5709
 
+    just_seen_crlf = 0;
5710
 
+    for (p = (u_char*)v->data; p != last; p++) {
5711
 
+        if (*p == '\0') {
5712
 
+            if (p + 1 != last && *(p + 1) == LF) {
5713
 
+                just_seen_crlf = 1;
5714
 
+                *p = CR;
5715
 
+            } else {
5716
 
+                *p = ':';
5717
 
+                just_seen_crlf = 0;
5718
 
+            }
5719
 
+        } else if (*p == CR) {
5720
 
+            if (just_seen_crlf) {
5721
 
+                *p = '\0';
5722
 
+                last = p;
5723
 
+                break;
5724
 
+            }
5725
 
+        } else if (*p != LF) {
5726
 
+            just_seen_crlf = 0;
5727
 
+        }
5728
 
+    }
5729
 
+
5730
 
+    v->len = last - v->data;
5731
 
+    v->valid = 1;
5732
 
+    v->no_cacheable = 0;
5733
 
+    v->not_found = 0;
5734
 
+
5735
 
+    return NGX_OK;
5736
 
+}
5737
 
+
5738
 
+
5739
 
+ngx_int_t
5740
 
+ngx_http_echo_cacheable_request_uri_variable(ngx_http_request_t *r,
5741
 
+        ngx_http_variable_value_t *v, uintptr_t data)
5742
 
+{
5743
 
+    if (r->uri.len) {
5744
 
+        v->len = r->uri.len;
5745
 
+        v->valid = 1;
5746
 
+        v->no_cacheable = 0;
5747
 
+        v->not_found = 0;
5748
 
+        v->data = r->uri.data;
5749
 
+    } else {
5750
 
+        v->not_found = 1;
5751
 
+    }
5752
 
+
5753
 
+    return NGX_OK;
5754
 
+}
5755
 
+
5756
 
+
5757
 
+ngx_int_t
5758
 
+ngx_http_echo_request_uri_variable(ngx_http_request_t *r,
5759
 
+        ngx_http_variable_value_t *v, uintptr_t data)
5760
 
+{
5761
 
+    if (r->uri.len) {
5762
 
+        v->len = r->uri.len;
5763
 
+        v->valid = 1;
5764
 
+        v->no_cacheable = 1;
5765
 
+        v->not_found = 0;
5766
 
+        v->data = r->uri.data;
5767
 
+    } else {
5768
 
+        v->not_found = 1;
5769
 
+    }
5770
 
+
5771
 
+    return NGX_OK;
5772
 
+}
5773
 
+
5774
 
+
5775
 
+ngx_int_t
5776
 
+ngx_http_echo_response_status_variable(ngx_http_request_t *r,
5777
 
+        ngx_http_variable_value_t *v, uintptr_t data)
5778
 
+{
5779
 
+    u_char                      *p;
5780
 
+
5781
 
+    if (r->headers_out.status) {
5782
 
+        dd("headers out status: %d", (int) r->headers_out.status);
5783
 
+
5784
 
+        p = ngx_palloc(r->pool, NGX_INT_T_LEN);
5785
 
+        if (p == NULL) {
5786
 
+            return NGX_ERROR;
5787
 
+        }
5788
 
+
5789
 
+        v->len = ngx_sprintf(p, "%ui", r->headers_out.status) - p;
5790
 
+        v->data = p;
5791
 
+
5792
 
+        v->valid = 1;
5793
 
+        v->no_cacheable = 1;
5794
 
+        v->not_found = 0;
5795
 
+    } else {
5796
 
+        v->not_found = 1;
5797
 
+    }
5798
 
+
5799
 
+    return NGX_OK;
5800
 
+}
5801
 
+
5802
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_request_info.h
5803
 
===================================================================
5804
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
5805
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_request_info.h     2010-10-31 07:35:23.648338001 +0000
5806
 
@@ -0,0 +1,31 @@
5807
 
+#ifndef ECHO_REQUEST_INFO_H
5808
 
+#define ECHO_REQUEST_INFO_H
5809
 
+
5810
 
+#include "ngx_http_echo_module.h"
5811
 
+
5812
 
+ngx_int_t ngx_http_echo_exec_echo_read_request_body(
5813
 
+        ngx_http_request_t* r, ngx_http_echo_ctx_t *ctx);
5814
 
+
5815
 
+ngx_int_t ngx_http_echo_request_method_variable(ngx_http_request_t *r,
5816
 
+        ngx_http_variable_value_t *v, uintptr_t data);
5817
 
+
5818
 
+ngx_int_t ngx_http_echo_client_request_method_variable(ngx_http_request_t *r,
5819
 
+        ngx_http_variable_value_t *v, uintptr_t data);
5820
 
+
5821
 
+ngx_int_t ngx_http_echo_request_body_variable(ngx_http_request_t *r,
5822
 
+        ngx_http_variable_value_t *v, uintptr_t data);
5823
 
+
5824
 
+ngx_int_t ngx_http_echo_client_request_headers_variable(ngx_http_request_t *r,
5825
 
+        ngx_http_variable_value_t *v, uintptr_t data);
5826
 
+
5827
 
+ngx_int_t ngx_http_echo_cacheable_request_uri_variable(ngx_http_request_t *r,
5828
 
+        ngx_http_variable_value_t *v, uintptr_t data);
5829
 
+
5830
 
+ngx_int_t ngx_http_echo_request_uri_variable(ngx_http_request_t *r,
5831
 
+        ngx_http_variable_value_t *v, uintptr_t data);
5832
 
+
5833
 
+ngx_int_t ngx_http_echo_response_status_variable(ngx_http_request_t *r,
5834
 
+        ngx_http_variable_value_t *v, uintptr_t data);
5835
 
+
5836
 
+#endif /* ECHO_REQUEST_INFO_H */
5837
 
+
5838
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_sleep.c
5839
 
===================================================================
5840
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
5841
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_sleep.c    2010-10-31 07:35:23.648338001 +0000
5842
 
@@ -0,0 +1,200 @@
5843
 
+/* Copyright (C) agentzh */
5844
 
+
5845
 
+#define DDEBUG 0
5846
 
+#include "ddebug.h"
5847
 
+
5848
 
+#include "ngx_http_echo_sleep.h"
5849
 
+#include "ngx_http_echo_handler.h"
5850
 
+
5851
 
+#include <nginx.h>
5852
 
+#include <ngx_log.h>
5853
 
+
5854
 
+/* event handler for echo_sleep */
5855
 
+
5856
 
+static void ngx_http_echo_post_sleep(ngx_http_request_t *r);
5857
 
+
5858
 
+static void ngx_http_echo_sleep_cleanup(void *data);
5859
 
+
5860
 
+
5861
 
+ngx_int_t
5862
 
+ngx_http_echo_exec_echo_sleep(
5863
 
+        ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx,
5864
 
+        ngx_array_t *computed_args)
5865
 
+{
5866
 
+    ngx_str_t                   *computed_arg;
5867
 
+    ngx_str_t                   *computed_arg_elts;
5868
 
+    float                        delay; /* in sec */
5869
 
+    ngx_http_cleanup_t          *cln;
5870
 
+
5871
 
+    computed_arg_elts = computed_args->elts;
5872
 
+    computed_arg = &computed_arg_elts[0];
5873
 
+
5874
 
+    delay = atof( (char*) computed_arg->data );
5875
 
+
5876
 
+    if (delay < 0.001) { /* should be bigger than 1 msec */
5877
 
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
5878
 
+                   "invalid sleep duration \"%V\"", &computed_arg_elts[0]);
5879
 
+
5880
 
+        return NGX_HTTP_BAD_REQUEST;
5881
 
+    }
5882
 
+
5883
 
+    dd("DELAY = %.02lf sec", delay);
5884
 
+
5885
 
+    ngx_add_timer(&ctx->sleep, (ngx_msec_t) (1000 * delay));
5886
 
+
5887
 
+    /* we don't check broken downstream connections
5888
 
+     * ourselves so even if the client shuts down
5889
 
+     * the connection prematurely, nginx will still
5890
 
+     * go on waiting for our timers to get properly
5891
 
+     * expired. However, we'd still register a
5892
 
+     * cleanup handler for completeness. */
5893
 
+
5894
 
+    cln = ngx_http_cleanup_add(r, 0);
5895
 
+    if (cln == NULL) {
5896
 
+        return NGX_ERROR;
5897
 
+    }
5898
 
+
5899
 
+    cln->handler = ngx_http_echo_sleep_cleanup;
5900
 
+    cln->data = r;
5901
 
+
5902
 
+    return NGX_AGAIN;
5903
 
+}
5904
 
+
5905
 
+
5906
 
+static void
5907
 
+ngx_http_echo_post_sleep(ngx_http_request_t *r)
5908
 
+{
5909
 
+    ngx_http_echo_ctx_t         *ctx;
5910
 
+    /* ngx_int_t                    rc; */
5911
 
+
5912
 
+    dd("entered echo post sleep...(r->done: %d)", r->done);
5913
 
+
5914
 
+    dd("sleep: before get module ctx");
5915
 
+
5916
 
+    ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module);
5917
 
+
5918
 
+    if (ctx == NULL) {
5919
 
+        return;
5920
 
+    }
5921
 
+
5922
 
+    ctx->waiting = 0;
5923
 
+    ctx->done = 1;
5924
 
+
5925
 
+    dd("sleep: after get module ctx");
5926
 
+
5927
 
+    dd("timed out? %d", ctx->sleep.timedout);
5928
 
+    dd("timer set? %d", ctx->sleep.timer_set);
5929
 
+
5930
 
+    if ( ! ctx->sleep.timedout ) {
5931
 
+        dd("HERE reached!");
5932
 
+        return;
5933
 
+    }
5934
 
+
5935
 
+    ctx->sleep.timedout = 0;
5936
 
+
5937
 
+    if (ctx->sleep.timer_set) {
5938
 
+        dd("deleting timer for echo_sleep");
5939
 
+
5940
 
+        ngx_del_timer(&ctx->sleep);
5941
 
+    }
5942
 
+
5943
 
+    /* r->write_event_handler = ngx_http_request_empty_handler; */
5944
 
+
5945
 
+    ngx_http_echo_wev_handler(r);
5946
 
+}
5947
 
+
5948
 
+
5949
 
+void
5950
 
+ngx_http_echo_sleep_event_handler(ngx_event_t *ev)
5951
 
+{
5952
 
+    ngx_connection_t        *c;
5953
 
+    ngx_http_request_t      *r;
5954
 
+    ngx_http_log_ctx_t      *ctx;
5955
 
+
5956
 
+    r = ev->data;
5957
 
+    c = r->connection;
5958
 
+
5959
 
+    if (c->destroyed) {
5960
 
+        return;
5961
 
+    }
5962
 
+
5963
 
+    ctx = c->log->data;
5964
 
+    ctx->current_request = r;
5965
 
+
5966
 
+    /* XXX when r->done == 1 we should do cleaning immediately
5967
 
+     * and delete our timer and then quit. */
5968
 
+
5969
 
+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
5970
 
+            "echo sleep handler: \"%V?%V\"", &r->uri, &r->args);
5971
 
+
5972
 
+    /*
5973
 
+    if (r->done) {
5974
 
+        return;
5975
 
+    }
5976
 
+    */
5977
 
+
5978
 
+    ngx_http_echo_post_sleep(r);
5979
 
+
5980
 
+#if defined(nginx_version)
5981
 
+
5982
 
+    dd("before run posted requests");
5983
 
+
5984
 
+    ngx_http_run_posted_requests(c);
5985
 
+
5986
 
+    dd("after run posted requests");
5987
 
+
5988
 
+#endif
5989
 
+
5990
 
+}
5991
 
+
5992
 
+
5993
 
+ngx_int_t
5994
 
+ngx_http_echo_exec_echo_blocking_sleep(ngx_http_request_t *r,
5995
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
5996
 
+{
5997
 
+    ngx_str_t                   *computed_arg;
5998
 
+    ngx_str_t                   *computed_arg_elts;
5999
 
+    float                       delay; /* in sec */
6000
 
+
6001
 
+    computed_arg_elts = computed_args->elts;
6002
 
+    computed_arg = &computed_arg_elts[0];
6003
 
+
6004
 
+    delay = atof( (char*) computed_arg->data );
6005
 
+
6006
 
+    if (delay < 0.001) { /* should be bigger than 1 msec */
6007
 
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
6008
 
+                   "invalid sleep duration \"%V\"", &computed_arg_elts[0]);
6009
 
+        return NGX_HTTP_BAD_REQUEST;
6010
 
+    }
6011
 
+
6012
 
+    dd("blocking DELAY = %.02lf sec", delay);
6013
 
+
6014
 
+    ngx_msleep((ngx_msec_t) (1000 * delay));
6015
 
+
6016
 
+    return NGX_OK;
6017
 
+}
6018
 
+
6019
 
+
6020
 
+static void
6021
 
+ngx_http_echo_sleep_cleanup(void *data)
6022
 
+{
6023
 
+    ngx_http_request_t      *r = data;
6024
 
+    ngx_http_echo_ctx_t         *ctx;
6025
 
+
6026
 
+    dd("echo sleep cleanup");
6027
 
+
6028
 
+    ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module);
6029
 
+    if (ctx == NULL) {
6030
 
+        return;
6031
 
+    }
6032
 
+
6033
 
+    if (ctx->sleep.timer_set) {
6034
 
+        dd("cleanup: deleting timer for echo_sleep");
6035
 
+
6036
 
+        ngx_del_timer(&ctx->sleep);
6037
 
+        return;
6038
 
+    }
6039
 
+
6040
 
+    dd("cleanup: timer not set");
6041
 
+}
6042
 
+
6043
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_sleep.h
6044
 
===================================================================
6045
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
6046
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_sleep.h    2010-10-31 07:35:23.648338001 +0000
6047
 
@@ -0,0 +1,16 @@
6048
 
+#ifndef ECHO_SLEEP_H
6049
 
+#define ECHO_SLEEP_H
6050
 
+
6051
 
+#include "ngx_http_echo_module.h"
6052
 
+
6053
 
+ngx_int_t ngx_http_echo_exec_echo_sleep(
6054
 
+        ngx_http_request_t *r, ngx_http_echo_ctx_t *ctx,
6055
 
+        ngx_array_t *computed_args);
6056
 
+
6057
 
+ngx_int_t ngx_http_echo_exec_echo_blocking_sleep(ngx_http_request_t *r,
6058
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args);
6059
 
+
6060
 
+void ngx_http_echo_sleep_event_handler(ngx_event_t *ev);
6061
 
+
6062
 
+#endif /* ECHO_SLEEP_H */
6063
 
+
6064
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_subrequest.c
6065
 
===================================================================
6066
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
6067
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_subrequest.c       2010-10-31 07:35:23.648338001 +0000
6068
 
@@ -0,0 +1,606 @@
6069
 
+#define DDEBUG 0
6070
 
+#include "ddebug.h"
6071
 
+
6072
 
+#include "ngx_http_echo_util.h"
6073
 
+#include "ngx_http_echo_subrequest.h"
6074
 
+#include "ngx_http_echo_handler.h"
6075
 
+
6076
 
+#define ngx_http_echo_method_name(m) { sizeof(m) - 1, (u_char *) m " " }
6077
 
+
6078
 
+ngx_str_t  ngx_http_echo_content_length_header_key = ngx_string("Content-Length");
6079
 
+
6080
 
+ngx_str_t  ngx_http_echo_get_method = ngx_http_echo_method_name("GET");
6081
 
+ngx_str_t  ngx_http_echo_put_method = ngx_http_echo_method_name("PUT");
6082
 
+ngx_str_t  ngx_http_echo_post_method = ngx_http_echo_method_name("POST");
6083
 
+ngx_str_t  ngx_http_echo_head_method = ngx_http_echo_method_name("HEAD");
6084
 
+ngx_str_t  ngx_http_echo_copy_method = ngx_http_echo_method_name("COPY");
6085
 
+ngx_str_t  ngx_http_echo_move_method = ngx_http_echo_method_name("MOVE");
6086
 
+ngx_str_t  ngx_http_echo_lock_method = ngx_http_echo_method_name("LOCK");
6087
 
+ngx_str_t  ngx_http_echo_mkcol_method = ngx_http_echo_method_name("MKCOL");
6088
 
+ngx_str_t  ngx_http_echo_trace_method = ngx_http_echo_method_name("TRACE");
6089
 
+ngx_str_t  ngx_http_echo_delete_method = ngx_http_echo_method_name("DELETE");
6090
 
+ngx_str_t  ngx_http_echo_unlock_method = ngx_http_echo_method_name("UNLOCK");
6091
 
+ngx_str_t  ngx_http_echo_options_method = ngx_http_echo_method_name("OPTIONS");
6092
 
+ngx_str_t  ngx_http_echo_propfind_method = ngx_http_echo_method_name("PROPFIND");
6093
 
+ngx_str_t  ngx_http_echo_proppatch_method = ngx_http_echo_method_name("PROPPATCH");
6094
 
+
6095
 
+
6096
 
+typedef struct ngx_http_echo_subrequest_s {
6097
 
+    ngx_uint_t                  method;
6098
 
+    ngx_str_t                   *method_name;
6099
 
+    ngx_str_t                   *location;
6100
 
+    ngx_str_t                   *query_string;
6101
 
+    ssize_t                     content_length_n;
6102
 
+    ngx_http_request_body_t     *request_body;
6103
 
+} ngx_http_echo_subrequest_t;
6104
 
+
6105
 
+
6106
 
+static ngx_int_t ngx_http_echo_parse_method_name(ngx_str_t **method_name_ptr);
6107
 
+
6108
 
+static ngx_int_t ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr,
6109
 
+        ngx_http_echo_subrequest_t *parsed_sr);
6110
 
+
6111
 
+static ngx_int_t ngx_http_echo_parse_subrequest_spec(ngx_http_request_t *r,
6112
 
+        ngx_array_t *computed_args, ngx_http_echo_subrequest_t **parsed_sr_ptr);
6113
 
+
6114
 
+
6115
 
+ngx_int_t
6116
 
+ngx_http_echo_exec_echo_subrequest_async(ngx_http_request_t *r,
6117
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
6118
 
+{
6119
 
+    ngx_int_t                       rc;
6120
 
+    ngx_http_echo_subrequest_t      *parsed_sr;
6121
 
+    ngx_http_request_t              *sr; /* subrequest object */
6122
 
+    ngx_str_t                       args;
6123
 
+    ngx_uint_t                      flags = 0;
6124
 
+
6125
 
+
6126
 
+    dd_enter();
6127
 
+
6128
 
+    rc = ngx_http_echo_parse_subrequest_spec(r, computed_args, &parsed_sr);
6129
 
+    if (rc != NGX_OK) {
6130
 
+        return rc;
6131
 
+    }
6132
 
+
6133
 
+    dd("location: %s", parsed_sr->location->data);
6134
 
+    dd("location args: %s", (char*) (parsed_sr->query_string ?
6135
 
+                parsed_sr->query_string->data : (u_char*)"NULL"));
6136
 
+
6137
 
+    args.data = NULL;
6138
 
+    args.len = 0;
6139
 
+
6140
 
+    if (ngx_http_parse_unsafe_uri(r, parsed_sr->location, &args, &flags)
6141
 
+            != NGX_OK)
6142
 
+    {
6143
 
+        ctx->headers_sent = 1;
6144
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
6145
 
+    }
6146
 
+
6147
 
+    if (args.len > 0 && parsed_sr->query_string == NULL) {
6148
 
+        parsed_sr->query_string = &args;
6149
 
+    }
6150
 
+
6151
 
+    rc = ngx_http_echo_send_header_if_needed(r, ctx);
6152
 
+    if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
6153
 
+        return rc;
6154
 
+    }
6155
 
+
6156
 
+    rc = ngx_http_subrequest(r, parsed_sr->location, parsed_sr->query_string,
6157
 
+            &sr, NULL, 0);
6158
 
+
6159
 
+    if (rc != NGX_OK) {
6160
 
+        return NGX_ERROR;
6161
 
+    }
6162
 
+
6163
 
+    rc = ngx_http_echo_adjust_subrequest(sr, parsed_sr);
6164
 
+
6165
 
+    if (rc != NGX_OK) {
6166
 
+        return rc;
6167
 
+    }
6168
 
+
6169
 
+    return NGX_OK;
6170
 
+}
6171
 
+
6172
 
+
6173
 
+ngx_int_t
6174
 
+ngx_http_echo_exec_echo_subrequest(ngx_http_request_t *r,
6175
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
6176
 
+{
6177
 
+    ngx_int_t                           rc;
6178
 
+    ngx_http_request_t                  *sr; /* subrequest object */
6179
 
+    ngx_http_post_subrequest_t          *psr;
6180
 
+    ngx_http_echo_subrequest_t          *parsed_sr;
6181
 
+    ngx_str_t                           args;
6182
 
+    ngx_uint_t                          flags = 0;
6183
 
+
6184
 
+
6185
 
+    dd_enter();
6186
 
+
6187
 
+    rc = ngx_http_echo_parse_subrequest_spec(r, computed_args, &parsed_sr);
6188
 
+    if (rc != NGX_OK) {
6189
 
+        return rc;
6190
 
+    }
6191
 
+
6192
 
+    args.data = NULL;
6193
 
+    args.len = 0;
6194
 
+
6195
 
+    if (ngx_http_parse_unsafe_uri(r, parsed_sr->location, &args, &flags)
6196
 
+            != NGX_OK) {
6197
 
+        ctx->headers_sent = 1;
6198
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
6199
 
+    }
6200
 
+
6201
 
+    if (args.len > 0 && parsed_sr->query_string == NULL) {
6202
 
+        parsed_sr->query_string = &args;
6203
 
+    }
6204
 
+
6205
 
+    rc = ngx_http_echo_send_header_if_needed(r, ctx);
6206
 
+
6207
 
+    if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
6208
 
+        return rc;
6209
 
+    }
6210
 
+
6211
 
+    psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
6212
 
+
6213
 
+    if (psr == NULL) {
6214
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
6215
 
+    }
6216
 
+
6217
 
+    psr->handler = ngx_http_echo_post_subrequest;
6218
 
+    psr->data = ctx;
6219
 
+
6220
 
+    rc = ngx_http_subrequest(r, parsed_sr->location, parsed_sr->query_string,
6221
 
+            &sr, psr, 0);
6222
 
+
6223
 
+    if (rc != NGX_OK) {
6224
 
+        return NGX_ERROR;
6225
 
+    }
6226
 
+
6227
 
+    rc = ngx_http_echo_adjust_subrequest(sr, parsed_sr);
6228
 
+
6229
 
+    if (rc != NGX_OK) {
6230
 
+        return NGX_ERROR;
6231
 
+    }
6232
 
+
6233
 
+    return NGX_AGAIN;
6234
 
+}
6235
 
+
6236
 
+
6237
 
+static ngx_int_t
6238
 
+ngx_http_echo_parse_subrequest_spec(ngx_http_request_t *r,
6239
 
+        ngx_array_t *computed_args, ngx_http_echo_subrequest_t **parsed_sr_ptr)
6240
 
+{
6241
 
+    ngx_str_t                   *computed_arg_elts, *arg;
6242
 
+    ngx_str_t                  **to_write = NULL;
6243
 
+    ngx_str_t                   *method_name;
6244
 
+    ngx_str_t                   *body_str = NULL;
6245
 
+    ngx_uint_t                   i;
6246
 
+    ngx_flag_t                   expecting_opt;
6247
 
+    ngx_http_request_body_t     *rb = NULL;
6248
 
+    ngx_buf_t                   *b;
6249
 
+    ngx_http_echo_subrequest_t  *parsed_sr;
6250
 
+
6251
 
+    *parsed_sr_ptr = ngx_pcalloc(r->pool, sizeof(ngx_http_echo_subrequest_t));
6252
 
+    if (*parsed_sr_ptr == NULL) {
6253
 
+        return NGX_ERROR;
6254
 
+    }
6255
 
+
6256
 
+    parsed_sr = *parsed_sr_ptr;
6257
 
+
6258
 
+    computed_arg_elts = computed_args->elts;
6259
 
+
6260
 
+    method_name = &computed_arg_elts[0];
6261
 
+
6262
 
+    parsed_sr->location     = &computed_arg_elts[1];
6263
 
+
6264
 
+    if (parsed_sr->location->len == 0) {
6265
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
6266
 
+    }
6267
 
+
6268
 
+    expecting_opt = 1;
6269
 
+    for (i = 2; i < computed_args->nelts; i++) {
6270
 
+        arg = &computed_arg_elts[i];
6271
 
+        if (!expecting_opt) {
6272
 
+            if (to_write == NULL) {
6273
 
+                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
6274
 
+                        "echo_subrequest_async: to_write should NOT be NULL");
6275
 
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
6276
 
+            }
6277
 
+
6278
 
+            *to_write = arg;
6279
 
+            to_write = NULL;
6280
 
+
6281
 
+            expecting_opt = 1;
6282
 
+
6283
 
+            continue;
6284
 
+        }
6285
 
+
6286
 
+        if (arg->len == 2) {
6287
 
+            if (ngx_strncmp("-q", arg->data, arg->len) == 0) {
6288
 
+                to_write = &parsed_sr->query_string;
6289
 
+                expecting_opt = 0;
6290
 
+                continue;
6291
 
+            }
6292
 
+
6293
 
+            if (ngx_strncmp("-b", arg->data, arg->len) == 0) {
6294
 
+                to_write = &body_str;
6295
 
+                expecting_opt = 0;
6296
 
+                continue;
6297
 
+            }
6298
 
+        }
6299
 
+
6300
 
+        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
6301
 
+                "Unknown option for echo_subrequest_async: %V", arg);
6302
 
+
6303
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
6304
 
+    }
6305
 
+
6306
 
+    if (body_str != NULL && body_str->len != 0) {
6307
 
+        rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
6308
 
+
6309
 
+        if (rb == NULL) {
6310
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
6311
 
+        }
6312
 
+
6313
 
+        parsed_sr->content_length_n = body_str->len;
6314
 
+
6315
 
+        b = ngx_calloc_buf(r->pool);
6316
 
+        if (b == NULL) {
6317
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
6318
 
+        }
6319
 
+
6320
 
+        b->temporary = 1;
6321
 
+        /* b->memory = 1; */
6322
 
+        b->start = b->pos = body_str->data;
6323
 
+        b->end = b->last = body_str->data + body_str->len;
6324
 
+
6325
 
+        rb->bufs = ngx_alloc_chain_link(r->pool);
6326
 
+        if (rb->bufs == NULL) {
6327
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
6328
 
+        }
6329
 
+
6330
 
+        rb->bufs->buf = b;
6331
 
+        rb->bufs->next = NULL;
6332
 
+
6333
 
+        rb->buf = b;
6334
 
+    }
6335
 
+
6336
 
+    parsed_sr->request_body = rb;
6337
 
+
6338
 
+    parsed_sr->method = ngx_http_echo_parse_method_name(&method_name);
6339
 
+    parsed_sr->method_name  = method_name;
6340
 
+
6341
 
+    return NGX_OK;
6342
 
+}
6343
 
+
6344
 
+
6345
 
+static ngx_int_t
6346
 
+ngx_http_echo_adjust_subrequest(ngx_http_request_t *sr,
6347
 
+        ngx_http_echo_subrequest_t *parsed_sr)
6348
 
+{
6349
 
+    ngx_table_elt_t            *h;
6350
 
+    ngx_http_core_main_conf_t  *cmcf;
6351
 
+    ngx_http_request_t         *r;
6352
 
+
6353
 
+    sr->method = parsed_sr->method;
6354
 
+    sr->method_name = *(parsed_sr->method_name);
6355
 
+
6356
 
+    r = sr->parent;
6357
 
+
6358
 
+    sr->header_in = r->header_in;
6359
 
+
6360
 
+    /* XXX work-around a bug in ngx_http_subrequest */
6361
 
+    if (r->headers_in.headers.last == &r->headers_in.headers.part) {
6362
 
+        sr->headers_in.headers.last = &sr->headers_in.headers.part;
6363
 
+    }
6364
 
+
6365
 
+    /* we do not inherit the parent request's variables */
6366
 
+    cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module);
6367
 
+    sr->variables = ngx_pcalloc(sr->pool, cmcf->variables.nelts
6368
 
+                                        * sizeof(ngx_http_variable_value_t));
6369
 
+
6370
 
+    if (sr->variables == NULL) {
6371
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
6372
 
+    }
6373
 
+
6374
 
+    if (parsed_sr->content_length_n > 0) {
6375
 
+        sr->headers_in.content_length_n = parsed_sr->content_length_n;
6376
 
+        sr->request_body = parsed_sr->request_body;
6377
 
+
6378
 
+        sr->headers_in.content_length = ngx_pcalloc(sr->pool,
6379
 
+                sizeof(ngx_table_elt_t));
6380
 
+        sr->headers_in.content_length->value.data =
6381
 
+            ngx_palloc(sr->pool, NGX_OFF_T_LEN);
6382
 
+        if (sr->headers_in.content_length->value.data == NULL) {
6383
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
6384
 
+        }
6385
 
+        sr->headers_in.content_length->value.len = ngx_sprintf(
6386
 
+                sr->headers_in.content_length->value.data, "%O",
6387
 
+                sr->headers_in.content_length_n) -
6388
 
+                sr->headers_in.content_length->value.data;
6389
 
+
6390
 
+        if (ngx_list_init(&sr->headers_in.headers, sr->pool, 20,
6391
 
+                    sizeof(ngx_table_elt_t)) != NGX_OK) {
6392
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
6393
 
+        }
6394
 
+
6395
 
+        h = ngx_list_push(&sr->headers_in.headers);
6396
 
+        if (h == NULL) {
6397
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
6398
 
+        }
6399
 
+
6400
 
+        h->hash = sr->header_hash;
6401
 
+
6402
 
+        h->key = ngx_http_echo_content_length_header_key;
6403
 
+        h->value = sr->headers_in.content_length->value;
6404
 
+
6405
 
+        h->lowcase_key = ngx_pnalloc(sr->pool, h->key.len);
6406
 
+        if (h->lowcase_key == NULL) {
6407
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
6408
 
+        }
6409
 
+
6410
 
+        ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
6411
 
+
6412
 
+        dd("sr content length: %s", sr->headers_in.content_length->value.data);
6413
 
+    }
6414
 
+
6415
 
+    dd("subrequest body: %p", sr->request_body);
6416
 
+
6417
 
+    return NGX_OK;
6418
 
+}
6419
 
+
6420
 
+
6421
 
+static ngx_int_t
6422
 
+ngx_http_echo_parse_method_name(ngx_str_t **method_name_ptr)
6423
 
+{
6424
 
+    const ngx_str_t* method_name = *method_name_ptr;
6425
 
+
6426
 
+    switch (method_name->len) {
6427
 
+    case 3:
6428
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "GET") == 0) {
6429
 
+            *method_name_ptr = &ngx_http_echo_get_method;
6430
 
+            return NGX_HTTP_GET;
6431
 
+            break;
6432
 
+        }
6433
 
+
6434
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "PUT") == 0) {
6435
 
+            *method_name_ptr = &ngx_http_echo_put_method;
6436
 
+            return NGX_HTTP_PUT;
6437
 
+            break;
6438
 
+        }
6439
 
+
6440
 
+        return NGX_HTTP_UNKNOWN;
6441
 
+        break;
6442
 
+
6443
 
+    case 4:
6444
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "POST") == 0) {
6445
 
+            *method_name_ptr = &ngx_http_echo_post_method;
6446
 
+            return NGX_HTTP_POST;
6447
 
+            break;
6448
 
+        }
6449
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "HEAD") == 0) {
6450
 
+            *method_name_ptr = &ngx_http_echo_head_method;
6451
 
+            return NGX_HTTP_HEAD;
6452
 
+            break;
6453
 
+        }
6454
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "COPY") == 0) {
6455
 
+            *method_name_ptr = &ngx_http_echo_copy_method;
6456
 
+            return NGX_HTTP_COPY;
6457
 
+            break;
6458
 
+        }
6459
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "MOVE") == 0) {
6460
 
+            *method_name_ptr = &ngx_http_echo_move_method;
6461
 
+            return NGX_HTTP_MOVE;
6462
 
+            break;
6463
 
+        }
6464
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "LOCK") == 0) {
6465
 
+            *method_name_ptr = &ngx_http_echo_lock_method;
6466
 
+            return NGX_HTTP_LOCK;
6467
 
+            break;
6468
 
+        }
6469
 
+        return NGX_HTTP_UNKNOWN;
6470
 
+        break;
6471
 
+
6472
 
+    case 5:
6473
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "MKCOL") == 0) {
6474
 
+            *method_name_ptr = &ngx_http_echo_mkcol_method;
6475
 
+            return NGX_HTTP_MKCOL;
6476
 
+            break;
6477
 
+        }
6478
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "TRACE") == 0) {
6479
 
+            *method_name_ptr = &ngx_http_echo_trace_method;
6480
 
+            return NGX_HTTP_TRACE;
6481
 
+            break;
6482
 
+        }
6483
 
+        return NGX_HTTP_UNKNOWN;
6484
 
+        break;
6485
 
+
6486
 
+    case 6:
6487
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "DELETE") == 0) {
6488
 
+            *method_name_ptr = &ngx_http_echo_delete_method;
6489
 
+            return NGX_HTTP_DELETE;
6490
 
+            break;
6491
 
+        }
6492
 
+
6493
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "UNLOCK") == 0) {
6494
 
+            *method_name_ptr = &ngx_http_echo_unlock_method;
6495
 
+            return NGX_HTTP_UNLOCK;
6496
 
+            break;
6497
 
+        }
6498
 
+        return NGX_HTTP_UNKNOWN;
6499
 
+        break;
6500
 
+
6501
 
+    case 7:
6502
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "OPTIONS") == 0) {
6503
 
+            *method_name_ptr = &ngx_http_echo_options_method;
6504
 
+            return NGX_HTTP_OPTIONS;
6505
 
+            break;
6506
 
+        }
6507
 
+        return NGX_HTTP_UNKNOWN;
6508
 
+        break;
6509
 
+
6510
 
+    case 8:
6511
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "PROPFIND") == 0) {
6512
 
+            *method_name_ptr = &ngx_http_echo_propfind_method;
6513
 
+            return NGX_HTTP_PROPFIND;
6514
 
+            break;
6515
 
+        }
6516
 
+        return NGX_HTTP_UNKNOWN;
6517
 
+        break;
6518
 
+
6519
 
+    case 9:
6520
 
+        if (ngx_http_echo_strcmp_const(method_name->data, "PROPPATCH") == 0) {
6521
 
+            *method_name_ptr = &ngx_http_echo_proppatch_method;
6522
 
+            return NGX_HTTP_PROPPATCH;
6523
 
+            break;
6524
 
+        }
6525
 
+        return NGX_HTTP_UNKNOWN;
6526
 
+        break;
6527
 
+
6528
 
+    default:
6529
 
+        return NGX_HTTP_UNKNOWN;
6530
 
+        break;
6531
 
+    }
6532
 
+
6533
 
+    return NGX_HTTP_UNKNOWN;
6534
 
+}
6535
 
+
6536
 
+
6537
 
+/* XXX extermely evil and not working yet */
6538
 
+ngx_int_t
6539
 
+ngx_http_echo_exec_abort_parent(ngx_http_request_t *r,
6540
 
+        ngx_http_echo_ctx_t *ctx)
6541
 
+{
6542
 
+#if 0
6543
 
+    ngx_http_postponed_request_t    *pr, *ppr;
6544
 
+    ngx_http_request_t              *saved_data = NULL;
6545
 
+    ngx_chain_t                     *out = NULL;
6546
 
+    /* ngx_int_t                       rc; */
6547
 
+
6548
 
+    dd("aborting parent...");
6549
 
+
6550
 
+    if (r == r->main || r->parent == NULL) {
6551
 
+        return NGX_OK;
6552
 
+    }
6553
 
+
6554
 
+    if (r->parent->postponed) {
6555
 
+        dd("Found parent->postponed...");
6556
 
+
6557
 
+        saved_data = r->connection->data;
6558
 
+        ppr = NULL;
6559
 
+        for (pr = r->parent->postponed; pr->next; pr = pr->next) {
6560
 
+            if (pr->request == NULL) {
6561
 
+                continue;
6562
 
+            }
6563
 
+
6564
 
+            if (pr->request == r) {
6565
 
+                /* r->parent->postponed->next = pr; */
6566
 
+                dd("found the current subrequest");
6567
 
+                out = pr->out;
6568
 
+                continue;
6569
 
+            }
6570
 
+
6571
 
+            /* r->connection->data = pr->request; */
6572
 
+            dd("finalizing the subrequest...");
6573
 
+            ngx_http_upstream_create(pr->request);
6574
 
+            pr->request->upstream = NULL;
6575
 
+
6576
 
+            if (ppr == NULL) {
6577
 
+                r->parent->postponed = pr->next;
6578
 
+                ppr = pr->next;
6579
 
+            } else {
6580
 
+                ppr->next = pr->next;
6581
 
+                ppr = pr->next;
6582
 
+            }
6583
 
+        }
6584
 
+    }
6585
 
+
6586
 
+    r->parent->postponed->next = NULL;
6587
 
+
6588
 
+    /*
6589
 
+    r->connection->data = r->parent;
6590
 
+    r->connection->buffered = 0;
6591
 
+
6592
 
+    if (out != NULL) {
6593
 
+        dd("trying to send more stuffs for the parent");
6594
 
+        ngx_http_output_filter(r->parent, out);
6595
 
+    }
6596
 
+    */
6597
 
+
6598
 
+    /* ngx_http_send_special(r->parent, NGX_HTTP_LAST); */
6599
 
+
6600
 
+    if (saved_data) {
6601
 
+        r->connection->data = saved_data;
6602
 
+    }
6603
 
+
6604
 
+    dd("terminating the parent request");
6605
 
+
6606
 
+    return ngx_http_echo_send_chain_link(r, ctx, NULL /* indicate LAST */);
6607
 
+
6608
 
+    /* ngx_http_upstream_create(r); */
6609
 
+
6610
 
+    /* ngx_http_finalize_request(r->parent, NGX_ERROR); */
6611
 
+#endif
6612
 
+
6613
 
+    return NGX_OK;
6614
 
+}
6615
 
+
6616
 
+
6617
 
+ngx_int_t
6618
 
+ngx_http_echo_exec_exec(ngx_http_request_t *r,
6619
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args)
6620
 
+{
6621
 
+    ngx_str_t                       *uri;
6622
 
+    ngx_str_t                       *user_args;
6623
 
+    ngx_str_t                        args;
6624
 
+    ngx_uint_t                       flags;
6625
 
+    ngx_str_t                       *computed_arg;
6626
 
+
6627
 
+    computed_arg = computed_args->elts;
6628
 
+
6629
 
+    uri = &computed_arg[0];
6630
 
+
6631
 
+    if (uri->len == 0) {
6632
 
+        return NGX_HTTP_BAD_REQUEST;
6633
 
+    }
6634
 
+
6635
 
+    if (computed_args->nelts > 1) {
6636
 
+        user_args = &computed_arg[1];
6637
 
+    } else {
6638
 
+        user_args = NULL;
6639
 
+    }
6640
 
+
6641
 
+    args.data = NULL;
6642
 
+    args.len = 0;
6643
 
+
6644
 
+    if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags)
6645
 
+            != NGX_OK) {
6646
 
+        ctx->headers_sent = 1;
6647
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
6648
 
+    }
6649
 
+
6650
 
+    if (args.len > 0 && user_args == NULL) {
6651
 
+        user_args = &args;
6652
 
+    }
6653
 
+
6654
 
+    /*
6655
 
+    rc = ngx_http_echo_send_header_if_needed(r, ctx);
6656
 
+    if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
6657
 
+        return rc;
6658
 
+    }
6659
 
+    */
6660
 
+
6661
 
+    if (uri->data[0] == '@') {
6662
 
+        if (user_args && user_args->len > 0) {
6663
 
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
6664
 
+                    "query strings %V ignored when exec'ing named location %V",
6665
 
+                    user_args, uri);
6666
 
+
6667
 
+        }
6668
 
+
6669
 
+        return ngx_http_named_location(r, uri);
6670
 
+    }
6671
 
+
6672
 
+    return ngx_http_internal_redirect(r, uri, user_args);
6673
 
+}
6674
 
+
6675
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_subrequest.h
6676
 
===================================================================
6677
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
6678
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_subrequest.h       2010-10-31 07:35:23.648338001 +0000
6679
 
@@ -0,0 +1,19 @@
6680
 
+#ifndef ECHO_SUBREQUEST_H
6681
 
+#define ECHO_SUBREQUEST_H
6682
 
+
6683
 
+#include "ngx_http_echo_module.h"
6684
 
+
6685
 
+ngx_int_t ngx_http_echo_exec_echo_subrequest(ngx_http_request_t *r,
6686
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args);
6687
 
+
6688
 
+ngx_int_t ngx_http_echo_exec_echo_subrequest_async(ngx_http_request_t *r,
6689
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args);
6690
 
+
6691
 
+ngx_int_t ngx_http_echo_exec_abort_parent(ngx_http_request_t *r,
6692
 
+        ngx_http_echo_ctx_t *ctx);
6693
 
+
6694
 
+ngx_int_t ngx_http_echo_exec_exec(ngx_http_request_t *r,
6695
 
+        ngx_http_echo_ctx_t *ctx, ngx_array_t *computed_args);
6696
 
+
6697
 
+#endif /* ECHO_SUBREQUEST_H */
6698
 
+
6699
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_timer.c
6700
 
===================================================================
6701
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
6702
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_timer.c    2010-10-31 07:35:23.648338001 +0000
6703
 
@@ -0,0 +1,76 @@
6704
 
+#define DDEBUG 0
6705
 
+
6706
 
+#include "ddebug.h"
6707
 
+
6708
 
+#include "ngx_http_echo_timer.h"
6709
 
+
6710
 
+#include <stdlib.h>
6711
 
+#include <ngx_log.h>
6712
 
+
6713
 
+ngx_int_t
6714
 
+ngx_http_echo_timer_elapsed_variable(ngx_http_request_t *r,
6715
 
+        ngx_http_variable_value_t *v, uintptr_t data)
6716
 
+{
6717
 
+    ngx_http_echo_ctx_t     *ctx;
6718
 
+    ngx_msec_int_t          ms;
6719
 
+    u_char                  *p;
6720
 
+    ngx_time_t              *tp;
6721
 
+    size_t                  size;
6722
 
+
6723
 
+    ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module);
6724
 
+    if (ctx->timer_begin.sec == 0) {
6725
 
+        ctx->timer_begin.sec  = r->start_sec;
6726
 
+        ctx->timer_begin.msec = (ngx_msec_t) r->start_msec;
6727
 
+    }
6728
 
+
6729
 
+    /* force the ngx timer to update */
6730
 
+
6731
 
+#if defined nginx_version && (nginx_version >= 8035 \
6732
 
+        || (nginx_version < 8000 && nginx_version >= 7066))
6733
 
+    ngx_time_update();
6734
 
+#else
6735
 
+    ngx_time_update(0, 0);
6736
 
+#endif
6737
 
+
6738
 
+    tp = ngx_timeofday();
6739
 
+
6740
 
+    dd("old sec msec: %ld %d\n", ctx->timer_begin.sec, ctx->timer_begin.msec);
6741
 
+    dd("new sec msec: %ld %d\n", tp->sec, tp->msec);
6742
 
+
6743
 
+    ms = (ngx_msec_int_t)
6744
 
+             ((tp->sec - ctx->timer_begin.sec) * 1000 +
6745
 
+              (tp->msec - ctx->timer_begin.msec));
6746
 
+    ms = (ms >= 0) ? ms : 0;
6747
 
+
6748
 
+    size = sizeof("-9223372036854775808.000") - 1;
6749
 
+    p = ngx_palloc(r->pool, size);
6750
 
+    v->len = ngx_snprintf(p, size, "%T.%03M",
6751
 
+             ms / 1000, ms % 1000) - p;
6752
 
+    v->data = p;
6753
 
+
6754
 
+    v->valid = 1;
6755
 
+    v->no_cacheable = 1;
6756
 
+    v->not_found = 0;
6757
 
+    return NGX_OK;
6758
 
+}
6759
 
+
6760
 
+
6761
 
+ngx_int_t
6762
 
+ngx_http_echo_exec_echo_reset_timer(ngx_http_request_t *r,
6763
 
+        ngx_http_echo_ctx_t *ctx)
6764
 
+{
6765
 
+    dd("Exec timer...");
6766
 
+
6767
 
+    /* force the ngx timer to update */
6768
 
+
6769
 
+#if defined nginx_version && (nginx_version >= 8035 \
6770
 
+        || (nginx_version < 8000 && nginx_version >= 7066))
6771
 
+    ngx_time_update();
6772
 
+#else
6773
 
+    ngx_time_update(0, 0);
6774
 
+#endif
6775
 
+
6776
 
+    ctx->timer_begin = *ngx_timeofday();
6777
 
+    return NGX_OK;
6778
 
+}
6779
 
+
6780
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_timer.h
6781
 
===================================================================
6782
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
6783
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_timer.h    2010-10-31 07:35:23.648338001 +0000
6784
 
@@ -0,0 +1,13 @@
6785
 
+#ifndef ECHO_TIMER_H
6786
 
+#define ECHO_TIMER_H
6787
 
+
6788
 
+#include "ngx_http_echo_module.h"
6789
 
+
6790
 
+ngx_int_t ngx_http_echo_timer_elapsed_variable(ngx_http_request_t *r,
6791
 
+        ngx_http_variable_value_t *v, uintptr_t data);
6792
 
+
6793
 
+ngx_int_t ngx_http_echo_exec_echo_reset_timer(ngx_http_request_t *r,
6794
 
+        ngx_http_echo_ctx_t *ctx);
6795
 
+
6796
 
+#endif /* ECHO_TIMER_H */
6797
 
+
6798
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_util.c
6799
 
===================================================================
6800
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
6801
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_util.c     2010-10-31 07:35:23.648338001 +0000
6802
 
@@ -0,0 +1,248 @@
6803
 
+#define DDEBUG 0
6804
 
+#include "ddebug.h"
6805
 
+
6806
 
+#include "ngx_http_echo_util.h"
6807
 
+#include "ngx_http_echo_sleep.h"
6808
 
+
6809
 
+
6810
 
+ngx_int_t
6811
 
+ngx_http_echo_init_ctx(ngx_http_request_t *r, ngx_http_echo_ctx_t **ctx_ptr)
6812
 
+{
6813
 
+    ngx_http_echo_ctx_t         *ctx;
6814
 
+
6815
 
+    *ctx_ptr = ngx_pcalloc(r->pool, sizeof(ngx_http_echo_ctx_t));
6816
 
+    if (*ctx_ptr == NULL) {
6817
 
+        return NGX_HTTP_INTERNAL_SERVER_ERROR;
6818
 
+    }
6819
 
+
6820
 
+    ctx = *ctx_ptr;
6821
 
+
6822
 
+    ctx->sleep.handler   = ngx_http_echo_sleep_event_handler;
6823
 
+    ctx->sleep.data      = r;
6824
 
+    ctx->sleep.log       = r->connection->log;
6825
 
+
6826
 
+    return NGX_OK;
6827
 
+}
6828
 
+
6829
 
+ngx_int_t
6830
 
+ngx_http_echo_eval_cmd_args(ngx_http_request_t *r,
6831
 
+        ngx_http_echo_cmd_t *cmd, ngx_array_t *computed_args,
6832
 
+        ngx_array_t *opts)
6833
 
+{
6834
 
+    ngx_uint_t                       i;
6835
 
+    ngx_array_t                     *args = cmd->args;
6836
 
+    ngx_str_t                       *arg, *raw, *opt;
6837
 
+    ngx_http_echo_arg_template_t    *value;
6838
 
+    ngx_flag_t                       expecting_opts = 1;
6839
 
+
6840
 
+
6841
 
+    value = args->elts;
6842
 
+
6843
 
+    for (i = 0; i < args->nelts; i++) {
6844
 
+        raw = &value[i].raw_value;
6845
 
+
6846
 
+        if (value[i].lengths == NULL && raw->len > 0) {
6847
 
+            if (expecting_opts) {
6848
 
+                if (raw->len == 1 || raw->data[0] != '-') {
6849
 
+                    expecting_opts = 0;
6850
 
+
6851
 
+                } else if (raw->data[1] == '-') {
6852
 
+                    expecting_opts = 0;
6853
 
+                    continue;
6854
 
+
6855
 
+                } else {
6856
 
+                    opt = ngx_array_push(opts);
6857
 
+                    if (opt == NULL) {
6858
 
+                        return NGX_HTTP_INTERNAL_SERVER_ERROR;
6859
 
+                    }
6860
 
+
6861
 
+                    opt->len = raw->len - 1;
6862
 
+                    opt->data = raw->data + 1;
6863
 
+
6864
 
+                    continue;
6865
 
+                }
6866
 
+            }
6867
 
+        }
6868
 
+
6869
 
+        arg = ngx_array_push(computed_args);
6870
 
+        if (arg == NULL) {
6871
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
6872
 
+        }
6873
 
+
6874
 
+        if (value[i].lengths == NULL) { /* does not contain vars */
6875
 
+            dd("Using raw value \"%.*s\"", (int) raw->len, raw->data);
6876
 
+            *arg = *raw;
6877
 
+
6878
 
+        } else {
6879
 
+            if (ngx_http_script_run(r, arg, value[i].lengths->elts,
6880
 
+                        0, value[i].values->elts) == NULL)
6881
 
+            {
6882
 
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
6883
 
+            }
6884
 
+        }
6885
 
+    }
6886
 
+
6887
 
+    return NGX_OK;
6888
 
+}
6889
 
+
6890
 
+
6891
 
+ngx_int_t
6892
 
+ngx_http_echo_send_chain_link(ngx_http_request_t* r,
6893
 
+        ngx_http_echo_ctx_t *ctx, ngx_chain_t *cl)
6894
 
+{
6895
 
+    ngx_int_t       rc;
6896
 
+    size_t          size;
6897
 
+    ngx_chain_t     *p;
6898
 
+
6899
 
+    rc = ngx_http_echo_send_header_if_needed(r, ctx);
6900
 
+
6901
 
+    if (r->header_only || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
6902
 
+        return rc;
6903
 
+    }
6904
 
+
6905
 
+    if (r->http_version < NGX_HTTP_VERSION_11 && !ctx->headers_sent) {
6906
 
+        ctx->headers_sent = 1;
6907
 
+
6908
 
+        size = 0;
6909
 
+
6910
 
+        for (p = cl; p; p = p->next) {
6911
 
+            if (p->buf->memory) {
6912
 
+                size += p->buf->last - p->buf->pos;
6913
 
+            }
6914
 
+        }
6915
 
+
6916
 
+        r->headers_out.content_length_n = (off_t) size;
6917
 
+
6918
 
+        if (r->headers_out.content_length) {
6919
 
+            r->headers_out.content_length->hash = 0;
6920
 
+        }
6921
 
+
6922
 
+        r->headers_out.content_length = NULL;
6923
 
+
6924
 
+        rc = ngx_http_send_header(r);
6925
 
+
6926
 
+        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
6927
 
+            return rc;
6928
 
+        }
6929
 
+    }
6930
 
+
6931
 
+    if (cl == NULL) {
6932
 
+
6933
 
+#if defined(nginx_version) && nginx_version <= 8004
6934
 
+
6935
 
+        /* earlier versions of nginx does not allow subrequests
6936
 
+            to send last_buf themselves */
6937
 
+        if (r != r->main) {
6938
 
+            return NGX_OK;
6939
 
+        }
6940
 
+
6941
 
+#endif
6942
 
+
6943
 
+        rc = ngx_http_send_special(r, NGX_HTTP_LAST);
6944
 
+        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
6945
 
+            return rc;
6946
 
+        }
6947
 
+
6948
 
+        return NGX_OK;
6949
 
+    }
6950
 
+
6951
 
+    return ngx_http_output_filter(r, cl);
6952
 
+}
6953
 
+
6954
 
+ngx_int_t
6955
 
+ngx_http_echo_send_header_if_needed(ngx_http_request_t* r,
6956
 
+        ngx_http_echo_ctx_t *ctx) {
6957
 
+    /* ngx_int_t   rc; */
6958
 
+
6959
 
+    if ( ! ctx->headers_sent ) {
6960
 
+        r->headers_out.status = NGX_HTTP_OK;
6961
 
+
6962
 
+        if (ngx_http_set_content_type(r) != NGX_OK) {
6963
 
+            return NGX_HTTP_INTERNAL_SERVER_ERROR;
6964
 
+        }
6965
 
+
6966
 
+        ngx_http_clear_content_length(r);
6967
 
+        ngx_http_clear_accept_ranges(r);
6968
 
+
6969
 
+        if (r->http_version >= NGX_HTTP_VERSION_11) {
6970
 
+            ctx->headers_sent = 1;
6971
 
+            return ngx_http_send_header(r);
6972
 
+        }
6973
 
+    }
6974
 
+
6975
 
+    return NGX_OK;
6976
 
+}
6977
 
+
6978
 
+ssize_t
6979
 
+ngx_http_echo_atosz(u_char *line, size_t n) {
6980
 
+    ssize_t  value;
6981
 
+
6982
 
+    if (n == 0) {
6983
 
+        return NGX_ERROR;
6984
 
+    }
6985
 
+
6986
 
+    for (value = 0; n--; line++) {
6987
 
+        if (*line == '_') { /* we ignore undercores */
6988
 
+            continue;
6989
 
+        }
6990
 
+
6991
 
+        if (*line < '0' || *line > '9') {
6992
 
+            return NGX_ERROR;
6993
 
+        }
6994
 
+
6995
 
+        value = value * 10 + (*line - '0');
6996
 
+    }
6997
 
+
6998
 
+    if (value < 0) {
6999
 
+        return NGX_ERROR;
7000
 
+    } else {
7001
 
+        return value;
7002
 
+    }
7003
 
+}
7004
 
+
7005
 
+/* Modified from the ngx_strlcasestrn function in ngx_string.h
7006
 
+ * Copyright (C) by Igor Sysoev */
7007
 
+u_char *
7008
 
+ngx_http_echo_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n)
7009
 
+{
7010
 
+    ngx_uint_t  c1, c2;
7011
 
+
7012
 
+    c2 = (ngx_uint_t) *s2++;
7013
 
+    last -= n;
7014
 
+
7015
 
+    do {
7016
 
+        do {
7017
 
+            if (s1 >= last) {
7018
 
+                return NULL;
7019
 
+            }
7020
 
+
7021
 
+            c1 = (ngx_uint_t) *s1++;
7022
 
+
7023
 
+        } while (c1 != c2);
7024
 
+
7025
 
+    } while (ngx_strncmp(s1, s2, n) != 0);
7026
 
+
7027
 
+    return --s1;
7028
 
+}
7029
 
+
7030
 
+
7031
 
+ngx_int_t
7032
 
+ngx_http_echo_post_request_at_head(ngx_http_request_t *r,
7033
 
+        ngx_http_posted_request_t *pr)
7034
 
+{
7035
 
+    dd_enter();
7036
 
+
7037
 
+    if (pr == NULL) {
7038
 
+        pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));
7039
 
+        if (pr == NULL) {
7040
 
+            return NGX_ERROR;
7041
 
+        }
7042
 
+    }
7043
 
+
7044
 
+    pr->request = r;
7045
 
+    pr->next = r->main->posted_requests;
7046
 
+    r->main->posted_requests = pr;
7047
 
+
7048
 
+    return NGX_OK;
7049
 
+}
7050
 
+
7051
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_util.h
7052
 
===================================================================
7053
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
7054
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_util.h     2010-10-31 07:35:23.648338001 +0000
7055
 
@@ -0,0 +1,30 @@
7056
 
+#ifndef NGX_HTTP_ECHO_UTIL_H
7057
 
+#define NGX_HTTP_ECHO_UTIL_H
7058
 
+
7059
 
+#include "ngx_http_echo_module.h"
7060
 
+
7061
 
+#define ngx_http_echo_strcmp_const(a, b) \
7062
 
+    ngx_strncmp(a, b, sizeof(b) - 1)
7063
 
+
7064
 
+ngx_int_t ngx_http_echo_init_ctx(ngx_http_request_t *r, ngx_http_echo_ctx_t **ctx_ptr);
7065
 
+
7066
 
+ngx_int_t ngx_http_echo_eval_cmd_args(ngx_http_request_t *r,
7067
 
+        ngx_http_echo_cmd_t *cmd, ngx_array_t *computed_args,
7068
 
+        ngx_array_t *opts);
7069
 
+
7070
 
+ngx_int_t ngx_http_echo_send_header_if_needed(ngx_http_request_t* r,
7071
 
+        ngx_http_echo_ctx_t *ctx);
7072
 
+
7073
 
+ngx_int_t ngx_http_echo_send_chain_link(ngx_http_request_t* r,
7074
 
+        ngx_http_echo_ctx_t *ctx, ngx_chain_t *cl);
7075
 
+
7076
 
+ssize_t ngx_http_echo_atosz(u_char *line, size_t n);
7077
 
+
7078
 
+u_char * ngx_http_echo_strlstrn(u_char *s1, u_char *last, u_char *s2, size_t n);
7079
 
+
7080
 
+ngx_int_t ngx_http_echo_post_request_at_head(ngx_http_request_t *r,
7081
 
+        ngx_http_posted_request_t *pr);
7082
 
+
7083
 
+
7084
 
+#endif /* NGX_HTTP_ECHO_UTIL_H */
7085
 
+
7086
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_var.c
7087
 
===================================================================
7088
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
7089
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_var.c      2010-10-31 07:35:23.648338001 +0000
7090
 
@@ -0,0 +1,98 @@
7091
 
+#define DDEBUG 0
7092
 
+#include "ddebug.h"
7093
 
+
7094
 
+#include "ngx_http_echo_var.h"
7095
 
+#include "ngx_http_echo_timer.h"
7096
 
+#include "ngx_http_echo_request_info.h"
7097
 
+#include "ngx_http_echo_foreach.h"
7098
 
+
7099
 
+static ngx_int_t ngx_http_echo_incr_variable(ngx_http_request_t *r,
7100
 
+        ngx_http_variable_value_t *v, uintptr_t data);
7101
 
+
7102
 
+static ngx_http_variable_t ngx_http_echo_variables[] = {
7103
 
+    { ngx_string("echo_timer_elapsed"), NULL,
7104
 
+      ngx_http_echo_timer_elapsed_variable, 0,
7105
 
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
7106
 
+
7107
 
+    { ngx_string("echo_request_method"), NULL,
7108
 
+      ngx_http_echo_request_method_variable, 0,
7109
 
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
7110
 
+
7111
 
+    { ngx_string("echo_cacheable_request_uri"), NULL,
7112
 
+      ngx_http_echo_cacheable_request_uri_variable, 0,
7113
 
+      0, 0 },
7114
 
+
7115
 
+    { ngx_string("echo_request_uri"), NULL,
7116
 
+      ngx_http_echo_request_uri_variable, 0,
7117
 
+      0, 0 },
7118
 
+
7119
 
+    { ngx_string("echo_client_request_method"), NULL,
7120
 
+      ngx_http_echo_client_request_method_variable, 0,
7121
 
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
7122
 
+
7123
 
+    { ngx_string("echo_request_body"), NULL,
7124
 
+      ngx_http_echo_request_body_variable, 0,
7125
 
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
7126
 
+
7127
 
+    { ngx_string("echo_client_request_headers"), NULL,
7128
 
+      ngx_http_echo_client_request_headers_variable, 0,
7129
 
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
7130
 
+
7131
 
+    { ngx_string("echo_it"), NULL,
7132
 
+      ngx_http_echo_it_variable, 0,
7133
 
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
7134
 
+
7135
 
+    { ngx_string("echo_incr"), NULL,
7136
 
+      ngx_http_echo_incr_variable, 0,
7137
 
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
7138
 
+
7139
 
+    { ngx_string("echo_response_status"), NULL,
7140
 
+      ngx_http_echo_response_status_variable, 0,
7141
 
+      NGX_HTTP_VAR_NOCACHEABLE, 0 },
7142
 
+
7143
 
+    { ngx_null_string, NULL, NULL, 0, 0, 0 }
7144
 
+};
7145
 
+
7146
 
+ngx_int_t
7147
 
+ngx_http_echo_add_variables(ngx_conf_t *cf) {
7148
 
+    ngx_http_variable_t *var, *v;
7149
 
+    for (v = ngx_http_echo_variables; v->name.len; v++) {
7150
 
+        var = ngx_http_add_variable(cf, &v->name, v->flags);
7151
 
+        if (var == NULL) {
7152
 
+            return NGX_ERROR;
7153
 
+        }
7154
 
+        var->get_handler = v->get_handler;
7155
 
+        var->data = v->data;
7156
 
+    }
7157
 
+    return NGX_OK;
7158
 
+}
7159
 
+
7160
 
+static ngx_int_t
7161
 
+ngx_http_echo_incr_variable(ngx_http_request_t *r,
7162
 
+        ngx_http_variable_value_t *v, uintptr_t data) {
7163
 
+    ngx_http_echo_ctx_t         *ctx;
7164
 
+    u_char                      *p;
7165
 
+
7166
 
+    ctx = ngx_http_get_module_ctx(r->main, ngx_http_echo_module);
7167
 
+
7168
 
+    if (ctx == NULL) {
7169
 
+        return NGX_ERROR;
7170
 
+    }
7171
 
+
7172
 
+    ctx->counter++;
7173
 
+
7174
 
+    p = ngx_palloc(r->pool, NGX_INT_T_LEN);
7175
 
+    if (p == NULL) {
7176
 
+        return NGX_ERROR;
7177
 
+    }
7178
 
+
7179
 
+    v->len = ngx_sprintf(p, "%ui", ctx->counter) - p;
7180
 
+    v->data = p;
7181
 
+
7182
 
+    v->valid = 1;
7183
 
+    v->not_found = 0;
7184
 
+    v->no_cacheable = 1;
7185
 
+
7186
 
+    return NGX_OK;
7187
 
+}
7188
 
+
7189
 
Index: 0.8/modules/nginx-echo/src/ngx_http_echo_var.h
7190
 
===================================================================
7191
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
7192
 
+++ 0.8/modules/nginx-echo/src/ngx_http_echo_var.h      2010-10-31 07:35:23.648338001 +0000
7193
 
@@ -0,0 +1,9 @@
7194
 
+#ifndef ECHO_VAR_H
7195
 
+#define ECHO_VAR_H
7196
 
+
7197
 
+#include "ngx_http_echo_module.h"
7198
 
+
7199
 
+ngx_int_t ngx_http_echo_add_variables(ngx_conf_t *cf);
7200
 
+
7201
 
+#endif /* ECHO_VAR_H */
7202
 
+
7203
 
Index: 0.8/modules/nginx-echo/test/inc/Module/AutoInstall.pm
7204
 
===================================================================
7205
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
7206
 
+++ 0.8/modules/nginx-echo/test/inc/Module/AutoInstall.pm       2010-10-31 07:35:47.718338000 +0000
7207
 
@@ -0,0 +1,820 @@
7208
 
+#line 1
7209
 
+package Module::AutoInstall;
7210
 
+
7211
 
+use strict;
7212
 
+use Cwd                 ();
7213
 
+use ExtUtils::MakeMaker ();
7214
 
+
7215
 
+use vars qw{$VERSION};
7216
 
+BEGIN {
7217
 
+       $VERSION = '1.03';
7218
 
+}
7219
 
+
7220
 
+# special map on pre-defined feature sets
7221
 
+my %FeatureMap = (
7222
 
+    ''      => 'Core Features',    # XXX: deprecated
7223
 
+    '-core' => 'Core Features',
7224
 
+);
7225
 
+
7226
 
+# various lexical flags
7227
 
+my ( @Missing, @Existing,  %DisabledTests, $UnderCPAN,     $HasCPANPLUS );
7228
 
+my (
7229
 
+    $Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly, $AllDeps
7230
 
+);
7231
 
+my ( $PostambleActions, $PostambleUsed );
7232
 
+
7233
 
+# See if it's a testing or non-interactive session
7234
 
+_accept_default( $ENV{AUTOMATED_TESTING} or ! -t STDIN );
7235
 
+_init();
7236
 
+
7237
 
+sub _accept_default {
7238
 
+    $AcceptDefault = shift;
7239
 
+}
7240
 
+
7241
 
+sub missing_modules {
7242
 
+    return @Missing;
7243
 
+}
7244
 
+
7245
 
+sub do_install {
7246
 
+    __PACKAGE__->install(
7247
 
+        [
7248
 
+            $Config
7249
 
+            ? ( UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} )
7250
 
+            : ()
7251
 
+        ],
7252
 
+        @Missing,
7253
 
+    );
7254
 
+}
7255
 
+
7256
 
+# initialize various flags, and/or perform install
7257
 
+sub _init {
7258
 
+    foreach my $arg (
7259
 
+        @ARGV,
7260
 
+        split(
7261
 
+            /[\s\t]+/,
7262
 
+            $ENV{PERL_AUTOINSTALL} || $ENV{PERL_EXTUTILS_AUTOINSTALL} || ''
7263
 
+        )
7264
 
+      )
7265
 
+    {
7266
 
+        if ( $arg =~ /^--config=(.*)$/ ) {
7267
 
+            $Config = [ split( ',', $1 ) ];
7268
 
+        }
7269
 
+        elsif ( $arg =~ /^--installdeps=(.*)$/ ) {
7270
 
+            __PACKAGE__->install( $Config, @Missing = split( /,/, $1 ) );
7271
 
+            exit 0;
7272
 
+        }
7273
 
+        elsif ( $arg =~ /^--default(?:deps)?$/ ) {
7274
 
+            $AcceptDefault = 1;
7275
 
+        }
7276
 
+        elsif ( $arg =~ /^--check(?:deps)?$/ ) {
7277
 
+            $CheckOnly = 1;
7278
 
+        }
7279
 
+        elsif ( $arg =~ /^--skip(?:deps)?$/ ) {
7280
 
+            $SkipInstall = 1;
7281
 
+        }
7282
 
+        elsif ( $arg =~ /^--test(?:only)?$/ ) {
7283
 
+            $TestOnly = 1;
7284
 
+        }
7285
 
+        elsif ( $arg =~ /^--all(?:deps)?$/ ) {
7286
 
+            $AllDeps = 1;
7287
 
+        }
7288
 
+    }
7289
 
+}
7290
 
+
7291
 
+# overrides MakeMaker's prompt() to automatically accept the default choice
7292
 
+sub _prompt {
7293
 
+    goto &ExtUtils::MakeMaker::prompt unless $AcceptDefault;
7294
 
+
7295
 
+    my ( $prompt, $default ) = @_;
7296
 
+    my $y = ( $default =~ /^[Yy]/ );
7297
 
+
7298
 
+    print $prompt, ' [', ( $y ? 'Y' : 'y' ), '/', ( $y ? 'n' : 'N' ), '] ';
7299
 
+    print "$default\n";
7300
 
+    return $default;
7301
 
+}
7302
 
+
7303
 
+# the workhorse
7304
 
+sub import {
7305
 
+    my $class = shift;
7306
 
+    my @args  = @_ or return;
7307
 
+    my $core_all;
7308
 
+
7309
 
+    print "*** $class version " . $class->VERSION . "\n";
7310
 
+    print "*** Checking for Perl dependencies...\n";
7311
 
+
7312
 
+    my $cwd = Cwd::cwd();
7313
 
+
7314
 
+    $Config = [];
7315
 
+
7316
 
+    my $maxlen = length(
7317
 
+        (
7318
 
+            sort   { length($b) <=> length($a) }
7319
 
+              grep { /^[^\-]/ }
7320
 
+              map  {
7321
 
+                ref($_)
7322
 
+                  ? ( ( ref($_) eq 'HASH' ) ? keys(%$_) : @{$_} )
7323
 
+                  : ''
7324
 
+              }
7325
 
+              map { +{@args}->{$_} }
7326
 
+              grep { /^[^\-]/ or /^-core$/i } keys %{ +{@args} }
7327
 
+        )[0]
7328
 
+    );
7329
 
+
7330
 
+    # We want to know if we're under CPAN early to avoid prompting, but
7331
 
+    # if we aren't going to try and install anything anyway then skip the
7332
 
+    # check entirely since we don't want to have to load (and configure)
7333
 
+    # an old CPAN just for a cosmetic message
7334
 
+
7335
 
+    $UnderCPAN = _check_lock(1) unless $SkipInstall;
7336
 
+
7337
 
+    while ( my ( $feature, $modules ) = splice( @args, 0, 2 ) ) {
7338
 
+        my ( @required, @tests, @skiptests );
7339
 
+        my $default  = 1;
7340
 
+        my $conflict = 0;
7341
 
+
7342
 
+        if ( $feature =~ m/^-(\w+)$/ ) {
7343
 
+            my $option = lc($1);
7344
 
+
7345
 
+            # check for a newer version of myself
7346
 
+            _update_to( $modules, @_ ) and return if $option eq 'version';
7347
 
+
7348
 
+            # sets CPAN configuration options
7349
 
+            $Config = $modules if $option eq 'config';
7350
 
+
7351
 
+            # promote every features to core status
7352
 
+            $core_all = ( $modules =~ /^all$/i ) and next
7353
 
+              if $option eq 'core';
7354
 
+
7355
 
+            next unless $option eq 'core';
7356
 
+        }
7357
 
+
7358
 
+        print "[" . ( $FeatureMap{ lc($feature) } || $feature ) . "]\n";
7359
 
+
7360
 
+        $modules = [ %{$modules} ] if UNIVERSAL::isa( $modules, 'HASH' );
7361
 
+
7362
 
+        unshift @$modules, -default => &{ shift(@$modules) }
7363
 
+          if ( ref( $modules->[0] ) eq 'CODE' );    # XXX: bugward combatability
7364
 
+
7365
 
+        while ( my ( $mod, $arg ) = splice( @$modules, 0, 2 ) ) {
7366
 
+            if ( $mod =~ m/^-(\w+)$/ ) {
7367
 
+                my $option = lc($1);
7368
 
+
7369
 
+                $default   = $arg    if ( $option eq 'default' );
7370
 
+                $conflict  = $arg    if ( $option eq 'conflict' );
7371
 
+                @tests     = @{$arg} if ( $option eq 'tests' );
7372
 
+                @skiptests = @{$arg} if ( $option eq 'skiptests' );
7373
 
+
7374
 
+                next;
7375
 
+            }
7376
 
+
7377
 
+            printf( "- %-${maxlen}s ...", $mod );
7378
 
+
7379
 
+            if ( $arg and $arg =~ /^\D/ ) {
7380
 
+                unshift @$modules, $arg;
7381
 
+                $arg = 0;
7382
 
+            }
7383
 
+
7384
 
+            # XXX: check for conflicts and uninstalls(!) them.
7385
 
+            my $cur = _load($mod);
7386
 
+            if (_version_cmp ($cur, $arg) >= 0)
7387
 
+            {
7388
 
+                print "loaded. ($cur" . ( $arg ? " >= $arg" : '' ) . ")\n";
7389
 
+                push @Existing, $mod => $arg;
7390
 
+                $DisabledTests{$_} = 1 for map { glob($_) } @skiptests;
7391
 
+            }
7392
 
+            else {
7393
 
+                if (not defined $cur)   # indeed missing
7394
 
+                {
7395
 
+                    print "missing." . ( $arg ? " (would need $arg)" : '' ) . "\n";
7396
 
+                }
7397
 
+                else
7398
 
+                {
7399
 
+                    # no need to check $arg as _version_cmp ($cur, undef) would satisfy >= above
7400
 
+                    print "too old. ($cur < $arg)\n";
7401
 
+                }
7402
 
+
7403
 
+                push @required, $mod => $arg;
7404
 
+            }
7405
 
+        }
7406
 
+
7407
 
+        next unless @required;
7408
 
+
7409
 
+        my $mandatory = ( $feature eq '-core' or $core_all );
7410
 
+
7411
 
+        if (
7412
 
+            !$SkipInstall
7413
 
+            and (
7414
 
+                $CheckOnly
7415
 
+                or ($mandatory and $UnderCPAN)
7416
 
+                or $AllDeps
7417
 
+                or _prompt(
7418
 
+                    qq{==> Auto-install the }
7419
 
+                      . ( @required / 2 )
7420
 
+                      . ( $mandatory ? ' mandatory' : ' optional' )
7421
 
+                      . qq{ module(s) from CPAN?},
7422
 
+                    $default ? 'y' : 'n',
7423
 
+                ) =~ /^[Yy]/
7424
 
+            )
7425
 
+          )
7426
 
+        {
7427
 
+            push( @Missing, @required );
7428
 
+            $DisabledTests{$_} = 1 for map { glob($_) } @skiptests;
7429
 
+        }
7430
 
+
7431
 
+        elsif ( !$SkipInstall
7432
 
+            and $default
7433
 
+            and $mandatory
7434
 
+            and
7435
 
+            _prompt( qq{==> The module(s) are mandatory! Really skip?}, 'n', )
7436
 
+            =~ /^[Nn]/ )
7437
 
+        {
7438
 
+            push( @Missing, @required );
7439
 
+            $DisabledTests{$_} = 1 for map { glob($_) } @skiptests;
7440
 
+        }
7441
 
+
7442
 
+        else {
7443
 
+            $DisabledTests{$_} = 1 for map { glob($_) } @tests;
7444
 
+        }
7445
 
+    }
7446
 
+
7447
 
+    if ( @Missing and not( $CheckOnly or $UnderCPAN ) ) {
7448
 
+        require Config;
7449
 
+        print
7450
 
+"*** Dependencies will be installed the next time you type '$Config::Config{make}'.\n";
7451
 
+
7452
 
+        # make an educated guess of whether we'll need root permission.
7453
 
+        print "    (You may need to do that as the 'root' user.)\n"
7454
 
+          if eval '$>';
7455
 
+    }
7456
 
+    print "*** $class configuration finished.\n";
7457
 
+
7458
 
+    chdir $cwd;
7459
 
+
7460
 
+    # import to main::
7461
 
+    no strict 'refs';
7462
 
+    *{'main::WriteMakefile'} = \&Write if caller(0) eq 'main';
7463
 
+
7464
 
+    return (@Existing, @Missing);
7465
 
+}
7466
 
+
7467
 
+sub _running_under {
7468
 
+    my $thing = shift;
7469
 
+    print <<"END_MESSAGE";
7470
 
+*** Since we're running under ${thing}, I'll just let it take care
7471
 
+    of the dependency's installation later.
7472
 
+END_MESSAGE
7473
 
+    return 1;
7474
 
+}
7475
 
+
7476
 
+# Check to see if we are currently running under CPAN.pm and/or CPANPLUS;
7477
 
+# if we are, then we simply let it taking care of our dependencies
7478
 
+sub _check_lock {
7479
 
+    return unless @Missing or @_;
7480
 
+
7481
 
+    my $cpan_env = $ENV{PERL5_CPAN_IS_RUNNING};
7482
 
+
7483
 
+    if ($ENV{PERL5_CPANPLUS_IS_RUNNING}) {
7484
 
+        return _running_under($cpan_env ? 'CPAN' : 'CPANPLUS');
7485
 
+    }
7486
 
+
7487
 
+    require CPAN;
7488
 
+
7489
 
+    if ($CPAN::VERSION > '1.89') {
7490
 
+        if ($cpan_env) {
7491
 
+            return _running_under('CPAN');
7492
 
+        }
7493
 
+        return; # CPAN.pm new enough, don't need to check further
7494
 
+    }
7495
 
+
7496
 
+    # last ditch attempt, this -will- configure CPAN, very sorry
7497
 
+
7498
 
+    _load_cpan(1); # force initialize even though it's already loaded
7499
 
+
7500
 
+    # Find the CPAN lock-file
7501
 
+    my $lock = MM->catfile( $CPAN::Config->{cpan_home}, ".lock" );
7502
 
+    return unless -f $lock;
7503
 
+
7504
 
+    # Check the lock
7505
 
+    local *LOCK;
7506
 
+    return unless open(LOCK, $lock);
7507
 
+
7508
 
+    if (
7509
 
+            ( $^O eq 'MSWin32' ? _under_cpan() : <LOCK> == getppid() )
7510
 
+        and ( $CPAN::Config->{prerequisites_policy} || '' ) ne 'ignore'
7511
 
+    ) {
7512
 
+        print <<'END_MESSAGE';
7513
 
+
7514
 
+*** Since we're running under CPAN, I'll just let it take care
7515
 
+    of the dependency's installation later.
7516
 
+END_MESSAGE
7517
 
+        return 1;
7518
 
+    }
7519
 
+
7520
 
+    close LOCK;
7521
 
+    return;
7522
 
+}
7523
 
+
7524
 
+sub install {
7525
 
+    my $class = shift;
7526
 
+
7527
 
+    my $i;    # used below to strip leading '-' from config keys
7528
 
+    my @config = ( map { s/^-// if ++$i; $_ } @{ +shift } );
7529
 
+
7530
 
+    my ( @modules, @installed );
7531
 
+    while ( my ( $pkg, $ver ) = splice( @_, 0, 2 ) ) {
7532
 
+
7533
 
+        # grep out those already installed
7534
 
+        if ( _version_cmp( _load($pkg), $ver ) >= 0 ) {
7535
 
+            push @installed, $pkg;
7536
 
+        }
7537
 
+        else {
7538
 
+            push @modules, $pkg, $ver;
7539
 
+        }
7540
 
+    }
7541
 
+
7542
 
+    return @installed unless @modules;  # nothing to do
7543
 
+    return @installed if _check_lock(); # defer to the CPAN shell
7544
 
+
7545
 
+    print "*** Installing dependencies...\n";
7546
 
+
7547
 
+    return unless _connected_to('cpan.org');
7548
 
+
7549
 
+    my %args = @config;
7550
 
+    my %failed;
7551
 
+    local *FAILED;
7552
 
+    if ( $args{do_once} and open( FAILED, '.#autoinstall.failed' ) ) {
7553
 
+        while (<FAILED>) { chomp; $failed{$_}++ }
7554
 
+        close FAILED;
7555
 
+
7556
 
+        my @newmod;
7557
 
+        while ( my ( $k, $v ) = splice( @modules, 0, 2 ) ) {
7558
 
+            push @newmod, ( $k => $v ) unless $failed{$k};
7559
 
+        }
7560
 
+        @modules = @newmod;
7561
 
+    }
7562
 
+
7563
 
+    if ( _has_cpanplus() and not $ENV{PERL_AUTOINSTALL_PREFER_CPAN} ) {
7564
 
+        _install_cpanplus( \@modules, \@config );
7565
 
+    } else {
7566
 
+        _install_cpan( \@modules, \@config );
7567
 
+    }
7568
 
+
7569
 
+    print "*** $class installation finished.\n";
7570
 
+
7571
 
+    # see if we have successfully installed them
7572
 
+    while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) {
7573
 
+        if ( _version_cmp( _load($pkg), $ver ) >= 0 ) {
7574
 
+            push @installed, $pkg;
7575
 
+        }
7576
 
+        elsif ( $args{do_once} and open( FAILED, '>> .#autoinstall.failed' ) ) {
7577
 
+            print FAILED "$pkg\n";
7578
 
+        }
7579
 
+    }
7580
 
+
7581
 
+    close FAILED if $args{do_once};
7582
 
+
7583
 
+    return @installed;
7584
 
+}
7585
 
+
7586
 
+sub _install_cpanplus {
7587
 
+    my @modules   = @{ +shift };
7588
 
+    my @config    = _cpanplus_config( @{ +shift } );
7589
 
+    my $installed = 0;
7590
 
+
7591
 
+    require CPANPLUS::Backend;
7592
 
+    my $cp   = CPANPLUS::Backend->new;
7593
 
+    my $conf = $cp->configure_object;
7594
 
+
7595
 
+    return unless $conf->can('conf') # 0.05x+ with "sudo" support
7596
 
+               or _can_write($conf->_get_build('base'));  # 0.04x
7597
 
+
7598
 
+    # if we're root, set UNINST=1 to avoid trouble unless user asked for it.
7599
 
+    my $makeflags = $conf->get_conf('makeflags') || '';
7600
 
+    if ( UNIVERSAL::isa( $makeflags, 'HASH' ) ) {
7601
 
+        # 0.03+ uses a hashref here
7602
 
+        $makeflags->{UNINST} = 1 unless exists $makeflags->{UNINST};
7603
 
+
7604
 
+    } else {
7605
 
+        # 0.02 and below uses a scalar
7606
 
+        $makeflags = join( ' ', split( ' ', $makeflags ), 'UNINST=1' )
7607
 
+          if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } );
7608
 
+
7609
 
+    }
7610
 
+    $conf->set_conf( makeflags => $makeflags );
7611
 
+    $conf->set_conf( prereqs   => 1 );
7612
 
+
7613
 
+
7614
 
+
7615
 
+    while ( my ( $key, $val ) = splice( @config, 0, 2 ) ) {
7616
 
+        $conf->set_conf( $key, $val );
7617
 
+    }
7618
 
+
7619
 
+    my $modtree = $cp->module_tree;
7620
 
+    while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) {
7621
 
+        print "*** Installing $pkg...\n";
7622
 
+
7623
 
+        MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall;
7624
 
+
7625
 
+        my $success;
7626
 
+        my $obj = $modtree->{$pkg};
7627
 
+
7628
 
+        if ( $obj and _version_cmp( $obj->{version}, $ver ) >= 0 ) {
7629
 
+            my $pathname = $pkg;
7630
 
+            $pathname =~ s/::/\\W/;
7631
 
+
7632
 
+            foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) {
7633
 
+                delete $INC{$inc};
7634
 
+            }
7635
 
+
7636
 
+            my $rv = $cp->install( modules => [ $obj->{module} ] );
7637
 
+
7638
 
+            if ( $rv and ( $rv->{ $obj->{module} } or $rv->{ok} ) ) {
7639
 
+                print "*** $pkg successfully installed.\n";
7640
 
+                $success = 1;
7641
 
+            } else {
7642
 
+                print "*** $pkg installation cancelled.\n";
7643
 
+                $success = 0;
7644
 
+            }
7645
 
+
7646
 
+            $installed += $success;
7647
 
+        } else {
7648
 
+            print << ".";
7649
 
+*** Could not find a version $ver or above for $pkg; skipping.
7650
 
+.
7651
 
+        }
7652
 
+
7653
 
+        MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall;
7654
 
+    }
7655
 
+
7656
 
+    return $installed;
7657
 
+}
7658
 
+
7659
 
+sub _cpanplus_config {
7660
 
+       my @config = ();
7661
 
+       while ( @_ ) {
7662
 
+               my ($key, $value) = (shift(), shift());
7663
 
+               if ( $key eq 'prerequisites_policy' ) {
7664
 
+                       if ( $value eq 'follow' ) {
7665
 
+                               $value = CPANPLUS::Internals::Constants::PREREQ_INSTALL();
7666
 
+                       } elsif ( $value eq 'ask' ) {
7667
 
+                               $value = CPANPLUS::Internals::Constants::PREREQ_ASK();
7668
 
+                       } elsif ( $value eq 'ignore' ) {
7669
 
+                               $value = CPANPLUS::Internals::Constants::PREREQ_IGNORE();
7670
 
+                       } else {
7671
 
+                               die "*** Cannot convert option $key = '$value' to CPANPLUS version.\n";
7672
 
+                       }
7673
 
+               } else {
7674
 
+                       die "*** Cannot convert option $key to CPANPLUS version.\n";
7675
 
+               }
7676
 
+       }
7677
 
+       return @config;
7678
 
+}
7679
 
+
7680
 
+sub _install_cpan {
7681
 
+    my @modules   = @{ +shift };
7682
 
+    my @config    = @{ +shift };
7683
 
+    my $installed = 0;
7684
 
+    my %args;
7685
 
+
7686
 
+    _load_cpan();
7687
 
+    require Config;
7688
 
+
7689
 
+    if (CPAN->VERSION < 1.80) {
7690
 
+        # no "sudo" support, probe for writableness
7691
 
+        return unless _can_write( MM->catfile( $CPAN::Config->{cpan_home}, 'sources' ) )
7692
 
+                  and _can_write( $Config::Config{sitelib} );
7693
 
+    }
7694
 
+
7695
 
+    # if we're root, set UNINST=1 to avoid trouble unless user asked for it.
7696
 
+    my $makeflags = $CPAN::Config->{make_install_arg} || '';
7697
 
+    $CPAN::Config->{make_install_arg} =
7698
 
+      join( ' ', split( ' ', $makeflags ), 'UNINST=1' )
7699
 
+      if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } );
7700
 
+
7701
 
+    # don't show start-up info
7702
 
+    $CPAN::Config->{inhibit_startup_message} = 1;
7703
 
+
7704
 
+    # set additional options
7705
 
+    while ( my ( $opt, $arg ) = splice( @config, 0, 2 ) ) {
7706
 
+        ( $args{$opt} = $arg, next )
7707
 
+          if $opt =~ /^force$/;    # pseudo-option
7708
 
+        $CPAN::Config->{$opt} = $arg;
7709
 
+    }
7710
 
+
7711
 
+    local $CPAN::Config->{prerequisites_policy} = 'follow';
7712
 
+
7713
 
+    while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) {
7714
 
+        MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall;
7715
 
+
7716
 
+        print "*** Installing $pkg...\n";
7717
 
+
7718
 
+        my $obj     = CPAN::Shell->expand( Module => $pkg );
7719
 
+        my $success = 0;
7720
 
+
7721
 
+        if ( $obj and _version_cmp( $obj->cpan_version, $ver ) >= 0 ) {
7722
 
+            my $pathname = $pkg;
7723
 
+            $pathname =~ s/::/\\W/;
7724
 
+
7725
 
+            foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) {
7726
 
+                delete $INC{$inc};
7727
 
+            }
7728
 
+
7729
 
+            my $rv = $args{force} ? CPAN::Shell->force( install => $pkg )
7730
 
+                                  : CPAN::Shell->install($pkg);
7731
 
+            $rv ||= eval {
7732
 
+                $CPAN::META->instance( 'CPAN::Distribution', $obj->cpan_file, )
7733
 
+                  ->{install}
7734
 
+                  if $CPAN::META;
7735
 
+            };
7736
 
+
7737
 
+            if ( $rv eq 'YES' ) {
7738
 
+                print "*** $pkg successfully installed.\n";
7739
 
+                $success = 1;
7740
 
+            }
7741
 
+            else {
7742
 
+                print "*** $pkg installation failed.\n";
7743
 
+                $success = 0;
7744
 
+            }
7745
 
+
7746
 
+            $installed += $success;
7747
 
+        }
7748
 
+        else {
7749
 
+            print << ".";
7750
 
+*** Could not find a version $ver or above for $pkg; skipping.
7751
 
+.
7752
 
+        }
7753
 
+
7754
 
+        MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall;
7755
 
+    }
7756
 
+
7757
 
+    return $installed;
7758
 
+}
7759
 
+
7760
 
+sub _has_cpanplus {
7761
 
+    return (
7762
 
+        $HasCPANPLUS = (
7763
 
+            $INC{'CPANPLUS/Config.pm'}
7764
 
+              or _load('CPANPLUS::Shell::Default')
7765
 
+        )
7766
 
+    );
7767
 
+}
7768
 
+
7769
 
+# make guesses on whether we're under the CPAN installation directory
7770
 
+sub _under_cpan {
7771
 
+    require Cwd;
7772
 
+    require File::Spec;
7773
 
+
7774
 
+    my $cwd  = File::Spec->canonpath( Cwd::cwd() );
7775
 
+    my $cpan = File::Spec->canonpath( $CPAN::Config->{cpan_home} );
7776
 
+
7777
 
+    return ( index( $cwd, $cpan ) > -1 );
7778
 
+}
7779
 
+
7780
 
+sub _update_to {
7781
 
+    my $class = __PACKAGE__;
7782
 
+    my $ver   = shift;
7783
 
+
7784
 
+    return
7785
 
+      if _version_cmp( _load($class), $ver ) >= 0;  # no need to upgrade
7786
 
+
7787
 
+    if (
7788
 
+        _prompt( "==> A newer version of $class ($ver) is required. Install?",
7789
 
+            'y' ) =~ /^[Nn]/
7790
 
+      )
7791
 
+    {
7792
 
+        die "*** Please install $class $ver manually.\n";
7793
 
+    }
7794
 
+
7795
 
+    print << ".";
7796
 
+*** Trying to fetch it from CPAN...
7797
 
+.
7798
 
+
7799
 
+    # install ourselves
7800
 
+    _load($class) and return $class->import(@_)
7801
 
+      if $class->install( [], $class, $ver );
7802
 
+
7803
 
+    print << '.'; exit 1;
7804
 
+
7805
 
+*** Cannot bootstrap myself. :-( Installation terminated.
7806
 
+.
7807
 
+}
7808
 
+
7809
 
+# check if we're connected to some host, using inet_aton
7810
 
+sub _connected_to {
7811
 
+    my $site = shift;
7812
 
+
7813
 
+    return (
7814
 
+        ( _load('Socket') and Socket::inet_aton($site) ) or _prompt(
7815
 
+            qq(
7816
 
+*** Your host cannot resolve the domain name '$site', which
7817
 
+    probably means the Internet connections are unavailable.
7818
 
+==> Should we try to install the required module(s) anyway?), 'n'
7819
 
+          ) =~ /^[Yy]/
7820
 
+    );
7821
 
+}
7822
 
+
7823
 
+# check if a directory is writable; may create it on demand
7824
 
+sub _can_write {
7825
 
+    my $path = shift;
7826
 
+    mkdir( $path, 0755 ) unless -e $path;
7827
 
+
7828
 
+    return 1 if -w $path;
7829
 
+
7830
 
+    print << ".";
7831
 
+*** You are not allowed to write to the directory '$path';
7832
 
+    the installation may fail due to insufficient permissions.
7833
 
+.
7834
 
+
7835
 
+    if (
7836
 
+        eval '$>' and lc(`sudo -V`) =~ /version/ and _prompt(
7837
 
+            qq(
7838
 
+==> Should we try to re-execute the autoinstall process with 'sudo'?),
7839
 
+            ((-t STDIN) ? 'y' : 'n')
7840
 
+        ) =~ /^[Yy]/
7841
 
+      )
7842
 
+    {
7843
 
+
7844
 
+        # try to bootstrap ourselves from sudo
7845
 
+        print << ".";
7846
 
+*** Trying to re-execute the autoinstall process with 'sudo'...
7847
 
+.
7848
 
+        my $missing = join( ',', @Missing );
7849
 
+        my $config = join( ',',
7850
 
+            UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} )
7851
 
+          if $Config;
7852
 
+
7853
 
+        return
7854
 
+          unless system( 'sudo', $^X, $0, "--config=$config",
7855
 
+            "--installdeps=$missing" );
7856
 
+
7857
 
+        print << ".";
7858
 
+*** The 'sudo' command exited with error!  Resuming...
7859
 
+.
7860
 
+    }
7861
 
+
7862
 
+    return _prompt(
7863
 
+        qq(
7864
 
+==> Should we try to install the required module(s) anyway?), 'n'
7865
 
+    ) =~ /^[Yy]/;
7866
 
+}
7867
 
+
7868
 
+# load a module and return the version it reports
7869
 
+sub _load {
7870
 
+    my $mod  = pop;    # class/instance doesn't matter
7871
 
+    my $file = $mod;
7872
 
+
7873
 
+    $file =~ s|::|/|g;
7874
 
+    $file .= '.pm';
7875
 
+
7876
 
+    local $@;
7877
 
+    return eval { require $file; $mod->VERSION } || ( $@ ? undef: 0 );
7878
 
+}
7879
 
+
7880
 
+# Load CPAN.pm and it's configuration
7881
 
+sub _load_cpan {
7882
 
+    return if $CPAN::VERSION and $CPAN::Config and not @_;
7883
 
+    require CPAN;
7884
 
+
7885
 
+    # CPAN-1.82+ adds CPAN::Config::AUTOLOAD to redirect to
7886
 
+    #    CPAN::HandleConfig->load. CPAN reports that the redirection
7887
 
+    #    is deprecated in a warning printed at the user.
7888
 
+
7889
 
+    # CPAN-1.81 expects CPAN::HandleConfig->load, does not have
7890
 
+    #   $CPAN::HandleConfig::VERSION but cannot handle
7891
 
+    #   CPAN::Config->load
7892
 
+
7893
 
+    # Which "versions expect CPAN::Config->load?
7894
 
+
7895
 
+    if ( $CPAN::HandleConfig::VERSION
7896
 
+        || CPAN::HandleConfig->can('load')
7897
 
+    ) {
7898
 
+        # Newer versions of CPAN have a HandleConfig module
7899
 
+        CPAN::HandleConfig->load;
7900
 
+    } else {
7901
 
+       # Older versions had the load method in Config directly
7902
 
+        CPAN::Config->load;
7903
 
+    }
7904
 
+}
7905
 
+
7906
 
+# compare two versions, either use Sort::Versions or plain comparison
7907
 
+# return values same as <=>
7908
 
+sub _version_cmp {
7909
 
+    my ( $cur, $min ) = @_;
7910
 
+    return -1 unless defined $cur;  # if 0 keep comparing
7911
 
+    return 1 unless $min;
7912
 
+
7913
 
+    $cur =~ s/\s+$//;
7914
 
+
7915
 
+    # check for version numbers that are not in decimal format
7916
 
+    if ( ref($cur) or ref($min) or $cur =~ /v|\..*\./ or $min =~ /v|\..*\./ ) {
7917
 
+        if ( ( $version::VERSION or defined( _load('version') )) and
7918
 
+             version->can('new')
7919
 
+            ) {
7920
 
+
7921
 
+            # use version.pm if it is installed.
7922
 
+            return version->new($cur) <=> version->new($min);
7923
 
+        }
7924
 
+        elsif ( $Sort::Versions::VERSION or defined( _load('Sort::Versions') ) )
7925
 
+        {
7926
 
+
7927
 
+            # use Sort::Versions as the sorting algorithm for a.b.c versions
7928
 
+            return Sort::Versions::versioncmp( $cur, $min );
7929
 
+        }
7930
 
+
7931
 
+        warn "Cannot reliably compare non-decimal formatted versions.\n"
7932
 
+          . "Please install version.pm or Sort::Versions.\n";
7933
 
+    }
7934
 
+
7935
 
+    # plain comparison
7936
 
+    local $^W = 0;    # shuts off 'not numeric' bugs
7937
 
+    return $cur <=> $min;
7938
 
+}
7939
 
+
7940
 
+# nothing; this usage is deprecated.
7941
 
+sub main::PREREQ_PM { return {}; }
7942
 
+
7943
 
+sub _make_args {
7944
 
+    my %args = @_;
7945
 
+
7946
 
+    $args{PREREQ_PM} = { %{ $args{PREREQ_PM} || {} }, @Existing, @Missing }
7947
 
+      if $UnderCPAN or $TestOnly;
7948
 
+
7949
 
+    if ( $args{EXE_FILES} and -e 'MANIFEST' ) {
7950
 
+        require ExtUtils::Manifest;
7951
 
+        my $manifest = ExtUtils::Manifest::maniread('MANIFEST');
7952
 
+
7953
 
+        $args{EXE_FILES} =
7954
 
+          [ grep { exists $manifest->{$_} } @{ $args{EXE_FILES} } ];
7955
 
+    }
7956
 
+
7957
 
+    $args{test}{TESTS} ||= 't/*.t';
7958
 
+    $args{test}{TESTS} = join( ' ',
7959
 
+        grep { !exists( $DisabledTests{$_} ) }
7960
 
+          map { glob($_) } split( /\s+/, $args{test}{TESTS} ) );
7961
 
+
7962
 
+    my $missing = join( ',', @Missing );
7963
 
+    my $config =
7964
 
+      join( ',', UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} )
7965
 
+      if $Config;
7966
 
+
7967
 
+    $PostambleActions = (
7968
 
+        ($missing and not $UnderCPAN)
7969
 
+        ? "\$(PERL) $0 --config=$config --installdeps=$missing"
7970
 
+        : "\$(NOECHO) \$(NOOP)"
7971
 
+    );
7972
 
+
7973
 
+    return %args;
7974
 
+}
7975
 
+
7976
 
+# a wrapper to ExtUtils::MakeMaker::WriteMakefile
7977
 
+sub Write {
7978
 
+    require Carp;
7979
 
+    Carp::croak "WriteMakefile: Need even number of args" if @_ % 2;
7980
 
+
7981
 
+    if ($CheckOnly) {
7982
 
+        print << ".";
7983
 
+*** Makefile not written in check-only mode.
7984
 
+.
7985
 
+        return;
7986
 
+    }
7987
 
+
7988
 
+    my %args = _make_args(@_);
7989
 
+
7990
 
+    no strict 'refs';
7991
 
+
7992
 
+    $PostambleUsed = 0;
7993
 
+    local *MY::postamble = \&postamble unless defined &MY::postamble;
7994
 
+    ExtUtils::MakeMaker::WriteMakefile(%args);
7995
 
+
7996
 
+    print << "." unless $PostambleUsed;
7997
 
+*** WARNING: Makefile written with customized MY::postamble() without
7998
 
+    including contents from Module::AutoInstall::postamble() --
7999
 
+    auto installation features disabled.  Please contact the author.
8000
 
+.
8001
 
+
8002
 
+    return 1;
8003
 
+}
8004
 
+
8005
 
+sub postamble {
8006
 
+    $PostambleUsed = 1;
8007
 
+
8008
 
+    return <<"END_MAKE";
8009
 
+
8010
 
+config :: installdeps
8011
 
+\t\$(NOECHO) \$(NOOP)
8012
 
+
8013
 
+checkdeps ::
8014
 
+\t\$(PERL) $0 --checkdeps
8015
 
+
8016
 
+installdeps ::
8017
 
+\t$PostambleActions
8018
 
+
8019
 
+END_MAKE
8020
 
+
8021
 
+}
8022
 
+
8023
 
+1;
8024
 
+
8025
 
+__END__
8026
 
+
8027
 
+#line 1071
8028
 
Index: 0.8/modules/nginx-echo/test/inc/Module/Install.pm
8029
 
===================================================================
8030
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
8031
 
+++ 0.8/modules/nginx-echo/test/inc/Module/Install.pm   2010-10-31 07:35:23.648338001 +0000
8032
 
@@ -0,0 +1,466 @@
8033
 
+#line 1
8034
 
+package Module::Install;
8035
 
+
8036
 
+# For any maintainers:
8037
 
+# The load order for Module::Install is a bit magic.
8038
 
+# It goes something like this...
8039
 
+#
8040
 
+# IF ( host has Module::Install installed, creating author mode ) {
8041
 
+#     1. Makefile.PL calls "use inc::Module::Install"
8042
 
+#     2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install
8043
 
+#     3. The installed version of inc::Module::Install loads
8044
 
+#     4. inc::Module::Install calls "require Module::Install"
8045
 
+#     5. The ./inc/ version of Module::Install loads
8046
 
+# } ELSE {
8047
 
+#     1. Makefile.PL calls "use inc::Module::Install"
8048
 
+#     2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install
8049
 
+#     3. The ./inc/ version of Module::Install loads
8050
 
+# }
8051
 
+
8052
 
+use 5.005;
8053
 
+use strict 'vars';
8054
 
+use Cwd        ();
8055
 
+use File::Find ();
8056
 
+use File::Path ();
8057
 
+
8058
 
+use vars qw{$VERSION $MAIN};
8059
 
+BEGIN {
8060
 
+       # All Module::Install core packages now require synchronised versions.
8061
 
+       # This will be used to ensure we don't accidentally load old or
8062
 
+       # different versions of modules.
8063
 
+       # This is not enforced yet, but will be some time in the next few
8064
 
+       # releases once we can make sure it won't clash with custom
8065
 
+       # Module::Install extensions.
8066
 
+       $VERSION = '0.99';
8067
 
+
8068
 
+       # Storage for the pseudo-singleton
8069
 
+       $MAIN    = undef;
8070
 
+
8071
 
+       *inc::Module::Install::VERSION = *VERSION;
8072
 
+       @inc::Module::Install::ISA     = __PACKAGE__;
8073
 
+
8074
 
+}
8075
 
+
8076
 
+sub import {
8077
 
+       my $class = shift;
8078
 
+       my $self  = $class->new(@_);
8079
 
+       my $who   = $self->_caller;
8080
 
+
8081
 
+       #-------------------------------------------------------------
8082
 
+       # all of the following checks should be included in import(),
8083
 
+       # to allow "eval 'require Module::Install; 1' to test
8084
 
+       # installation of Module::Install. (RT #51267)
8085
 
+       #-------------------------------------------------------------
8086
 
+
8087
 
+       # Whether or not inc::Module::Install is actually loaded, the
8088
 
+       # $INC{inc/Module/Install.pm} is what will still get set as long as
8089
 
+       # the caller loaded module this in the documented manner.
8090
 
+       # If not set, the caller may NOT have loaded the bundled version, and thus
8091
 
+       # they may not have a MI version that works with the Makefile.PL. This would
8092
 
+       # result in false errors or unexpected behaviour. And we don't want that.
8093
 
+       my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
8094
 
+       unless ( $INC{$file} ) { die <<"END_DIE" }
8095
 
+
8096
 
+Please invoke ${\__PACKAGE__} with:
8097
 
+
8098
 
+       use inc::${\__PACKAGE__};
8099
 
+
8100
 
+not:
8101
 
+
8102
 
+       use ${\__PACKAGE__};
8103
 
+
8104
 
+END_DIE
8105
 
+
8106
 
+       # This reportedly fixes a rare Win32 UTC file time issue, but
8107
 
+       # as this is a non-cross-platform XS module not in the core,
8108
 
+       # we shouldn't really depend on it. See RT #24194 for detail.
8109
 
+       # (Also, this module only supports Perl 5.6 and above).
8110
 
+       eval "use Win32::UTCFileTime" if $^O eq 'MSWin32' && $] >= 5.006;
8111
 
+
8112
 
+       # If the script that is loading Module::Install is from the future,
8113
 
+       # then make will detect this and cause it to re-run over and over
8114
 
+       # again. This is bad. Rather than taking action to touch it (which
8115
 
+       # is unreliable on some platforms and requires write permissions)
8116
 
+       # for now we should catch this and refuse to run.
8117
 
+       if ( -f $0 ) {
8118
 
+               my $s = (stat($0))[9];
8119
 
+
8120
 
+               # If the modification time is only slightly in the future,
8121
 
+               # sleep briefly to remove the problem.
8122
 
+               my $a = $s - time;
8123
 
+               if ( $a > 0 and $a < 5 ) { sleep 5 }
8124
 
+
8125
 
+               # Too far in the future, throw an error.
8126
 
+               my $t = time;
8127
 
+               if ( $s > $t ) { die <<"END_DIE" }
8128
 
+
8129
 
+Your installer $0 has a modification time in the future ($s > $t).
8130
 
+
8131
 
+This is known to create infinite loops in make.
8132
 
+
8133
 
+Please correct this, then run $0 again.
8134
 
+
8135
 
+END_DIE
8136
 
+       }
8137
 
+
8138
 
+
8139
 
+       # Build.PL was formerly supported, but no longer is due to excessive
8140
 
+       # difficulty in implementing every single feature twice.
8141
 
+       if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" }
8142
 
+
8143
 
+Module::Install no longer supports Build.PL.
8144
 
+
8145
 
+It was impossible to maintain duel backends, and has been deprecated.
8146
 
+
8147
 
+Please remove all Build.PL files and only use the Makefile.PL installer.
8148
 
+
8149
 
+END_DIE
8150
 
+
8151
 
+       #-------------------------------------------------------------
8152
 
+
8153
 
+       # To save some more typing in Module::Install installers, every...
8154
 
+       # use inc::Module::Install
8155
 
+       # ...also acts as an implicit use strict.
8156
 
+       $^H |= strict::bits(qw(refs subs vars));
8157
 
+
8158
 
+       #-------------------------------------------------------------
8159
 
+
8160
 
+       unless ( -f $self->{file} ) {
8161
 
+               foreach my $key (keys %INC) {
8162
 
+                       delete $INC{$key} if $key =~ /Module\/Install/;
8163
 
+               }
8164
 
+
8165
 
+               local $^W;
8166
 
+               require "$self->{path}/$self->{dispatch}.pm";
8167
 
+               File::Path::mkpath("$self->{prefix}/$self->{author}");
8168
 
+               $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self );
8169
 
+               $self->{admin}->init;
8170
 
+               @_ = ($class, _self => $self);
8171
 
+               goto &{"$self->{name}::import"};
8172
 
+       }
8173
 
+
8174
 
+       local $^W;
8175
 
+       *{"${who}::AUTOLOAD"} = $self->autoload;
8176
 
+       $self->preload;
8177
 
+
8178
 
+       # Unregister loader and worker packages so subdirs can use them again
8179
 
+       delete $INC{'inc/Module/Install.pm'};
8180
 
+       delete $INC{'Module/Install.pm'};
8181
 
+
8182
 
+       # Save to the singleton
8183
 
+       $MAIN = $self;
8184
 
+
8185
 
+       return 1;
8186
 
+}
8187
 
+
8188
 
+sub autoload {
8189
 
+       my $self = shift;
8190
 
+       my $who  = $self->_caller;
8191
 
+       my $cwd  = Cwd::cwd();
8192
 
+       my $sym  = "${who}::AUTOLOAD";
8193
 
+       $sym->{$cwd} = sub {
8194
 
+               my $pwd = Cwd::cwd();
8195
 
+               if ( my $code = $sym->{$pwd} ) {
8196
 
+                       # Delegate back to parent dirs
8197
 
+                       goto &$code unless $cwd eq $pwd;
8198
 
+               }
8199
 
+               unless ($$sym =~ s/([^:]+)$//) {
8200
 
+                       # XXX: it looks like we can't retrieve the missing function
8201
 
+                       # via $$sym (usually $main::AUTOLOAD) in this case.
8202
 
+                       # I'm still wondering if we should slurp Makefile.PL to
8203
 
+                       # get some context or not ...
8204
 
+                       my ($package, $file, $line) = caller;
8205
 
+                       die <<"EOT";
8206
 
+Unknown function is found at $file line $line.
8207
 
+Execution of $file aborted due to runtime errors.
8208
 
+
8209
 
+If you're a contributor to a project, you may need to install
8210
 
+some Module::Install extensions from CPAN (or other repository).
8211
 
+If you're a user of a module, please contact the author.
8212
 
+EOT
8213
 
+               }
8214
 
+               my $method = $1;
8215
 
+               if ( uc($method) eq $method ) {
8216
 
+                       # Do nothing
8217
 
+                       return;
8218
 
+               } elsif ( $method =~ /^_/ and $self->can($method) ) {
8219
 
+                       # Dispatch to the root M:I class
8220
 
+                       return $self->$method(@_);
8221
 
+               }
8222
 
+
8223
 
+               # Dispatch to the appropriate plugin
8224
 
+               unshift @_, ( $self, $1 );
8225
 
+               goto &{$self->can('call')};
8226
 
+       };
8227
 
+}
8228
 
+
8229
 
+sub preload {
8230
 
+       my $self = shift;
8231
 
+       unless ( $self->{extensions} ) {
8232
 
+               $self->load_extensions(
8233
 
+                       "$self->{prefix}/$self->{path}", $self
8234
 
+               );
8235
 
+       }
8236
 
+
8237
 
+       my @exts = @{$self->{extensions}};
8238
 
+       unless ( @exts ) {
8239
 
+               @exts = $self->{admin}->load_all_extensions;
8240
 
+       }
8241
 
+
8242
 
+       my %seen;
8243
 
+       foreach my $obj ( @exts ) {
8244
 
+               while (my ($method, $glob) = each %{ref($obj) . '::'}) {
8245
 
+                       next unless $obj->can($method);
8246
 
+                       next if $method =~ /^_/;
8247
 
+                       next if $method eq uc($method);
8248
 
+                       $seen{$method}++;
8249
 
+               }
8250
 
+       }
8251
 
+
8252
 
+       my $who = $self->_caller;
8253
 
+       foreach my $name ( sort keys %seen ) {
8254
 
+               local $^W;
8255
 
+               *{"${who}::$name"} = sub {
8256
 
+                       ${"${who}::AUTOLOAD"} = "${who}::$name";
8257
 
+                       goto &{"${who}::AUTOLOAD"};
8258
 
+               };
8259
 
+       }
8260
 
+}
8261
 
+
8262
 
+sub new {
8263
 
+       my ($class, %args) = @_;
8264
 
+
8265
 
+    delete $INC{'FindBin.pm'};
8266
 
+    require FindBin;
8267
 
+
8268
 
+       # ignore the prefix on extension modules built from top level.
8269
 
+       my $base_path = Cwd::abs_path($FindBin::Bin);
8270
 
+       unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) {
8271
 
+               delete $args{prefix};
8272
 
+       }
8273
 
+       return $args{_self} if $args{_self};
8274
 
+
8275
 
+       $args{dispatch} ||= 'Admin';
8276
 
+       $args{prefix}   ||= 'inc';
8277
 
+       $args{author}   ||= ($^O eq 'VMS' ? '_author' : '.author');
8278
 
+       $args{bundle}   ||= 'inc/BUNDLES';
8279
 
+       $args{base}     ||= $base_path;
8280
 
+       $class =~ s/^\Q$args{prefix}\E:://;
8281
 
+       $args{name}     ||= $class;
8282
 
+       $args{version}  ||= $class->VERSION;
8283
 
+       unless ( $args{path} ) {
8284
 
+               $args{path}  = $args{name};
8285
 
+               $args{path}  =~ s!::!/!g;
8286
 
+       }
8287
 
+       $args{file}     ||= "$args{base}/$args{prefix}/$args{path}.pm";
8288
 
+       $args{wrote}      = 0;
8289
 
+
8290
 
+       bless( \%args, $class );
8291
 
+}
8292
 
+
8293
 
+sub call {
8294
 
+       my ($self, $method) = @_;
8295
 
+       my $obj = $self->load($method) or return;
8296
 
+        splice(@_, 0, 2, $obj);
8297
 
+       goto &{$obj->can($method)};
8298
 
+}
8299
 
+
8300
 
+sub load {
8301
 
+       my ($self, $method) = @_;
8302
 
+
8303
 
+       $self->load_extensions(
8304
 
+               "$self->{prefix}/$self->{path}", $self
8305
 
+       ) unless $self->{extensions};
8306
 
+
8307
 
+       foreach my $obj (@{$self->{extensions}}) {
8308
 
+               return $obj if $obj->can($method);
8309
 
+       }
8310
 
+
8311
 
+       my $admin = $self->{admin} or die <<"END_DIE";
8312
 
+The '$method' method does not exist in the '$self->{prefix}' path!
8313
 
+Please remove the '$self->{prefix}' directory and run $0 again to load it.
8314
 
+END_DIE
8315
 
+
8316
 
+       my $obj = $admin->load($method, 1);
8317
 
+       push @{$self->{extensions}}, $obj;
8318
 
+
8319
 
+       $obj;
8320
 
+}
8321
 
+
8322
 
+sub load_extensions {
8323
 
+       my ($self, $path, $top) = @_;
8324
 
+
8325
 
+       my $should_reload = 0;
8326
 
+       unless ( grep { ! ref $_ and lc $_ eq lc $self->{prefix} } @INC ) {
8327
 
+               unshift @INC, $self->{prefix};
8328
 
+               $should_reload = 1;
8329
 
+       }
8330
 
+
8331
 
+       foreach my $rv ( $self->find_extensions($path) ) {
8332
 
+               my ($file, $pkg) = @{$rv};
8333
 
+               next if $self->{pathnames}{$pkg};
8334
 
+
8335
 
+               local $@;
8336
 
+               my $new = eval { local $^W; require $file; $pkg->can('new') };
8337
 
+               unless ( $new ) {
8338
 
+                       warn $@ if $@;
8339
 
+                       next;
8340
 
+               }
8341
 
+               $self->{pathnames}{$pkg} =
8342
 
+                       $should_reload ? delete $INC{$file} : $INC{$file};
8343
 
+               push @{$self->{extensions}}, &{$new}($pkg, _top => $top );
8344
 
+       }
8345
 
+
8346
 
+       $self->{extensions} ||= [];
8347
 
+}
8348
 
+
8349
 
+sub find_extensions {
8350
 
+       my ($self, $path) = @_;
8351
 
+
8352
 
+       my @found;
8353
 
+       File::Find::find( sub {
8354
 
+               my $file = $File::Find::name;
8355
 
+               return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is;
8356
 
+               my $subpath = $1;
8357
 
+               return if lc($subpath) eq lc($self->{dispatch});
8358
 
+
8359
 
+               $file = "$self->{path}/$subpath.pm";
8360
 
+               my $pkg = "$self->{name}::$subpath";
8361
 
+               $pkg =~ s!/!::!g;
8362
 
+
8363
 
+               # If we have a mixed-case package name, assume case has been preserved
8364
 
+               # correctly.  Otherwise, root through the file to locate the case-preserved
8365
 
+               # version of the package name.
8366
 
+               if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) {
8367
 
+                       my $content = Module::Install::_read($subpath . '.pm');
8368
 
+                       my $in_pod  = 0;
8369
 
+                       foreach ( split //, $content ) {
8370
 
+                               $in_pod = 1 if /^=\w/;
8371
 
+                               $in_pod = 0 if /^=cut/;
8372
 
+                               next if ($in_pod || /^=cut/);  # skip pod text
8373
 
+                               next if /^\s*#/;               # and comments
8374
 
+                               if ( m/^\s*package\s+($pkg)\s*;/i ) {
8375
 
+                                       $pkg = $1;
8376
 
+                                       last;
8377
 
+                               }
8378
 
+                       }
8379
 
+               }
8380
 
+
8381
 
+               push @found, [ $file, $pkg ];
8382
 
+       }, $path ) if -d $path;
8383
 
+
8384
 
+       @found;
8385
 
+}
8386
 
+
8387
 
+
8388
 
+
8389
 
+
8390
 
+
8391
 
+#####################################################################
8392
 
+# Common Utility Functions
8393
 
+
8394
 
+sub _caller {
8395
 
+       my $depth = 0;
8396
 
+       my $call  = caller($depth);
8397
 
+       while ( $call eq __PACKAGE__ ) {
8398
 
+               $depth++;
8399
 
+               $call = caller($depth);
8400
 
+       }
8401
 
+       return $call;
8402
 
+}
8403
 
+
8404
 
+# Done in evals to avoid confusing Perl::MinimumVersion
8405
 
+eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@;
8406
 
+sub _read {
8407
 
+       local *FH;
8408
 
+       open( FH, '<', $_[0] ) or die "open($_[0]): $!";
8409
 
+       my $string = do { local $/; <FH> };
8410
 
+       close FH or die "close($_[0]): $!";
8411
 
+       return $string;
8412
 
+}
8413
 
+END_NEW
8414
 
+sub _read {
8415
 
+       local *FH;
8416
 
+       open( FH, "< $_[0]"  ) or die "open($_[0]): $!";
8417
 
+       my $string = do { local $/; <FH> };
8418
 
+       close FH or die "close($_[0]): $!";
8419
 
+       return $string;
8420
 
+}
8421
 
+END_OLD
8422
 
+
8423
 
+sub _readperl {
8424
 
+       my $string = Module::Install::_read($_[0]);
8425
 
+       $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg;
8426
 
+       $string =~ s/(\n)\n*__(?:DATA|END)__\b.*\z/$1/s;
8427
 
+       $string =~ s/\n\n=\w+.+?\n\n=cut\b.+?\n+/\n\n/sg;
8428
 
+       return $string;
8429
 
+}
8430
 
+
8431
 
+sub _readpod {
8432
 
+       my $string = Module::Install::_read($_[0]);
8433
 
+       $string =~ s/(?:\015{1,2}\012|\015|\012)/\n/sg;
8434
 
+       return $string if $_[0] =~ /\.pod\z/;
8435
 
+       $string =~ s/(^|\n=cut\b.+?\n+)[^=\s].+?\n(\n=\w+|\z)/$1$2/sg;
8436
 
+       $string =~ s/\n*=pod\b[^\n]*\n+/\n\n/sg;
8437
 
+       $string =~ s/\n*=cut\b[^\n]*\n+/\n\n/sg;
8438
 
+       $string =~ s/^\n+//s;
8439
 
+       return $string;
8440
 
+}
8441
 
+
8442
 
+# Done in evals to avoid confusing Perl::MinimumVersion
8443
 
+eval( $] >= 5.006 ? <<'END_NEW' : <<'END_OLD' ); die $@ if $@;
8444
 
+sub _write {
8445
 
+       local *FH;
8446
 
+       open( FH, '>', $_[0] ) or die "open($_[0]): $!";
8447
 
+       foreach ( 1 .. $#_ ) {
8448
 
+               print FH $_[$_] or die "print($_[0]): $!";
8449
 
+       }
8450
 
+       close FH or die "close($_[0]): $!";
8451
 
+}
8452
 
+END_NEW
8453
 
+sub _write {
8454
 
+       local *FH;
8455
 
+       open( FH, "> $_[0]"  ) or die "open($_[0]): $!";
8456
 
+       foreach ( 1 .. $#_ ) {
8457
 
+               print FH $_[$_] or die "print($_[0]): $!";
8458
 
+       }
8459
 
+       close FH or die "close($_[0]): $!";
8460
 
+}
8461
 
+END_OLD
8462
 
+
8463
 
+# _version is for processing module versions (eg, 1.03_05) not
8464
 
+# Perl versions (eg, 5.8.1).
8465
 
+sub _version ($) {
8466
 
+       my $s = shift || 0;
8467
 
+       my $d =()= $s =~ /(\.)/g;
8468
 
+       if ( $d >= 2 ) {
8469
 
+               # Normalise multipart versions
8470
 
+               $s =~ s/(\.)(\d{1,3})/sprintf("$1%03d",$2)/eg;
8471
 
+       }
8472
 
+       $s =~ s/^(\d+)\.?//;
8473
 
+       my $l = $1 || 0;
8474
 
+       my @v = map {
8475
 
+               $_ . '0' x (3 - length $_)
8476
 
+       } $s =~ /(\d{1,3})\D?/g;
8477
 
+       $l = $l . '.' . join '', @v if @v;
8478
 
+       return $l + 0;
8479
 
+}
8480
 
+
8481
 
+sub _cmp ($$) {
8482
 
+       _version($_[0]) <=> _version($_[1]);
8483
 
+}
8484
 
+
8485
 
+# Cloned from Params::Util::_CLASS
8486
 
+sub _CLASS ($) {
8487
 
+       (
8488
 
+               defined $_[0]
8489
 
+               and
8490
 
+               ! ref $_[0]
8491
 
+               and
8492
 
+               $_[0] =~ m/^[^\W\d]\w*(?:::\w+)*\z/s
8493
 
+       ) ? $_[0] : undef;
8494
 
+}
8495
 
+
8496
 
+1;
8497
 
+
8498
 
+# Copyright 2008 - 2010 Adam Kennedy.
8499
 
Index: 0.8/modules/nginx-echo/test/inc/Spiffy.pm
8500
 
===================================================================
8501
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
8502
 
+++ 0.8/modules/nginx-echo/test/inc/Spiffy.pm   2010-10-31 07:35:47.698338000 +0000
8503
 
@@ -0,0 +1,539 @@
8504
 
+#line 1
8505
 
+package Spiffy;
8506
 
+use strict;
8507
 
+use 5.006001;
8508
 
+use warnings;
8509
 
+use Carp;
8510
 
+require Exporter;
8511
 
+our $VERSION = '0.30';
8512
 
+our @EXPORT = ();
8513
 
+our @EXPORT_BASE = qw(field const stub super);
8514
 
+our @EXPORT_OK = (@EXPORT_BASE, qw(id WWW XXX YYY ZZZ));
8515
 
+our %EXPORT_TAGS = (XXX => [qw(WWW XXX YYY ZZZ)]);
8516
 
+
8517
 
+my $stack_frame = 0;
8518
 
+my $dump = 'yaml';
8519
 
+my $bases_map = {};
8520
 
+
8521
 
+sub WWW; sub XXX; sub YYY; sub ZZZ;
8522
 
+
8523
 
+# This line is here to convince "autouse" into believing we are autousable.
8524
 
+sub can {
8525
 
+    ($_[1] eq 'import' and caller()->isa('autouse'))
8526
 
+        ? \&Exporter::import        # pacify autouse's equality test
8527
 
+        : $_[0]->SUPER::can($_[1])  # normal case
8528
 
+}
8529
 
+
8530
 
+# TODO
8531
 
+#
8532
 
+# Exported functions like field and super should be hidden so as not to
8533
 
+# be confused with methods that can be inherited.
8534
 
+#
8535
 
+
8536
 
+sub new {
8537
 
+    my $class = shift;
8538
 
+    $class = ref($class) || $class;
8539
 
+    my $self = bless {}, $class;
8540
 
+    while (@_) {
8541
 
+        my $method = shift;
8542
 
+        $self->$method(shift);
8543
 
+    }
8544
 
+    return $self;
8545
 
+}
8546
 
+
8547
 
+my $filtered_files = {};
8548
 
+my $filter_dump = 0;
8549
 
+my $filter_save = 0;
8550
 
+our $filter_result = '';
8551
 
+sub import {
8552
 
+    no strict 'refs';
8553
 
+    no warnings;
8554
 
+    my $self_package = shift;
8555
 
+
8556
 
+    # XXX Using parse_arguments here might cause confusion, because the
8557
 
+    # subclass's boolean_arguments and paired_arguments can conflict, causing
8558
 
+    # difficult debugging. Consider using something truly local.
8559
 
+    my ($args, @export_list) = do {
8560
 
+        local *boolean_arguments = sub {
8561
 
+            qw(
8562
 
+                -base -Base -mixin -selfless
8563
 
+                -XXX -dumper -yaml
8564
 
+                -filter_dump -filter_save
8565
 
+            )
8566
 
+        };
8567
 
+        local *paired_arguments = sub { qw(-package) };
8568
 
+        $self_package->parse_arguments(@_);
8569
 
+    };
8570
 
+    return spiffy_mixin_import(scalar(caller(0)), $self_package, @export_list)
8571
 
+      if $args->{-mixin};
8572
 
+
8573
 
+    $filter_dump = 1 if $args->{-filter_dump};
8574
 
+    $filter_save = 1 if $args->{-filter_save};
8575
 
+    $dump = 'yaml' if $args->{-yaml};
8576
 
+    $dump = 'dumper' if $args->{-dumper};
8577
 
+
8578
 
+    local @EXPORT_BASE = @EXPORT_BASE;
8579
 
+
8580
 
+    if ($args->{-XXX}) {
8581
 
+        push @EXPORT_BASE, @{$EXPORT_TAGS{XXX}}
8582
 
+          unless grep /^XXX$/, @EXPORT_BASE;
8583
 
+    }
8584
 
+
8585
 
+    spiffy_filter()
8586
 
+      if ($args->{-selfless} or $args->{-Base}) and
8587
 
+         not $filtered_files->{(caller($stack_frame))[1]}++;
8588
 
+
8589
 
+    my $caller_package = $args->{-package} || caller($stack_frame);
8590
 
+    push @{"$caller_package\::ISA"}, $self_package
8591
 
+      if $args->{-Base} or $args->{-base};
8592
 
+
8593
 
+    for my $class (@{all_my_bases($self_package)}) {
8594
 
+        next unless $class->isa('Spiffy');
8595
 
+        my @export = grep {
8596
 
+            not defined &{"$caller_package\::$_"};
8597
 
+        } ( @{"$class\::EXPORT"},
8598
 
+            ($args->{-Base} or $args->{-base})
8599
 
+              ? @{"$class\::EXPORT_BASE"} : (),
8600
 
+          );
8601
 
+        my @export_ok = grep {
8602
 
+            not defined &{"$caller_package\::$_"};
8603
 
+        } @{"$class\::EXPORT_OK"};
8604
 
+
8605
 
+        # Avoid calling the expensive Exporter::export
8606
 
+        # if there is nothing to do (optimization)
8607
 
+        my %exportable = map { ($_, 1) } @export, @export_ok;
8608
 
+        next unless keys %exportable;
8609
 
+
8610
 
+        my @export_save = @{"$class\::EXPORT"};
8611
 
+        my @export_ok_save = @{"$class\::EXPORT_OK"};
8612
 
+        @{"$class\::EXPORT"} = @export;
8613
 
+        @{"$class\::EXPORT_OK"} = @export_ok;
8614
 
+        my @list = grep {
8615
 
+            (my $v = $_) =~ s/^[\!\:]//;
8616
 
+            $exportable{$v} or ${"$class\::EXPORT_TAGS"}{$v};
8617
 
+        } @export_list;
8618
 
+        Exporter::export($class, $caller_package, @list);
8619
 
+        @{"$class\::EXPORT"} = @export_save;
8620
 
+        @{"$class\::EXPORT_OK"} = @export_ok_save;
8621
 
+    }
8622
 
+}
8623
 
+
8624
 
+sub spiffy_filter {
8625
 
+    require Filter::Util::Call;
8626
 
+    my $done = 0;
8627
 
+    Filter::Util::Call::filter_add(
8628
 
+        sub {
8629
 
+            return 0 if $done;
8630
 
+            my ($data, $end) = ('', '');
8631
 
+            while (my $status = Filter::Util::Call::filter_read()) {
8632
 
+                return $status if $status < 0;
8633
 
+                if (/^__(?:END|DATA)__\r?$/) {
8634
 
+                    $end = $_;
8635
 
+                    last;
8636
 
+                }
8637
 
+                $data .= $_;
8638
 
+                $_ = '';
8639
 
+            }
8640
 
+            $_ = $data;
8641
 
+            my @my_subs;
8642
 
+            s[^(sub\s+\w+\s+\{)(.*\n)]
8643
 
+             [${1}my \$self = shift;$2]gm;
8644
 
+            s[^(sub\s+\w+)\s*\(\s*\)(\s+\{.*\n)]
8645
 
+             [${1}${2}]gm;
8646
 
+            s[^my\s+sub\s+(\w+)(\s+\{)(.*)((?s:.*?\n))\}\n]
8647
 
+             [push @my_subs, $1; "\$$1 = sub$2my \$self = shift;$3$4\};\n"]gem;
8648
 
+            my $preclare = '';
8649
 
+            if (@my_subs) {
8650
 
+                $preclare = join ',', map "\$$_", @my_subs;
8651
 
+                $preclare = "my($preclare);";
8652
 
+            }
8653
 
+            $_ = "use strict;use warnings;$preclare${_};1;\n$end";
8654
 
+            if ($filter_dump) { print; exit }
8655
 
+            if ($filter_save) { $filter_result = $_; $_ = $filter_result; }
8656
 
+            $done = 1;
8657
 
+        }
8658
 
+    );
8659
 
+}
8660
 
+
8661
 
+sub base {
8662
 
+    push @_, -base;
8663
 
+    goto &import;
8664
 
+}
8665
 
+
8666
 
+sub all_my_bases {
8667
 
+    my $class = shift;
8668
 
+
8669
 
+    return $bases_map->{$class}
8670
 
+      if defined $bases_map->{$class};
8671
 
+
8672
 
+    my @bases = ($class);
8673
 
+    no strict 'refs';
8674
 
+    for my $base_class (@{"${class}::ISA"}) {
8675
 
+        push @bases, @{all_my_bases($base_class)};
8676
 
+    }
8677
 
+    my $used = {};
8678
 
+    $bases_map->{$class} = [grep {not $used->{$_}++} @bases];
8679
 
+}
8680
 
+
8681
 
+my %code = (
8682
 
+    sub_start =>
8683
 
+      "sub {\n",
8684
 
+    set_default =>
8685
 
+      "  \$_[0]->{%s} = %s\n    unless exists \$_[0]->{%s};\n",
8686
 
+    init =>
8687
 
+      "  return \$_[0]->{%s} = do { my \$self = \$_[0]; %s }\n" .
8688
 
+      "    unless \$#_ > 0 or defined \$_[0]->{%s};\n",
8689
 
+    weak_init =>
8690
 
+      "  return do {\n" .
8691
 
+      "    \$_[0]->{%s} = do { my \$self = \$_[0]; %s };\n" .
8692
 
+      "    Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n" .
8693
 
+      "    \$_[0]->{%s};\n" .
8694
 
+      "  } unless \$#_ > 0 or defined \$_[0]->{%s};\n",
8695
 
+    return_if_get =>
8696
 
+      "  return \$_[0]->{%s} unless \$#_ > 0;\n",
8697
 
+    set =>
8698
 
+      "  \$_[0]->{%s} = \$_[1];\n",
8699
 
+    weaken =>
8700
 
+      "  Scalar::Util::weaken(\$_[0]->{%s}) if ref \$_[0]->{%s};\n",
8701
 
+    sub_end =>
8702
 
+      "  return \$_[0]->{%s};\n}\n",
8703
 
+);
8704
 
+
8705
 
+sub field {
8706
 
+    my $package = caller;
8707
 
+    my ($args, @values) = do {
8708
 
+        no warnings;
8709
 
+        local *boolean_arguments = sub { (qw(-weak)) };
8710
 
+        local *paired_arguments = sub { (qw(-package -init)) };
8711
 
+        Spiffy->parse_arguments(@_);
8712
 
+    };
8713
 
+    my ($field, $default) = @values;
8714
 
+    $package = $args->{-package} if defined $args->{-package};
8715
 
+    die "Cannot have a default for a weakened field ($field)"
8716
 
+        if defined $default && $args->{-weak};
8717
 
+    return if defined &{"${package}::$field"};
8718
 
+    require Scalar::Util if $args->{-weak};
8719
 
+    my $default_string =
8720
 
+        ( ref($default) eq 'ARRAY' and not @$default )
8721
 
+        ? '[]'
8722
 
+        : (ref($default) eq 'HASH' and not keys %$default )
8723
 
+          ? '{}'
8724
 
+          : default_as_code($default);
8725
 
+
8726
 
+    my $code = $code{sub_start};
8727
 
+    if ($args->{-init}) {
8728
 
+        my $fragment = $args->{-weak} ? $code{weak_init} : $code{init};
8729
 
+        $code .= sprintf $fragment, $field, $args->{-init}, ($field) x 4;
8730
 
+    }
8731
 
+    $code .= sprintf $code{set_default}, $field, $default_string, $field
8732
 
+      if defined $default;
8733
 
+    $code .= sprintf $code{return_if_get}, $field;
8734
 
+    $code .= sprintf $code{set}, $field;
8735
 
+    $code .= sprintf $code{weaken}, $field, $field
8736
 
+      if $args->{-weak};
8737
 
+    $code .= sprintf $code{sub_end}, $field;
8738
 
+
8739
 
+    my $sub = eval $code;
8740
 
+    die $@ if $@;
8741
 
+    no strict 'refs';
8742
 
+    *{"${package}::$field"} = $sub;
8743
 
+    return $code if defined wantarray;
8744
 
+}
8745
 
+
8746
 
+sub default_as_code {
8747
 
+    require Data::Dumper;
8748
 
+    local $Data::Dumper::Sortkeys = 1;
8749
 
+    my $code = Data::Dumper::Dumper(shift);
8750
 
+    $code =~ s/^\$VAR1 = //;
8751
 
+    $code =~ s/;$//;
8752
 
+    return $code;
8753
 
+}
8754
 
+
8755
 
+sub const {
8756
 
+    my $package = caller;
8757
 
+    my ($args, @values) = do {
8758
 
+        no warnings;
8759
 
+        local *paired_arguments = sub { (qw(-package)) };
8760
 
+        Spiffy->parse_arguments(@_);
8761
 
+    };
8762
 
+    my ($field, $default) = @values;
8763
 
+    $package = $args->{-package} if defined $args->{-package};
8764
 
+    no strict 'refs';
8765
 
+    return if defined &{"${package}::$field"};
8766
 
+    *{"${package}::$field"} = sub { $default }
8767
 
+}
8768
 
+
8769
 
+sub stub {
8770
 
+    my $package = caller;
8771
 
+    my ($args, @values) = do {
8772
 
+        no warnings;
8773
 
+        local *paired_arguments = sub { (qw(-package)) };
8774
 
+        Spiffy->parse_arguments(@_);
8775
 
+    };
8776
 
+    my ($field, $default) = @values;
8777
 
+    $package = $args->{-package} if defined $args->{-package};
8778
 
+    no strict 'refs';
8779
 
+    return if defined &{"${package}::$field"};
8780
 
+    *{"${package}::$field"} =
8781
 
+    sub {
8782
 
+        require Carp;
8783
 
+        Carp::confess
8784
 
+          "Method $field in package $package must be subclassed";
8785
 
+    }
8786
 
+}
8787
 
+
8788
 
+sub parse_arguments {
8789
 
+    my $class = shift;
8790
 
+    my ($args, @values) = ({}, ());
8791
 
+    my %booleans = map { ($_, 1) } $class->boolean_arguments;
8792
 
+    my %pairs = map { ($_, 1) } $class->paired_arguments;
8793
 
+    while (@_) {
8794
 
+        my $elem = shift;
8795
 
+        if (defined $elem and defined $booleans{$elem}) {
8796
 
+            $args->{$elem} = (@_ and $_[0] =~ /^[01]$/)
8797
 
+            ? shift
8798
 
+            : 1;
8799
 
+        }
8800
 
+        elsif (defined $elem and defined $pairs{$elem} and @_) {
8801
 
+            $args->{$elem} = shift;
8802
 
+        }
8803
 
+        else {
8804
 
+            push @values, $elem;
8805
 
+        }
8806
 
+    }
8807
 
+    return wantarray ? ($args, @values) : $args;
8808
 
+}
8809
 
+
8810
 
+sub boolean_arguments { () }
8811
 
+sub paired_arguments { () }
8812
 
+
8813
 
+# get a unique id for any node
8814
 
+sub id {
8815
 
+    if (not ref $_[0]) {
8816
 
+        return 'undef' if not defined $_[0];
8817
 
+        \$_[0] =~ /\((\w+)\)$/o or die;
8818
 
+        return "$1-S";
8819
 
+    }
8820
 
+    require overload;
8821
 
+    overload::StrVal($_[0]) =~ /\((\w+)\)$/o or die;
8822
 
+    return $1;
8823
 
+}
8824
 
+
8825
 
+#===============================================================================
8826
 
+# It's super, man.
8827
 
+#===============================================================================
8828
 
+package DB;
8829
 
+{
8830
 
+    no warnings 'redefine';
8831
 
+    sub super_args {
8832
 
+        my @dummy = caller(@_ ? $_[0] : 2);
8833
 
+        return @DB::args;
8834
 
+    }
8835
 
+}
8836
 
+
8837
 
+package Spiffy;
8838
 
+sub super {
8839
 
+    my $method;
8840
 
+    my $frame = 1;
8841
 
+    while ($method = (caller($frame++))[3]) {
8842
 
+        $method =~ s/.*::// and last;
8843
 
+    }
8844
 
+    my @args = DB::super_args($frame);
8845
 
+    @_ = @_ ? ($args[0], @_) : @args;
8846
 
+    my $class = ref $_[0] ? ref $_[0] : $_[0];
8847
 
+    my $caller_class = caller;
8848
 
+    my $seen = 0;
8849
 
+    my @super_classes = reverse grep {
8850
 
+        ($seen or $seen = ($_ eq $caller_class)) ? 0 : 1;
8851
 
+    } reverse @{all_my_bases($class)};
8852
 
+    for my $super_class (@super_classes) {
8853
 
+        no strict 'refs';
8854
 
+        next if $super_class eq $class;
8855
 
+        if (defined &{"${super_class}::$method"}) {
8856
 
+            ${"$super_class\::AUTOLOAD"} = ${"$class\::AUTOLOAD"}
8857
 
+              if $method eq 'AUTOLOAD';
8858
 
+            return &{"${super_class}::$method"};
8859
 
+        }
8860
 
+    }
8861
 
+    return;
8862
 
+}
8863
 
+
8864
 
+#===============================================================================
8865
 
+# This code deserves a spanking, because it is being very naughty.
8866
 
+# It is exchanging base.pm's import() for its own, so that people
8867
 
+# can use base.pm with Spiffy modules, without being the wiser.
8868
 
+#===============================================================================
8869
 
+my $real_base_import;
8870
 
+my $real_mixin_import;
8871
 
+
8872
 
+BEGIN {
8873
 
+    require base unless defined $INC{'base.pm'};
8874
 
+    $INC{'mixin.pm'} ||= 'Spiffy/mixin.pm';
8875
 
+    $real_base_import = \&base::import;
8876
 
+    $real_mixin_import = \&mixin::import;
8877
 
+    no warnings;
8878
 
+    *base::import = \&spiffy_base_import;
8879
 
+    *mixin::import = \&spiffy_mixin_import;
8880
 
+}
8881
 
+
8882
 
+# my $i = 0;
8883
 
+# while (my $caller = caller($i++)) {
8884
 
+#     next unless $caller eq 'base' or $caller eq 'mixin';
8885
 
+#     croak <<END;
8886
 
+# Spiffy.pm must be loaded before calling 'use base' or 'use mixin' with a
8887
 
+# Spiffy module. See the documentation of Spiffy.pm for details.
8888
 
+# END
8889
 
+# }
8890
 
+
8891
 
+sub spiffy_base_import {
8892
 
+    my @base_classes = @_;
8893
 
+    shift @base_classes;
8894
 
+    no strict 'refs';
8895
 
+    goto &$real_base_import
8896
 
+      unless grep {
8897
 
+          eval "require $_" unless %{"$_\::"};
8898
 
+          $_->isa('Spiffy');
8899
 
+      } @base_classes;
8900
 
+    my $inheritor = caller(0);
8901
 
+    for my $base_class (@base_classes) {
8902
 
+        next if $inheritor->isa($base_class);
8903
 
+        croak "Can't mix Spiffy and non-Spiffy classes in 'use base'.\n",
8904
 
+              "See the documentation of Spiffy.pm for details\n  "
8905
 
+          unless $base_class->isa('Spiffy');
8906
 
+        $stack_frame = 1; # tell import to use different caller
8907
 
+        import($base_class, '-base');
8908
 
+        $stack_frame = 0;
8909
 
+    }
8910
 
+}
8911
 
+
8912
 
+sub mixin {
8913
 
+    my $self = shift;
8914
 
+    my $target_class = ref($self);
8915
 
+    spiffy_mixin_import($target_class, @_)
8916
 
+}
8917
 
+
8918
 
+sub spiffy_mixin_import {
8919
 
+    my $target_class = shift;
8920
 
+    $target_class = caller(0)
8921
 
+      if $target_class eq 'mixin';
8922
 
+    my $mixin_class = shift
8923
 
+      or die "Nothing to mixin";
8924
 
+    eval "require $mixin_class";
8925
 
+    my @roles = @_;
8926
 
+    my $pseudo_class = join '-', $target_class, $mixin_class, @roles;
8927
 
+    my %methods = spiffy_mixin_methods($mixin_class, @roles);
8928
 
+    no strict 'refs';
8929
 
+    no warnings;
8930
 
+    @{"$pseudo_class\::ISA"} = @{"$target_class\::ISA"};
8931
 
+    @{"$target_class\::ISA"} = ($pseudo_class);
8932
 
+    for (keys %methods) {
8933
 
+        *{"$pseudo_class\::$_"} = $methods{$_};
8934
 
+    }
8935
 
+}
8936
 
+
8937
 
+sub spiffy_mixin_methods {
8938
 
+    my $mixin_class = shift;
8939
 
+    no strict 'refs';
8940
 
+    my %methods = spiffy_all_methods($mixin_class);
8941
 
+    map {
8942
 
+        $methods{$_}
8943
 
+          ? ($_, \ &{"$methods{$_}\::$_"})
8944
 
+          : ($_, \ &{"$mixin_class\::$_"})
8945
 
+    } @_
8946
 
+      ? (get_roles($mixin_class, @_))
8947
 
+      : (keys %methods);
8948
 
+}
8949
 
+
8950
 
+sub get_roles {
8951
 
+    my $mixin_class = shift;
8952
 
+    my @roles = @_;
8953
 
+    while (grep /^!*:/, @roles) {
8954
 
+        @roles = map {
8955
 
+            s/!!//g;
8956
 
+            /^!:(.*)/ ? do {
8957
 
+                my $m = "_role_$1";
8958
 
+                map("!$_", $mixin_class->$m);
8959
 
+            } :
8960
 
+            /^:(.*)/ ? do {
8961
 
+                my $m = "_role_$1";
8962
 
+                ($mixin_class->$m);
8963
 
+            } :
8964
 
+            ($_)
8965
 
+        } @roles;
8966
 
+    }
8967
 
+    if (@roles and $roles[0] =~ /^!/) {
8968
 
+        my %methods = spiffy_all_methods($mixin_class);
8969
 
+        unshift @roles, keys(%methods);
8970
 
+    }
8971
 
+    my %roles;
8972
 
+    for (@roles) {
8973
 
+        s/!!//g;
8974
 
+        delete $roles{$1}, next
8975
 
+          if /^!(.*)/;
8976
 
+        $roles{$_} = 1;
8977
 
+    }
8978
 
+    keys %roles;
8979
 
+}
8980
 
+
8981
 
+sub spiffy_all_methods {
8982
 
+    no strict 'refs';
8983
 
+    my $class = shift;
8984
 
+    return if $class eq 'Spiffy';
8985
 
+    my %methods = map {
8986
 
+        ($_, $class)
8987
 
+    } grep {
8988
 
+        defined &{"$class\::$_"} and not /^_/
8989
 
+    } keys %{"$class\::"};
8990
 
+    my %super_methods;
8991
 
+    %super_methods = spiffy_all_methods(${"$class\::ISA"}[0])
8992
 
+      if @{"$class\::ISA"};
8993
 
+    %{{%super_methods, %methods}};
8994
 
+}
8995
 
+
8996
 
+
8997
 
+# END of naughty code.
8998
 
+#===============================================================================
8999
 
+# Debugging support
9000
 
+#===============================================================================
9001
 
+sub spiffy_dump {
9002
 
+    no warnings;
9003
 
+    if ($dump eq 'dumper') {
9004
 
+        require Data::Dumper;
9005
 
+        $Data::Dumper::Sortkeys = 1;
9006
 
+        $Data::Dumper::Indent = 1;
9007
 
+        return Data::Dumper::Dumper(@_);
9008
 
+    }
9009
 
+    require YAML;
9010
 
+    $YAML::UseVersion = 0;
9011
 
+    return YAML::Dump(@_) . "...\n";
9012
 
+}
9013
 
+
9014
 
+sub at_line_number {
9015
 
+    my ($file_path, $line_number) = (caller(1))[1,2];
9016
 
+    "  at $file_path line $line_number\n";
9017
 
+}
9018
 
+
9019
 
+sub WWW {
9020
 
+    warn spiffy_dump(@_) . at_line_number;
9021
 
+    return wantarray ? @_ : $_[0];
9022
 
+}
9023
 
+
9024
 
+sub XXX {
9025
 
+    die spiffy_dump(@_) . at_line_number;
9026
 
+}
9027
 
+
9028
 
+sub YYY {
9029
 
+    print spiffy_dump(@_) . at_line_number;
9030
 
+    return wantarray ? @_ : $_[0];
9031
 
+}
9032
 
+
9033
 
+sub ZZZ {
9034
 
+    require Carp;
9035
 
+    Carp::confess spiffy_dump(@_);
9036
 
+}
9037
 
+
9038
 
+1;
9039
 
+
9040
 
+__END__
9041
 
+
9042
 
+#line 1066
9043
 
Index: 0.8/modules/nginx-echo/test/inc/Test/Base.pm
9044
 
===================================================================
9045
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
9046
 
+++ 0.8/modules/nginx-echo/test/inc/Test/Base.pm        2010-10-31 07:35:47.678338000 +0000
9047
 
@@ -0,0 +1,684 @@
9048
 
+#line 1
9049
 
+# TODO:
9050
 
+#
9051
 
+package Test::Base;
9052
 
+use 5.006001;
9053
 
+use Spiffy 0.30 -Base;
9054
 
+use Spiffy ':XXX';
9055
 
+our $VERSION = '0.59';
9056
 
+
9057
 
+my @test_more_exports;
9058
 
+BEGIN {
9059
 
+    @test_more_exports = qw(
9060
 
+        ok isnt like unlike is_deeply cmp_ok
9061
 
+        skip todo_skip pass fail
9062
 
+        eq_array eq_hash eq_set
9063
 
+        plan can_ok isa_ok diag
9064
 
+        use_ok
9065
 
+        $TODO
9066
 
+    );
9067
 
+}
9068
 
+
9069
 
+use Test::More import => \@test_more_exports;
9070
 
+use Carp;
9071
 
+
9072
 
+our @EXPORT = (@test_more_exports, qw(
9073
 
+    is no_diff
9074
 
+
9075
 
+    blocks next_block first_block
9076
 
+    delimiters spec_file spec_string
9077
 
+    filters filters_delay filter_arguments
9078
 
+    run run_compare run_is run_is_deeply run_like run_unlike
9079
 
+    skip_all_unless_require is_deep run_is_deep
9080
 
+    WWW XXX YYY ZZZ
9081
 
+    tie_output no_diag_on_only
9082
 
+
9083
 
+    find_my_self default_object
9084
 
+
9085
 
+    croak carp cluck confess
9086
 
+));
9087
 
+
9088
 
+field '_spec_file';
9089
 
+field '_spec_string';
9090
 
+field _filters => [qw(norm trim)];
9091
 
+field _filters_map => {};
9092
 
+field spec =>
9093
 
+      -init => '$self->_spec_init';
9094
 
+field block_list =>
9095
 
+      -init => '$self->_block_list_init';
9096
 
+field _next_list => [];
9097
 
+field block_delim =>
9098
 
+      -init => '$self->block_delim_default';
9099
 
+field data_delim =>
9100
 
+      -init => '$self->data_delim_default';
9101
 
+field _filters_delay => 0;
9102
 
+field _no_diag_on_only => 0;
9103
 
+
9104
 
+field block_delim_default => '===';
9105
 
+field data_delim_default => '---';
9106
 
+
9107
 
+my $default_class;
9108
 
+my $default_object;
9109
 
+my $reserved_section_names = {};
9110
 
+
9111
 
+sub default_object {
9112
 
+    $default_object ||= $default_class->new;
9113
 
+    return $default_object;
9114
 
+}
9115
 
+
9116
 
+my $import_called = 0;
9117
 
+sub import() {
9118
 
+    $import_called = 1;
9119
 
+    my $class = (grep /^-base$/i, @_)
9120
 
+    ? scalar(caller)
9121
 
+    : $_[0];
9122
 
+    if (not defined $default_class) {
9123
 
+        $default_class = $class;
9124
 
+    }
9125
 
+#     else {
9126
 
+#         croak "Can't use $class after using $default_class"
9127
 
+#           unless $default_class->isa($class);
9128
 
+#     }
9129
 
+
9130
 
+    unless (grep /^-base$/i, @_) {
9131
 
+        my @args;
9132
 
+        for (my $ii = 1; $ii <= $#_; ++$ii) {
9133
 
+            if ($_[$ii] eq '-package') {
9134
 
+                ++$ii;
9135
 
+            } else {
9136
 
+                push @args, $_[$ii];
9137
 
+            }
9138
 
+        }
9139
 
+        Test::More->import(import => \@test_more_exports, @args)
9140
 
+            if @args;
9141
 
+     }
9142
 
+
9143
 
+    _strict_warnings();
9144
 
+    goto &Spiffy::import;
9145
 
+}
9146
 
+
9147
 
+# Wrap Test::Builder::plan
9148
 
+my $plan_code = \&Test::Builder::plan;
9149
 
+my $Have_Plan = 0;
9150
 
+{
9151
 
+    no warnings 'redefine';
9152
 
+    *Test::Builder::plan = sub {
9153
 
+        $Have_Plan = 1;
9154
 
+        goto &$plan_code;
9155
 
+    };
9156
 
+}
9157
 
+
9158
 
+my $DIED = 0;
9159
 
+$SIG{__DIE__} = sub { $DIED = 1; die @_ };
9160
 
+
9161
 
+sub block_class  { $self->find_class('Block') }
9162
 
+sub filter_class { $self->find_class('Filter') }
9163
 
+
9164
 
+sub find_class {
9165
 
+    my $suffix = shift;
9166
 
+    my $class = ref($self) . "::$suffix";
9167
 
+    return $class if $class->can('new');
9168
 
+    $class = __PACKAGE__ . "::$suffix";
9169
 
+    return $class if $class->can('new');
9170
 
+    eval "require $class";
9171
 
+    return $class if $class->can('new');
9172
 
+    die "Can't find a class for $suffix";
9173
 
+}
9174
 
+
9175
 
+sub check_late {
9176
 
+    if ($self->{block_list}) {
9177
 
+        my $caller = (caller(1))[3];
9178
 
+        $caller =~ s/.*:://;
9179
 
+        croak "Too late to call $caller()"
9180
 
+    }
9181
 
+}
9182
 
+
9183
 
+sub find_my_self() {
9184
 
+    my $self = ref($_[0]) eq $default_class
9185
 
+    ? splice(@_, 0, 1)
9186
 
+    : default_object();
9187
 
+    return $self, @_;
9188
 
+}
9189
 
+
9190
 
+sub blocks() {
9191
 
+    (my ($self), @_) = find_my_self(@_);
9192
 
+
9193
 
+    croak "Invalid arguments passed to 'blocks'"
9194
 
+      if @_ > 1;
9195
 
+    croak sprintf("'%s' is invalid argument to blocks()", shift(@_))
9196
 
+      if @_ && $_[0] !~ /^[a-zA-Z]\w*$/;
9197
 
+
9198
 
+    my $blocks = $self->block_list;
9199
 
+
9200
 
+    my $section_name = shift || '';
9201
 
+    my @blocks = $section_name
9202
 
+    ? (grep { exists $_->{$section_name} } @$blocks)
9203
 
+    : (@$blocks);
9204
 
+
9205
 
+    return scalar(@blocks) unless wantarray;
9206
 
+
9207
 
+    return (@blocks) if $self->_filters_delay;
9208
 
+
9209
 
+    for my $block (@blocks) {
9210
 
+        $block->run_filters
9211
 
+          unless $block->is_filtered;
9212
 
+    }
9213
 
+
9214
 
+    return (@blocks);
9215
 
+}
9216
 
+
9217
 
+sub next_block() {
9218
 
+    (my ($self), @_) = find_my_self(@_);
9219
 
+    my $list = $self->_next_list;
9220
 
+    if (@$list == 0) {
9221
 
+        $list = [@{$self->block_list}, undef];
9222
 
+        $self->_next_list($list);
9223
 
+    }
9224
 
+    my $block = shift @$list;
9225
 
+    if (defined $block and not $block->is_filtered) {
9226
 
+        $block->run_filters;
9227
 
+    }
9228
 
+    return $block;
9229
 
+}
9230
 
+
9231
 
+sub first_block() {
9232
 
+    (my ($self), @_) = find_my_self(@_);
9233
 
+    $self->_next_list([]);
9234
 
+    $self->next_block;
9235
 
+}
9236
 
+
9237
 
+sub filters_delay() {
9238
 
+    (my ($self), @_) = find_my_self(@_);
9239
 
+    $self->_filters_delay(defined $_[0] ? shift : 1);
9240
 
+}
9241
 
+
9242
 
+sub no_diag_on_only() {
9243
 
+    (my ($self), @_) = find_my_self(@_);
9244
 
+    $self->_no_diag_on_only(defined $_[0] ? shift : 1);
9245
 
+}
9246
 
+
9247
 
+sub delimiters() {
9248
 
+    (my ($self), @_) = find_my_self(@_);
9249
 
+    $self->check_late;
9250
 
+    my ($block_delimiter, $data_delimiter) = @_;
9251
 
+    $block_delimiter ||= $self->block_delim_default;
9252
 
+    $data_delimiter ||= $self->data_delim_default;
9253
 
+    $self->block_delim($block_delimiter);
9254
 
+    $self->data_delim($data_delimiter);
9255
 
+    return $self;
9256
 
+}
9257
 
+
9258
 
+sub spec_file() {
9259
 
+    (my ($self), @_) = find_my_self(@_);
9260
 
+    $self->check_late;
9261
 
+    $self->_spec_file(shift);
9262
 
+    return $self;
9263
 
+}
9264
 
+
9265
 
+sub spec_string() {
9266
 
+    (my ($self), @_) = find_my_self(@_);
9267
 
+    $self->check_late;
9268
 
+    $self->_spec_string(shift);
9269
 
+    return $self;
9270
 
+}
9271
 
+
9272
 
+sub filters() {
9273
 
+    (my ($self), @_) = find_my_self(@_);
9274
 
+    if (ref($_[0]) eq 'HASH') {
9275
 
+        $self->_filters_map(shift);
9276
 
+    }
9277
 
+    else {
9278
 
+        my $filters = $self->_filters;
9279
 
+        push @$filters, @_;
9280
 
+    }
9281
 
+    return $self;
9282
 
+}
9283
 
+
9284
 
+sub filter_arguments() {
9285
 
+    $Test::Base::Filter::arguments;
9286
 
+}
9287
 
+
9288
 
+sub have_text_diff {
9289
 
+    eval { require Text::Diff; 1 } &&
9290
 
+        $Text::Diff::VERSION >= 0.35 &&
9291
 
+        $Algorithm::Diff::VERSION >= 1.15;
9292
 
+}
9293
 
+
9294
 
+sub is($$;$) {
9295
 
+    (my ($self), @_) = find_my_self(@_);
9296
 
+    my ($actual, $expected, $name) = @_;
9297
 
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
9298
 
+    if ($ENV{TEST_SHOW_NO_DIFFS} or
9299
 
+         not defined $actual or
9300
 
+         not defined $expected or
9301
 
+         $actual eq $expected or
9302
 
+         not($self->have_text_diff) or
9303
 
+         $expected !~ /\n./s
9304
 
+    ) {
9305
 
+        Test::More::is($actual, $expected, $name);
9306
 
+    }
9307
 
+    else {
9308
 
+        $name = '' unless defined $name;
9309
 
+        ok $actual eq $expected,
9310
 
+           $name . "\n" . Text::Diff::diff(\$expected, \$actual);
9311
 
+    }
9312
 
+}
9313
 
+
9314
 
+sub run(&;$) {
9315
 
+    (my ($self), @_) = find_my_self(@_);
9316
 
+    my $callback = shift;
9317
 
+    for my $block (@{$self->block_list}) {
9318
 
+        $block->run_filters unless $block->is_filtered;
9319
 
+        &{$callback}($block);
9320
 
+    }
9321
 
+}
9322
 
+
9323
 
+my $name_error = "Can't determine section names";
9324
 
+sub _section_names {
9325
 
+    return @_ if @_ == 2;
9326
 
+    my $block = $self->first_block
9327
 
+      or croak $name_error;
9328
 
+    my @names = grep {
9329
 
+        $_ !~ /^(ONLY|LAST|SKIP)$/;
9330
 
+    } @{$block->{_section_order}[0] || []};
9331
 
+    croak "$name_error. Need two sections in first block"
9332
 
+      unless @names == 2;
9333
 
+    return @names;
9334
 
+}
9335
 
+
9336
 
+sub _assert_plan {
9337
 
+    plan('no_plan') unless $Have_Plan;
9338
 
+}
9339
 
+
9340
 
+sub END {
9341
 
+    run_compare() unless $Have_Plan or $DIED or not $import_called;
9342
 
+}
9343
 
+
9344
 
+sub run_compare() {
9345
 
+    (my ($self), @_) = find_my_self(@_);
9346
 
+    $self->_assert_plan;
9347
 
+    my ($x, $y) = $self->_section_names(@_);
9348
 
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
9349
 
+    for my $block (@{$self->block_list}) {
9350
 
+        next unless exists($block->{$x}) and exists($block->{$y});
9351
 
+        $block->run_filters unless $block->is_filtered;
9352
 
+        if (ref $block->$x) {
9353
 
+            is_deeply($block->$x, $block->$y,
9354
 
+                $block->name ? $block->name : ());
9355
 
+        }
9356
 
+        elsif (ref $block->$y eq 'Regexp') {
9357
 
+            my $regexp = ref $y ? $y : $block->$y;
9358
 
+            like($block->$x, $regexp, $block->name ? $block->name : ());
9359
 
+        }
9360
 
+        else {
9361
 
+            is($block->$x, $block->$y, $block->name ? $block->name : ());
9362
 
+        }
9363
 
+    }
9364
 
+}
9365
 
+
9366
 
+sub run_is() {
9367
 
+    (my ($self), @_) = find_my_self(@_);
9368
 
+    $self->_assert_plan;
9369
 
+    my ($x, $y) = $self->_section_names(@_);
9370
 
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
9371
 
+    for my $block (@{$self->block_list}) {
9372
 
+        next unless exists($block->{$x}) and exists($block->{$y});
9373
 
+        $block->run_filters unless $block->is_filtered;
9374
 
+        is($block->$x, $block->$y,
9375
 
+           $block->name ? $block->name : ()
9376
 
+          );
9377
 
+    }
9378
 
+}
9379
 
+
9380
 
+sub run_is_deeply() {
9381
 
+    (my ($self), @_) = find_my_self(@_);
9382
 
+    $self->_assert_plan;
9383
 
+    my ($x, $y) = $self->_section_names(@_);
9384
 
+    for my $block (@{$self->block_list}) {
9385
 
+        next unless exists($block->{$x}) and exists($block->{$y});
9386
 
+        $block->run_filters unless $block->is_filtered;
9387
 
+        is_deeply($block->$x, $block->$y,
9388
 
+           $block->name ? $block->name : ()
9389
 
+          );
9390
 
+    }
9391
 
+}
9392
 
+
9393
 
+sub run_like() {
9394
 
+    (my ($self), @_) = find_my_self(@_);
9395
 
+    $self->_assert_plan;
9396
 
+    my ($x, $y) = $self->_section_names(@_);
9397
 
+    for my $block (@{$self->block_list}) {
9398
 
+        next unless exists($block->{$x}) and defined($y);
9399
 
+        $block->run_filters unless $block->is_filtered;
9400
 
+        my $regexp = ref $y ? $y : $block->$y;
9401
 
+        like($block->$x, $regexp,
9402
 
+             $block->name ? $block->name : ()
9403
 
+            );
9404
 
+    }
9405
 
+}
9406
 
+
9407
 
+sub run_unlike() {
9408
 
+    (my ($self), @_) = find_my_self(@_);
9409
 
+    $self->_assert_plan;
9410
 
+    my ($x, $y) = $self->_section_names(@_);
9411
 
+    for my $block (@{$self->block_list}) {
9412
 
+        next unless exists($block->{$x}) and defined($y);
9413
 
+        $block->run_filters unless $block->is_filtered;
9414
 
+        my $regexp = ref $y ? $y : $block->$y;
9415
 
+        unlike($block->$x, $regexp,
9416
 
+               $block->name ? $block->name : ()
9417
 
+              );
9418
 
+    }
9419
 
+}
9420
 
+
9421
 
+sub skip_all_unless_require() {
9422
 
+    (my ($self), @_) = find_my_self(@_);
9423
 
+    my $module = shift;
9424
 
+    eval "require $module; 1"
9425
 
+        or Test::More::plan(
9426
 
+            skip_all => "$module failed to load"
9427
 
+        );
9428
 
+}
9429
 
+
9430
 
+sub is_deep() {
9431
 
+    (my ($self), @_) = find_my_self(@_);
9432
 
+    require Test::Deep;
9433
 
+    Test::Deep::cmp_deeply(@_);
9434
 
+}
9435
 
+
9436
 
+sub run_is_deep() {
9437
 
+    (my ($self), @_) = find_my_self(@_);
9438
 
+    $self->_assert_plan;
9439
 
+    my ($x, $y) = $self->_section_names(@_);
9440
 
+    for my $block (@{$self->block_list}) {
9441
 
+        next unless exists($block->{$x}) and exists($block->{$y});
9442
 
+        $block->run_filters unless $block->is_filtered;
9443
 
+        is_deep($block->$x, $block->$y,
9444
 
+           $block->name ? $block->name : ()
9445
 
+          );
9446
 
+    }
9447
 
+}
9448
 
+
9449
 
+sub _pre_eval {
9450
 
+    my $spec = shift;
9451
 
+    return $spec unless $spec =~
9452
 
+      s/\A\s*<<<(.*?)>>>\s*$//sm;
9453
 
+    my $eval_code = $1;
9454
 
+    eval "package main; $eval_code";
9455
 
+    croak $@ if $@;
9456
 
+    return $spec;
9457
 
+}
9458
 
+
9459
 
+sub _block_list_init {
9460
 
+    my $spec = $self->spec;
9461
 
+    $spec = $self->_pre_eval($spec);
9462
 
+    my $cd = $self->block_delim;
9463
 
+    my @hunks = ($spec =~ /^(\Q${cd}\E.*?(?=^\Q${cd}\E|\z))/msg);
9464
 
+    my $blocks = $self->_choose_blocks(@hunks);
9465
 
+    $self->block_list($blocks); # Need to set early for possible filter use
9466
 
+    my $seq = 1;
9467
 
+    for my $block (@$blocks) {
9468
 
+        $block->blocks_object($self);
9469
 
+        $block->seq_num($seq++);
9470
 
+    }
9471
 
+    return $blocks;
9472
 
+}
9473
 
+
9474
 
+sub _choose_blocks {
9475
 
+    my $blocks = [];
9476
 
+    for my $hunk (@_) {
9477
 
+        my $block = $self->_make_block($hunk);
9478
 
+        if (exists $block->{ONLY}) {
9479
 
+            diag "I found ONLY: maybe you're debugging?"
9480
 
+                unless $self->_no_diag_on_only;
9481
 
+            return [$block];
9482
 
+        }
9483
 
+        next if exists $block->{SKIP};
9484
 
+        push @$blocks, $block;
9485
 
+        if (exists $block->{LAST}) {
9486
 
+            return $blocks;
9487
 
+        }
9488
 
+    }
9489
 
+    return $blocks;
9490
 
+}
9491
 
+
9492
 
+sub _check_reserved {
9493
 
+    my $id = shift;
9494
 
+    croak "'$id' is a reserved name. Use something else.\n"
9495
 
+      if $reserved_section_names->{$id} or
9496
 
+         $id =~ /^_/;
9497
 
+}
9498
 
+
9499
 
+sub _make_block {
9500
 
+    my $hunk = shift;
9501
 
+    my $cd = $self->block_delim;
9502
 
+    my $dd = $self->data_delim;
9503
 
+    my $block = $self->block_class->new;
9504
 
+    $hunk =~ s/\A\Q${cd}\E[ \t]*(.*)\s+// or die;
9505
 
+    my $name = $1;
9506
 
+    my @parts = split /^\Q${dd}\E +\(?(\w+)\)? *(.*)?\n/m, $hunk;
9507
 
+    my $description = shift @parts;
9508
 
+    $description ||= '';
9509
 
+    unless ($description =~ /\S/) {
9510
 
+        $description = $name;
9511
 
+    }
9512
 
+    $description =~ s/\s*\z//;
9513
 
+    $block->set_value(description => $description);
9514
 
+
9515
 
+    my $section_map = {};
9516
 
+    my $section_order = [];
9517
 
+    while (@parts) {
9518
 
+        my ($type, $filters, $value) = splice(@parts, 0, 3);
9519
 
+        $self->_check_reserved($type);
9520
 
+        $value = '' unless defined $value;
9521
 
+        $filters = '' unless defined $filters;
9522
 
+        if ($filters =~ /:(\s|\z)/) {
9523
 
+            croak "Extra lines not allowed in '$type' section"
9524
 
+              if $value =~ /\S/;
9525
 
+            ($filters, $value) = split /\s*:(?:\s+|\z)/, $filters, 2;
9526
 
+            $value = '' unless defined $value;
9527
 
+            $value =~ s/^\s*(.*?)\s*$/$1/;
9528
 
+        }
9529
 
+        $section_map->{$type} = {
9530
 
+            filters => $filters,
9531
 
+        };
9532
 
+        push @$section_order, $type;
9533
 
+        $block->set_value($type, $value);
9534
 
+    }
9535
 
+    $block->set_value(name => $name);
9536
 
+    $block->set_value(_section_map => $section_map);
9537
 
+    $block->set_value(_section_order => $section_order);
9538
 
+    return $block;
9539
 
+}
9540
 
+
9541
 
+sub _spec_init {
9542
 
+    return $self->_spec_string
9543
 
+      if $self->_spec_string;
9544
 
+    local $/;
9545
 
+    my $spec;
9546
 
+    if (my $spec_file = $self->_spec_file) {
9547
 
+        open FILE, $spec_file or die $!;
9548
 
+        $spec = <FILE>;
9549
 
+        close FILE;
9550
 
+    }
9551
 
+    else {
9552
 
+        $spec = do {
9553
 
+            package main;
9554
 
+            no warnings 'once';
9555
 
+            <DATA>;
9556
 
+        };
9557
 
+    }
9558
 
+    return $spec;
9559
 
+}
9560
 
+
9561
 
+sub _strict_warnings() {
9562
 
+    require Filter::Util::Call;
9563
 
+    my $done = 0;
9564
 
+    Filter::Util::Call::filter_add(
9565
 
+        sub {
9566
 
+            return 0 if $done;
9567
 
+            my ($data, $end) = ('', '');
9568
 
+            while (my $status = Filter::Util::Call::filter_read()) {
9569
 
+                return $status if $status < 0;
9570
 
+                if (/^__(?:END|DATA)__\r?$/) {
9571
 
+                    $end = $_;
9572
 
+                    last;
9573
 
+                }
9574
 
+                $data .= $_;
9575
 
+                $_ = '';
9576
 
+            }
9577
 
+            $_ = "use strict;use warnings;$data$end";
9578
 
+            $done = 1;
9579
 
+        }
9580
 
+    );
9581
 
+}
9582
 
+
9583
 
+sub tie_output() {
9584
 
+    my $handle = shift;
9585
 
+    die "No buffer to tie" unless @_;
9586
 
+    tie $handle, 'Test::Base::Handle', $_[0];
9587
 
+}
9588
 
+
9589
 
+sub no_diff {
9590
 
+    $ENV{TEST_SHOW_NO_DIFFS} = 1;
9591
 
+}
9592
 
+
9593
 
+package Test::Base::Handle;
9594
 
+
9595
 
+sub TIEHANDLE() {
9596
 
+    my $class = shift;
9597
 
+    bless \ $_[0], $class;
9598
 
+}
9599
 
+
9600
 
+sub PRINT {
9601
 
+    $$self .= $_ for @_;
9602
 
+}
9603
 
+
9604
 
+#===============================================================================
9605
 
+# Test::Base::Block
9606
 
+#
9607
 
+# This is the default class for accessing a Test::Base block object.
9608
 
+#===============================================================================
9609
 
+package Test::Base::Block;
9610
 
+our @ISA = qw(Spiffy);
9611
 
+
9612
 
+our @EXPORT = qw(block_accessor);
9613
 
+
9614
 
+sub AUTOLOAD {
9615
 
+    return;
9616
 
+}
9617
 
+
9618
 
+sub block_accessor() {
9619
 
+    my $accessor = shift;
9620
 
+    no strict 'refs';
9621
 
+    return if defined &$accessor;
9622
 
+    *$accessor = sub {
9623
 
+        my $self = shift;
9624
 
+        if (@_) {
9625
 
+            Carp::croak "Not allowed to set values for '$accessor'";
9626
 
+        }
9627
 
+        my @list = @{$self->{$accessor} || []};
9628
 
+        return wantarray
9629
 
+        ? (@list)
9630
 
+        : $list[0];
9631
 
+    };
9632
 
+}
9633
 
+
9634
 
+block_accessor 'name';
9635
 
+block_accessor 'description';
9636
 
+Spiffy::field 'seq_num';
9637
 
+Spiffy::field 'is_filtered';
9638
 
+Spiffy::field 'blocks_object';
9639
 
+Spiffy::field 'original_values' => {};
9640
 
+
9641
 
+sub set_value {
9642
 
+    no strict 'refs';
9643
 
+    my $accessor = shift;
9644
 
+    block_accessor $accessor
9645
 
+      unless defined &$accessor;
9646
 
+    $self->{$accessor} = [@_];
9647
 
+}
9648
 
+
9649
 
+sub run_filters {
9650
 
+    my $map = $self->_section_map;
9651
 
+    my $order = $self->_section_order;
9652
 
+    Carp::croak "Attempt to filter a block twice"
9653
 
+      if $self->is_filtered;
9654
 
+    for my $type (@$order) {
9655
 
+        my $filters = $map->{$type}{filters};
9656
 
+        my @value = $self->$type;
9657
 
+        $self->original_values->{$type} = $value[0];
9658
 
+        for my $filter ($self->_get_filters($type, $filters)) {
9659
 
+            $Test::Base::Filter::arguments =
9660
 
+              $filter =~ s/=(.*)$// ? $1 : undef;
9661
 
+            my $function = "main::$filter";
9662
 
+            no strict 'refs';
9663
 
+            if (defined &$function) {
9664
 
+                local $_ =
9665
 
+                    (@value == 1 and not defined($value[0])) ? undef :
9666
 
+                        join '', @value;
9667
 
+                my $old = $_;
9668
 
+                @value = &$function(@value);
9669
 
+                if (not(@value) or
9670
 
+                    @value == 1 and defined($value[0]) and $value[0] =~ /\A(\d+|)\z/
9671
 
+                ) {
9672
 
+                    if ($value[0] && $_ eq $old) {
9673
 
+                        Test::Base::diag("Filters returning numbers are supposed to do munging \$_: your filter '$function' apparently doesn't.");
9674
 
+                    }
9675
 
+                    @value = ($_);
9676
 
+                }
9677
 
+            }
9678
 
+            else {
9679
 
+                my $filter_object = $self->blocks_object->filter_class->new;
9680
 
+                die "Can't find a function or method for '$filter' filter\n"
9681
 
+                  unless $filter_object->can($filter);
9682
 
+                $filter_object->current_block($self);
9683
 
+                @value = $filter_object->$filter(@value);
9684
 
+            }
9685
 
+            # Set the value after each filter since other filters may be
9686
 
+            # introspecting.
9687
 
+            $self->set_value($type, @value);
9688
 
+        }
9689
 
+    }
9690
 
+    $self->is_filtered(1);
9691
 
+}
9692
 
+
9693
 
+sub _get_filters {
9694
 
+    my $type = shift;
9695
 
+    my $string = shift || '';
9696
 
+    $string =~ s/\s*(.*?)\s*/$1/;
9697
 
+    my @filters = ();
9698
 
+    my $map_filters = $self->blocks_object->_filters_map->{$type} || [];
9699
 
+    $map_filters = [ $map_filters ] unless ref $map_filters;
9700
 
+    my @append = ();
9701
 
+    for (
9702
 
+        @{$self->blocks_object->_filters},
9703
 
+        @$map_filters,
9704
 
+        split(/\s+/, $string),
9705
 
+    ) {
9706
 
+        my $filter = $_;
9707
 
+        last unless length $filter;
9708
 
+        if ($filter =~ s/^-//) {
9709
 
+            @filters = grep { $_ ne $filter } @filters;
9710
 
+        }
9711
 
+        elsif ($filter =~ s/^\+//) {
9712
 
+            push @append, $filter;
9713
 
+        }
9714
 
+        else {
9715
 
+            push @filters, $filter;
9716
 
+        }
9717
 
+    }
9718
 
+    return @filters, @append;
9719
 
+}
9720
 
+
9721
 
+{
9722
 
+    %$reserved_section_names = map {
9723
 
+        ($_, 1);
9724
 
+    } keys(%Test::Base::Block::), qw( new DESTROY );
9725
 
+}
9726
 
+
9727
 
+__DATA__
9728
 
+
9729
 
+=encoding utf8
9730
 
+
9731
 
+#line 1376
9732
 
Index: 0.8/modules/nginx-echo/test/inc/Test/Builder.pm
9733
 
===================================================================
9734
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
9735
 
+++ 0.8/modules/nginx-echo/test/inc/Test/Builder.pm     2010-10-31 07:35:23.648338001 +0000
9736
 
@@ -0,0 +1,1413 @@
9737
 
+#line 1
9738
 
+package Test::Builder;
9739
 
+
9740
 
+use 5.006;
9741
 
+use strict;
9742
 
+use warnings;
9743
 
+
9744
 
+our $VERSION = '0.92';
9745
 
+$VERSION = eval $VERSION;    ## no critic (BuiltinFunctions::ProhibitStringyEval)
9746
 
+
9747
 
+BEGIN {
9748
 
+    if( $] < 5.008 ) {
9749
 
+        require Test::Builder::IO::Scalar;
9750
 
+    }
9751
 
+}
9752
 
+
9753
 
+
9754
 
+# Make Test::Builder thread-safe for ithreads.
9755
 
+BEGIN {
9756
 
+    use Config;
9757
 
+    # Load threads::shared when threads are turned on.
9758
 
+    # 5.8.0's threads are so busted we no longer support them.
9759
 
+    if( $] >= 5.008001 && $Config{useithreads} && $INC{'threads.pm'} ) {
9760
 
+        require threads::shared;
9761
 
+
9762
 
+        # Hack around YET ANOTHER threads::shared bug.  It would
9763
 
+        # occassionally forget the contents of the variable when sharing it.
9764
 
+        # So we first copy the data, then share, then put our copy back.
9765
 
+        *share = sub (\[$@%]) {
9766
 
+            my $type = ref $_[0];
9767
 
+            my $data;
9768
 
+
9769
 
+            if( $type eq 'HASH' ) {
9770
 
+                %$data = %{ $_[0] };
9771
 
+            }
9772
 
+            elsif( $type eq 'ARRAY' ) {
9773
 
+                @$data = @{ $_[0] };
9774
 
+            }
9775
 
+            elsif( $type eq 'SCALAR' ) {
9776
 
+                $$data = ${ $_[0] };
9777
 
+            }
9778
 
+            else {
9779
 
+                die( "Unknown type: " . $type );
9780
 
+            }
9781
 
+
9782
 
+            $_[0] = &threads::shared::share( $_[0] );
9783
 
+
9784
 
+            if( $type eq 'HASH' ) {
9785
 
+                %{ $_[0] } = %$data;
9786
 
+            }
9787
 
+            elsif( $type eq 'ARRAY' ) {
9788
 
+                @{ $_[0] } = @$data;
9789
 
+            }
9790
 
+            elsif( $type eq 'SCALAR' ) {
9791
 
+                ${ $_[0] } = $$data;
9792
 
+            }
9793
 
+            else {
9794
 
+                die( "Unknown type: " . $type );
9795
 
+            }
9796
 
+
9797
 
+            return $_[0];
9798
 
+        };
9799
 
+    }
9800
 
+    # 5.8.0's threads::shared is busted when threads are off
9801
 
+    # and earlier Perls just don't have that module at all.
9802
 
+    else {
9803
 
+        *share = sub { return $_[0] };
9804
 
+        *lock  = sub { 0 };
9805
 
+    }
9806
 
+}
9807
 
+
9808
 
+#line 117
9809
 
+
9810
 
+my $Test = Test::Builder->new;
9811
 
+
9812
 
+sub new {
9813
 
+    my($class) = shift;
9814
 
+    $Test ||= $class->create;
9815
 
+    return $Test;
9816
 
+}
9817
 
+
9818
 
+#line 139
9819
 
+
9820
 
+sub create {
9821
 
+    my $class = shift;
9822
 
+
9823
 
+    my $self = bless {}, $class;
9824
 
+    $self->reset;
9825
 
+
9826
 
+    return $self;
9827
 
+}
9828
 
+
9829
 
+#line 158
9830
 
+
9831
 
+our $Level;
9832
 
+
9833
 
+sub reset {    ## no critic (Subroutines::ProhibitBuiltinHomonyms)
9834
 
+    my($self) = @_;
9835
 
+
9836
 
+    # We leave this a global because it has to be localized and localizing
9837
 
+    # hash keys is just asking for pain.  Also, it was documented.
9838
 
+    $Level = 1;
9839
 
+
9840
 
+    $self->{Have_Plan}    = 0;
9841
 
+    $self->{No_Plan}      = 0;
9842
 
+    $self->{Have_Output_Plan} = 0;
9843
 
+
9844
 
+    $self->{Original_Pid} = $$;
9845
 
+
9846
 
+    share( $self->{Curr_Test} );
9847
 
+    $self->{Curr_Test} = 0;
9848
 
+    $self->{Test_Results} = &share( [] );
9849
 
+
9850
 
+    $self->{Exported_To}    = undef;
9851
 
+    $self->{Expected_Tests} = 0;
9852
 
+
9853
 
+    $self->{Skip_All} = 0;
9854
 
+
9855
 
+    $self->{Use_Nums} = 1;
9856
 
+
9857
 
+    $self->{No_Header} = 0;
9858
 
+    $self->{No_Ending} = 0;
9859
 
+
9860
 
+    $self->{Todo}       = undef;
9861
 
+    $self->{Todo_Stack} = [];
9862
 
+    $self->{Start_Todo} = 0;
9863
 
+    $self->{Opened_Testhandles} = 0;
9864
 
+
9865
 
+    $self->_dup_stdhandles;
9866
 
+
9867
 
+    return;
9868
 
+}
9869
 
+
9870
 
+#line 219
9871
 
+
9872
 
+my %plan_cmds = (
9873
 
+    no_plan     => \&no_plan,
9874
 
+    skip_all    => \&skip_all,
9875
 
+    tests       => \&_plan_tests,
9876
 
+);
9877
 
+
9878
 
+sub plan {
9879
 
+    my( $self, $cmd, $arg ) = @_;
9880
 
+
9881
 
+    return unless $cmd;
9882
 
+
9883
 
+    local $Level = $Level + 1;
9884
 
+
9885
 
+    $self->croak("You tried to plan twice") if $self->{Have_Plan};
9886
 
+
9887
 
+    if( my $method = $plan_cmds{$cmd} ) {
9888
 
+        local $Level = $Level + 1;
9889
 
+        $self->$method($arg);
9890
 
+    }
9891
 
+    else {
9892
 
+        my @args = grep { defined } ( $cmd, $arg );
9893
 
+        $self->croak("plan() doesn't understand @args");
9894
 
+    }
9895
 
+
9896
 
+    return 1;
9897
 
+}
9898
 
+
9899
 
+
9900
 
+sub _plan_tests {
9901
 
+    my($self, $arg) = @_;
9902
 
+
9903
 
+    if($arg) {
9904
 
+        local $Level = $Level + 1;
9905
 
+        return $self->expected_tests($arg);
9906
 
+    }
9907
 
+    elsif( !defined $arg ) {
9908
 
+        $self->croak("Got an undefined number of tests");
9909
 
+    }
9910
 
+    else {
9911
 
+        $self->croak("You said to run 0 tests");
9912
 
+    }
9913
 
+
9914
 
+    return;
9915
 
+}
9916
 
+
9917
 
+
9918
 
+#line 275
9919
 
+
9920
 
+sub expected_tests {
9921
 
+    my $self = shift;
9922
 
+    my($max) = @_;
9923
 
+
9924
 
+    if(@_) {
9925
 
+        $self->croak("Number of tests must be a positive integer.  You gave it '$max'")
9926
 
+          unless $max =~ /^\+?\d+$/;
9927
 
+
9928
 
+        $self->{Expected_Tests} = $max;
9929
 
+        $self->{Have_Plan}      = 1;
9930
 
+
9931
 
+        $self->_output_plan($max) unless $self->no_header;
9932
 
+    }
9933
 
+    return $self->{Expected_Tests};
9934
 
+}
9935
 
+
9936
 
+#line 299
9937
 
+
9938
 
+sub no_plan {
9939
 
+    my($self, $arg) = @_;
9940
 
+
9941
 
+    $self->carp("no_plan takes no arguments") if $arg;
9942
 
+
9943
 
+    $self->{No_Plan}   = 1;
9944
 
+    $self->{Have_Plan} = 1;
9945
 
+
9946
 
+    return 1;
9947
 
+}
9948
 
+
9949
 
+
9950
 
+#line 333
9951
 
+
9952
 
+sub _output_plan {
9953
 
+    my($self, $max, $directive, $reason) = @_;
9954
 
+
9955
 
+    $self->carp("The plan was already output") if $self->{Have_Output_Plan};
9956
 
+
9957
 
+    my $plan = "1..$max";
9958
 
+    $plan .= " # $directive" if defined $directive;
9959
 
+    $plan .= " $reason"      if defined $reason;
9960
 
+
9961
 
+    $self->_print("$plan\n");
9962
 
+
9963
 
+    $self->{Have_Output_Plan} = 1;
9964
 
+
9965
 
+    return;
9966
 
+}
9967
 
+
9968
 
+#line 384
9969
 
+
9970
 
+sub done_testing {
9971
 
+    my($self, $num_tests) = @_;
9972
 
+
9973
 
+    # If done_testing() specified the number of tests, shut off no_plan.
9974
 
+    if( defined $num_tests ) {
9975
 
+        $self->{No_Plan} = 0;
9976
 
+    }
9977
 
+    else {
9978
 
+        $num_tests = $self->current_test;
9979
 
+    }
9980
 
+
9981
 
+    if( $self->{Done_Testing} ) {
9982
 
+        my($file, $line) = @{$self->{Done_Testing}}[1,2];
9983
 
+        $self->ok(0, "done_testing() was already called at $file line $line");
9984
 
+        return;
9985
 
+    }
9986
 
+
9987
 
+    $self->{Done_Testing} = [caller];
9988
 
+
9989
 
+    if( $self->expected_tests && $num_tests != $self->expected_tests ) {
9990
 
+        $self->ok(0, "planned to run @{[ $self->expected_tests ]} ".
9991
 
+                     "but done_testing() expects $num_tests");
9992
 
+    }
9993
 
+    else {
9994
 
+        $self->{Expected_Tests} = $num_tests;
9995
 
+    }
9996
 
+
9997
 
+    $self->_output_plan($num_tests) unless $self->{Have_Output_Plan};
9998
 
+
9999
 
+    $self->{Have_Plan} = 1;
10000
 
+
10001
 
+    return 1;
10002
 
+}
10003
 
+
10004
 
+
10005
 
+#line 429
10006
 
+
10007
 
+sub has_plan {
10008
 
+    my $self = shift;
10009
 
+
10010
 
+    return( $self->{Expected_Tests} ) if $self->{Expected_Tests};
10011
 
+    return('no_plan') if $self->{No_Plan};
10012
 
+    return(undef);
10013
 
+}
10014
 
+
10015
 
+#line 446
10016
 
+
10017
 
+sub skip_all {
10018
 
+    my( $self, $reason ) = @_;
10019
 
+
10020
 
+    $self->{Skip_All} = 1;
10021
 
+
10022
 
+    $self->_output_plan(0, "SKIP", $reason) unless $self->no_header;
10023
 
+    exit(0);
10024
 
+}
10025
 
+
10026
 
+#line 468
10027
 
+
10028
 
+sub exported_to {
10029
 
+    my( $self, $pack ) = @_;
10030
 
+
10031
 
+    if( defined $pack ) {
10032
 
+        $self->{Exported_To} = $pack;
10033
 
+    }
10034
 
+    return $self->{Exported_To};
10035
 
+}
10036
 
+
10037
 
+#line 498
10038
 
+
10039
 
+sub ok {
10040
 
+    my( $self, $test, $name ) = @_;
10041
 
+
10042
 
+    # $test might contain an object which we don't want to accidentally
10043
 
+    # store, so we turn it into a boolean.
10044
 
+    $test = $test ? 1 : 0;
10045
 
+
10046
 
+    lock $self->{Curr_Test};
10047
 
+    $self->{Curr_Test}++;
10048
 
+
10049
 
+    # In case $name is a string overloaded object, force it to stringify.
10050
 
+    $self->_unoverload_str( \$name );
10051
 
+
10052
 
+    $self->diag(<<"ERR") if defined $name and $name =~ /^[\d\s]+$/;
10053
 
+    You named your test '$name'.  You shouldn't use numbers for your test names.
10054
 
+    Very confusing.
10055
 
+ERR
10056
 
+
10057
 
+    # Capture the value of $TODO for the rest of this ok() call
10058
 
+    # so it can more easily be found by other routines.
10059
 
+    my $todo    = $self->todo();
10060
 
+    my $in_todo = $self->in_todo;
10061
 
+    local $self->{Todo} = $todo if $in_todo;
10062
 
+
10063
 
+    $self->_unoverload_str( \$todo );
10064
 
+
10065
 
+    my $out;
10066
 
+    my $result = &share( {} );
10067
 
+
10068
 
+    unless($test) {
10069
 
+        $out .= "not ";
10070
 
+        @$result{ 'ok', 'actual_ok' } = ( ( $self->in_todo ? 1 : 0 ), 0 );
10071
 
+    }
10072
 
+    else {
10073
 
+        @$result{ 'ok', 'actual_ok' } = ( 1, $test );
10074
 
+    }
10075
 
+
10076
 
+    $out .= "ok";
10077
 
+    $out .= " $self->{Curr_Test}" if $self->use_numbers;
10078
 
+
10079
 
+    if( defined $name ) {
10080
 
+        $name =~ s|#|\\#|g;    # # in a name can confuse Test::Harness.
10081
 
+        $out .= " - $name";
10082
 
+        $result->{name} = $name;
10083
 
+    }
10084
 
+    else {
10085
 
+        $result->{name} = '';
10086
 
+    }
10087
 
+
10088
 
+    if( $self->in_todo ) {
10089
 
+        $out .= " # TODO $todo";
10090
 
+        $result->{reason} = $todo;
10091
 
+        $result->{type}   = 'todo';
10092
 
+    }
10093
 
+    else {
10094
 
+        $result->{reason} = '';
10095
 
+        $result->{type}   = '';
10096
 
+    }
10097
 
+
10098
 
+    $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = $result;
10099
 
+    $out .= "\n";
10100
 
+
10101
 
+    $self->_print($out);
10102
 
+
10103
 
+    unless($test) {
10104
 
+        my $msg = $self->in_todo ? "Failed (TODO)" : "Failed";
10105
 
+        $self->_print_to_fh( $self->_diag_fh, "\n" ) if $ENV{HARNESS_ACTIVE};
10106
 
+
10107
 
+        my( undef, $file, $line ) = $self->caller;
10108
 
+        if( defined $name ) {
10109
 
+            $self->diag(qq[  $msg test '$name'\n]);
10110
 
+            $self->diag(qq[  at $file line $line.\n]);
10111
 
+        }
10112
 
+        else {
10113
 
+            $self->diag(qq[  $msg test at $file line $line.\n]);
10114
 
+        }
10115
 
+    }
10116
 
+
10117
 
+    return $test ? 1 : 0;
10118
 
+}
10119
 
+
10120
 
+sub _unoverload {
10121
 
+    my $self = shift;
10122
 
+    my $type = shift;
10123
 
+
10124
 
+    $self->_try(sub { require overload; }, die_on_fail => 1);
10125
 
+
10126
 
+    foreach my $thing (@_) {
10127
 
+        if( $self->_is_object($$thing) ) {
10128
 
+            if( my $string_meth = overload::Method( $$thing, $type ) ) {
10129
 
+                $$thing = $$thing->$string_meth();
10130
 
+            }
10131
 
+        }
10132
 
+    }
10133
 
+
10134
 
+    return;
10135
 
+}
10136
 
+
10137
 
+sub _is_object {
10138
 
+    my( $self, $thing ) = @_;
10139
 
+
10140
 
+    return $self->_try( sub { ref $thing && $thing->isa('UNIVERSAL') } ) ? 1 : 0;
10141
 
+}
10142
 
+
10143
 
+sub _unoverload_str {
10144
 
+    my $self = shift;
10145
 
+
10146
 
+    return $self->_unoverload( q[""], @_ );
10147
 
+}
10148
 
+
10149
 
+sub _unoverload_num {
10150
 
+    my $self = shift;
10151
 
+
10152
 
+    $self->_unoverload( '0+', @_ );
10153
 
+
10154
 
+    for my $val (@_) {
10155
 
+        next unless $self->_is_dualvar($$val);
10156
 
+        $$val = $$val + 0;
10157
 
+    }
10158
 
+
10159
 
+    return;
10160
 
+}
10161
 
+
10162
 
+# This is a hack to detect a dualvar such as $!
10163
 
+sub _is_dualvar {
10164
 
+    my( $self, $val ) = @_;
10165
 
+
10166
 
+    # Objects are not dualvars.
10167
 
+    return 0 if ref $val;
10168
 
+
10169
 
+    no warnings 'numeric';
10170
 
+    my $numval = $val + 0;
10171
 
+    return $numval != 0 and $numval ne $val ? 1 : 0;
10172
 
+}
10173
 
+
10174
 
+#line 649
10175
 
+
10176
 
+sub is_eq {
10177
 
+    my( $self, $got, $expect, $name ) = @_;
10178
 
+    local $Level = $Level + 1;
10179
 
+
10180
 
+    $self->_unoverload_str( \$got, \$expect );
10181
 
+
10182
 
+    if( !defined $got || !defined $expect ) {
10183
 
+        # undef only matches undef and nothing else
10184
 
+        my $test = !defined $got && !defined $expect;
10185
 
+
10186
 
+        $self->ok( $test, $name );
10187
 
+        $self->_is_diag( $got, 'eq', $expect ) unless $test;
10188
 
+        return $test;
10189
 
+    }
10190
 
+
10191
 
+    return $self->cmp_ok( $got, 'eq', $expect, $name );
10192
 
+}
10193
 
+
10194
 
+sub is_num {
10195
 
+    my( $self, $got, $expect, $name ) = @_;
10196
 
+    local $Level = $Level + 1;
10197
 
+
10198
 
+    $self->_unoverload_num( \$got, \$expect );
10199
 
+
10200
 
+    if( !defined $got || !defined $expect ) {
10201
 
+        # undef only matches undef and nothing else
10202
 
+        my $test = !defined $got && !defined $expect;
10203
 
+
10204
 
+        $self->ok( $test, $name );
10205
 
+        $self->_is_diag( $got, '==', $expect ) unless $test;
10206
 
+        return $test;
10207
 
+    }
10208
 
+
10209
 
+    return $self->cmp_ok( $got, '==', $expect, $name );
10210
 
+}
10211
 
+
10212
 
+sub _diag_fmt {
10213
 
+    my( $self, $type, $val ) = @_;
10214
 
+
10215
 
+    if( defined $$val ) {
10216
 
+        if( $type eq 'eq' or $type eq 'ne' ) {
10217
 
+            # quote and force string context
10218
 
+            $$val = "'$$val'";
10219
 
+        }
10220
 
+        else {
10221
 
+            # force numeric context
10222
 
+            $self->_unoverload_num($val);
10223
 
+        }
10224
 
+    }
10225
 
+    else {
10226
 
+        $$val = 'undef';
10227
 
+    }
10228
 
+
10229
 
+    return;
10230
 
+}
10231
 
+
10232
 
+sub _is_diag {
10233
 
+    my( $self, $got, $type, $expect ) = @_;
10234
 
+
10235
 
+    $self->_diag_fmt( $type, $_ ) for \$got, \$expect;
10236
 
+
10237
 
+    local $Level = $Level + 1;
10238
 
+    return $self->diag(<<"DIAGNOSTIC");
10239
 
+         got: $got
10240
 
+    expected: $expect
10241
 
+DIAGNOSTIC
10242
 
+
10243
 
+}
10244
 
+
10245
 
+sub _isnt_diag {
10246
 
+    my( $self, $got, $type ) = @_;
10247
 
+
10248
 
+    $self->_diag_fmt( $type, \$got );
10249
 
+
10250
 
+    local $Level = $Level + 1;
10251
 
+    return $self->diag(<<"DIAGNOSTIC");
10252
 
+         got: $got
10253
 
+    expected: anything else
10254
 
+DIAGNOSTIC
10255
 
+}
10256
 
+
10257
 
+#line 746
10258
 
+
10259
 
+sub isnt_eq {
10260
 
+    my( $self, $got, $dont_expect, $name ) = @_;
10261
 
+    local $Level = $Level + 1;
10262
 
+
10263
 
+    if( !defined $got || !defined $dont_expect ) {
10264
 
+        # undef only matches undef and nothing else
10265
 
+        my $test = defined $got || defined $dont_expect;
10266
 
+
10267
 
+        $self->ok( $test, $name );
10268
 
+        $self->_isnt_diag( $got, 'ne' ) unless $test;
10269
 
+        return $test;
10270
 
+    }
10271
 
+
10272
 
+    return $self->cmp_ok( $got, 'ne', $dont_expect, $name );
10273
 
+}
10274
 
+
10275
 
+sub isnt_num {
10276
 
+    my( $self, $got, $dont_expect, $name ) = @_;
10277
 
+    local $Level = $Level + 1;
10278
 
+
10279
 
+    if( !defined $got || !defined $dont_expect ) {
10280
 
+        # undef only matches undef and nothing else
10281
 
+        my $test = defined $got || defined $dont_expect;
10282
 
+
10283
 
+        $self->ok( $test, $name );
10284
 
+        $self->_isnt_diag( $got, '!=' ) unless $test;
10285
 
+        return $test;
10286
 
+    }
10287
 
+
10288
 
+    return $self->cmp_ok( $got, '!=', $dont_expect, $name );
10289
 
+}
10290
 
+
10291
 
+#line 797
10292
 
+
10293
 
+sub like {
10294
 
+    my( $self, $this, $regex, $name ) = @_;
10295
 
+
10296
 
+    local $Level = $Level + 1;
10297
 
+    return $self->_regex_ok( $this, $regex, '=~', $name );
10298
 
+}
10299
 
+
10300
 
+sub unlike {
10301
 
+    my( $self, $this, $regex, $name ) = @_;
10302
 
+
10303
 
+    local $Level = $Level + 1;
10304
 
+    return $self->_regex_ok( $this, $regex, '!~', $name );
10305
 
+}
10306
 
+
10307
 
+#line 821
10308
 
+
10309
 
+my %numeric_cmps = map { ( $_, 1 ) } ( "<", "<=", ">", ">=", "==", "!=", "<=>" );
10310
 
+
10311
 
+sub cmp_ok {
10312
 
+    my( $self, $got, $type, $expect, $name ) = @_;
10313
 
+
10314
 
+    my $test;
10315
 
+    my $error;
10316
 
+    {
10317
 
+        ## no critic (BuiltinFunctions::ProhibitStringyEval)
10318
 
+
10319
 
+        local( $@, $!, $SIG{__DIE__} );    # isolate eval
10320
 
+
10321
 
+        my($pack, $file, $line) = $self->caller();
10322
 
+
10323
 
+        $test = eval qq[
10324
 
+#line 1 "cmp_ok [from $file line $line]"
10325
 
+\$got $type \$expect;
10326
 
+];
10327
 
+        $error = $@;
10328
 
+    }
10329
 
+    local $Level = $Level + 1;
10330
 
+    my $ok = $self->ok( $test, $name );
10331
 
+
10332
 
+    # Treat overloaded objects as numbers if we're asked to do a
10333
 
+    # numeric comparison.
10334
 
+    my $unoverload
10335
 
+      = $numeric_cmps{$type}
10336
 
+      ? '_unoverload_num'
10337
 
+      : '_unoverload_str';
10338
 
+
10339
 
+    $self->diag(<<"END") if $error;
10340
 
+An error occurred while using $type:
10341
 
+------------------------------------
10342
 
+$error
10343
 
+------------------------------------
10344
 
+END
10345
 
+
10346
 
+    unless($ok) {
10347
 
+        $self->$unoverload( \$got, \$expect );
10348
 
+
10349
 
+        if( $type =~ /^(eq|==)$/ ) {
10350
 
+            $self->_is_diag( $got, $type, $expect );
10351
 
+        }
10352
 
+        elsif( $type =~ /^(ne|!=)$/ ) {
10353
 
+            $self->_isnt_diag( $got, $type );
10354
 
+        }
10355
 
+        else {
10356
 
+            $self->_cmp_diag( $got, $type, $expect );
10357
 
+        }
10358
 
+    }
10359
 
+    return $ok;
10360
 
+}
10361
 
+
10362
 
+sub _cmp_diag {
10363
 
+    my( $self, $got, $type, $expect ) = @_;
10364
 
+
10365
 
+    $got    = defined $got    ? "'$got'"    : 'undef';
10366
 
+    $expect = defined $expect ? "'$expect'" : 'undef';
10367
 
+
10368
 
+    local $Level = $Level + 1;
10369
 
+    return $self->diag(<<"DIAGNOSTIC");
10370
 
+    $got
10371
 
+        $type
10372
 
+    $expect
10373
 
+DIAGNOSTIC
10374
 
+}
10375
 
+
10376
 
+sub _caller_context {
10377
 
+    my $self = shift;
10378
 
+
10379
 
+    my( $pack, $file, $line ) = $self->caller(1);
10380
 
+
10381
 
+    my $code = '';
10382
 
+    $code .= "#line $line $file\n" if defined $file and defined $line;
10383
 
+
10384
 
+    return $code;
10385
 
+}
10386
 
+
10387
 
+#line 920
10388
 
+
10389
 
+sub BAIL_OUT {
10390
 
+    my( $self, $reason ) = @_;
10391
 
+
10392
 
+    $self->{Bailed_Out} = 1;
10393
 
+    $self->_print("Bail out!  $reason");
10394
 
+    exit 255;
10395
 
+}
10396
 
+
10397
 
+#line 933
10398
 
+
10399
 
+*BAILOUT = \&BAIL_OUT;
10400
 
+
10401
 
+#line 944
10402
 
+
10403
 
+sub skip {
10404
 
+    my( $self, $why ) = @_;
10405
 
+    $why ||= '';
10406
 
+    $self->_unoverload_str( \$why );
10407
 
+
10408
 
+    lock( $self->{Curr_Test} );
10409
 
+    $self->{Curr_Test}++;
10410
 
+
10411
 
+    $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = &share(
10412
 
+        {
10413
 
+            'ok'      => 1,
10414
 
+            actual_ok => 1,
10415
 
+            name      => '',
10416
 
+            type      => 'skip',
10417
 
+            reason    => $why,
10418
 
+        }
10419
 
+    );
10420
 
+
10421
 
+    my $out = "ok";
10422
 
+    $out .= " $self->{Curr_Test}" if $self->use_numbers;
10423
 
+    $out .= " # skip";
10424
 
+    $out .= " $why"               if length $why;
10425
 
+    $out .= "\n";
10426
 
+
10427
 
+    $self->_print($out);
10428
 
+
10429
 
+    return 1;
10430
 
+}
10431
 
+
10432
 
+#line 985
10433
 
+
10434
 
+sub todo_skip {
10435
 
+    my( $self, $why ) = @_;
10436
 
+    $why ||= '';
10437
 
+
10438
 
+    lock( $self->{Curr_Test} );
10439
 
+    $self->{Curr_Test}++;
10440
 
+
10441
 
+    $self->{Test_Results}[ $self->{Curr_Test} - 1 ] = &share(
10442
 
+        {
10443
 
+            'ok'      => 1,
10444
 
+            actual_ok => 0,
10445
 
+            name      => '',
10446
 
+            type      => 'todo_skip',
10447
 
+            reason    => $why,
10448
 
+        }
10449
 
+    );
10450
 
+
10451
 
+    my $out = "not ok";
10452
 
+    $out .= " $self->{Curr_Test}" if $self->use_numbers;
10453
 
+    $out .= " # TODO & SKIP $why\n";
10454
 
+
10455
 
+    $self->_print($out);
10456
 
+
10457
 
+    return 1;
10458
 
+}
10459
 
+
10460
 
+#line 1062
10461
 
+
10462
 
+sub maybe_regex {
10463
 
+    my( $self, $regex ) = @_;
10464
 
+    my $usable_regex = undef;
10465
 
+
10466
 
+    return $usable_regex unless defined $regex;
10467
 
+
10468
 
+    my( $re, $opts );
10469
 
+
10470
 
+    # Check for qr/foo/
10471
 
+    if( _is_qr($regex) ) {
10472
 
+        $usable_regex = $regex;
10473
 
+    }
10474
 
+    # Check for '/foo/' or 'm,foo,'
10475
 
+    elsif(( $re, $opts )        = $regex =~ m{^ /(.*)/ (\w*) $ }sx              or
10476
 
+          ( undef, $re, $opts ) = $regex =~ m,^ m([^\w\s]) (.+) \1 (\w*) $,sx
10477
 
+    )
10478
 
+    {
10479
 
+        $usable_regex = length $opts ? "(?$opts)$re" : $re;
10480
 
+    }
10481
 
+
10482
 
+    return $usable_regex;
10483
 
+}
10484
 
+
10485
 
+sub _is_qr {
10486
 
+    my $regex = shift;
10487
 
+
10488
 
+    # is_regexp() checks for regexes in a robust manner, say if they're
10489
 
+    # blessed.
10490
 
+    return re::is_regexp($regex) if defined &re::is_regexp;
10491
 
+    return ref $regex eq 'Regexp';
10492
 
+}
10493
 
+
10494
 
+sub _regex_ok {
10495
 
+    my( $self, $this, $regex, $cmp, $name ) = @_;
10496
 
+
10497
 
+    my $ok           = 0;
10498
 
+    my $usable_regex = $self->maybe_regex($regex);
10499
 
+    unless( defined $usable_regex ) {
10500
 
+        local $Level = $Level + 1;
10501
 
+        $ok = $self->ok( 0, $name );
10502
 
+        $self->diag("    '$regex' doesn't look much like a regex to me.");
10503
 
+        return $ok;
10504
 
+    }
10505
 
+
10506
 
+    {
10507
 
+        ## no critic (BuiltinFunctions::ProhibitStringyEval)
10508
 
+
10509
 
+        my $test;
10510
 
+        my $code = $self->_caller_context;
10511
 
+
10512
 
+        local( $@, $!, $SIG{__DIE__} );    # isolate eval
10513
 
+
10514
 
+        # Yes, it has to look like this or 5.4.5 won't see the #line
10515
 
+        # directive.
10516
 
+        # Don't ask me, man, I just work here.
10517
 
+        $test = eval "
10518
 
+$code" . q{$test = $this =~ /$usable_regex/ ? 1 : 0};
10519
 
+
10520
 
+        $test = !$test if $cmp eq '!~';
10521
 
+
10522
 
+        local $Level = $Level + 1;
10523
 
+        $ok = $self->ok( $test, $name );
10524
 
+    }
10525
 
+
10526
 
+    unless($ok) {
10527
 
+        $this = defined $this ? "'$this'" : 'undef';
10528
 
+        my $match = $cmp eq '=~' ? "doesn't match" : "matches";
10529
 
+
10530
 
+        local $Level = $Level + 1;
10531
 
+        $self->diag( sprintf <<'DIAGNOSTIC', $this, $match, $regex );
10532
 
+                  %s
10533
 
+    %13s '%s'
10534
 
+DIAGNOSTIC
10535
 
+
10536
 
+    }
10537
 
+
10538
 
+    return $ok;
10539
 
+}
10540
 
+
10541
 
+# I'm not ready to publish this.  It doesn't deal with array return
10542
 
+# values from the code or context.
10543
 
+
10544
 
+#line 1162
10545
 
+
10546
 
+sub _try {
10547
 
+    my( $self, $code, %opts ) = @_;
10548
 
+
10549
 
+    my $error;
10550
 
+    my $return;
10551
 
+    {
10552
 
+        local $!;               # eval can mess up $!
10553
 
+        local $@;               # don't set $@ in the test
10554
 
+        local $SIG{__DIE__};    # don't trip an outside DIE handler.
10555
 
+        $return = eval { $code->() };
10556
 
+        $error = $@;
10557
 
+    }
10558
 
+
10559
 
+    die $error if $error and $opts{die_on_fail};
10560
 
+
10561
 
+    return wantarray ? ( $return, $error ) : $return;
10562
 
+}
10563
 
+
10564
 
+#line 1191
10565
 
+
10566
 
+sub is_fh {
10567
 
+    my $self     = shift;
10568
 
+    my $maybe_fh = shift;
10569
 
+    return 0 unless defined $maybe_fh;
10570
 
+
10571
 
+    return 1 if ref $maybe_fh  eq 'GLOB';    # its a glob ref
10572
 
+    return 1 if ref \$maybe_fh eq 'GLOB';    # its a glob
10573
 
+
10574
 
+    return eval { $maybe_fh->isa("IO::Handle") } ||
10575
 
+           # 5.5.4's tied() and can() doesn't like getting undef
10576
 
+           eval { ( tied($maybe_fh) || '' )->can('TIEHANDLE') };
10577
 
+}
10578
 
+
10579
 
+#line 1235
10580
 
+
10581
 
+sub level {
10582
 
+    my( $self, $level ) = @_;
10583
 
+
10584
 
+    if( defined $level ) {
10585
 
+        $Level = $level;
10586
 
+    }
10587
 
+    return $Level;
10588
 
+}
10589
 
+
10590
 
+#line 1267
10591
 
+
10592
 
+sub use_numbers {
10593
 
+    my( $self, $use_nums ) = @_;
10594
 
+
10595
 
+    if( defined $use_nums ) {
10596
 
+        $self->{Use_Nums} = $use_nums;
10597
 
+    }
10598
 
+    return $self->{Use_Nums};
10599
 
+}
10600
 
+
10601
 
+#line 1300
10602
 
+
10603
 
+foreach my $attribute (qw(No_Header No_Ending No_Diag)) {
10604
 
+    my $method = lc $attribute;
10605
 
+
10606
 
+    my $code = sub {
10607
 
+        my( $self, $no ) = @_;
10608
 
+
10609
 
+        if( defined $no ) {
10610
 
+            $self->{$attribute} = $no;
10611
 
+        }
10612
 
+        return $self->{$attribute};
10613
 
+    };
10614
 
+
10615
 
+    no strict 'refs';    ## no critic
10616
 
+    *{ __PACKAGE__ . '::' . $method } = $code;
10617
 
+}
10618
 
+
10619
 
+#line 1353
10620
 
+
10621
 
+sub diag {
10622
 
+    my $self = shift;
10623
 
+
10624
 
+    $self->_print_comment( $self->_diag_fh, @_ );
10625
 
+}
10626
 
+
10627
 
+#line 1368
10628
 
+
10629
 
+sub note {
10630
 
+    my $self = shift;
10631
 
+
10632
 
+    $self->_print_comment( $self->output, @_ );
10633
 
+}
10634
 
+
10635
 
+sub _diag_fh {
10636
 
+    my $self = shift;
10637
 
+
10638
 
+    local $Level = $Level + 1;
10639
 
+    return $self->in_todo ? $self->todo_output : $self->failure_output;
10640
 
+}
10641
 
+
10642
 
+sub _print_comment {
10643
 
+    my( $self, $fh, @msgs ) = @_;
10644
 
+
10645
 
+    return if $self->no_diag;
10646
 
+    return unless @msgs;
10647
 
+
10648
 
+    # Prevent printing headers when compiling (i.e. -c)
10649
 
+    return if $^C;
10650
 
+
10651
 
+    # Smash args together like print does.
10652
 
+    # Convert undef to 'undef' so its readable.
10653
 
+    my $msg = join '', map { defined($_) ? $_ : 'undef' } @msgs;
10654
 
+
10655
 
+    # Escape the beginning, _print will take care of the rest.
10656
 
+    $msg =~ s/^/# /;
10657
 
+
10658
 
+    local $Level = $Level + 1;
10659
 
+    $self->_print_to_fh( $fh, $msg );
10660
 
+
10661
 
+    return 0;
10662
 
+}
10663
 
+
10664
 
+#line 1418
10665
 
+
10666
 
+sub explain {
10667
 
+    my $self = shift;
10668
 
+
10669
 
+    return map {
10670
 
+        ref $_
10671
 
+          ? do {
10672
 
+            $self->_try(sub { require Data::Dumper }, die_on_fail => 1);
10673
 
+
10674
 
+            my $dumper = Data::Dumper->new( [$_] );
10675
 
+            $dumper->Indent(1)->Terse(1);
10676
 
+            $dumper->Sortkeys(1) if $dumper->can("Sortkeys");
10677
 
+            $dumper->Dump;
10678
 
+          }
10679
 
+          : $_
10680
 
+    } @_;
10681
 
+}
10682
 
+
10683
 
+#line 1447
10684
 
+
10685
 
+sub _print {
10686
 
+    my $self = shift;
10687
 
+    return $self->_print_to_fh( $self->output, @_ );
10688
 
+}
10689
 
+
10690
 
+sub _print_to_fh {
10691
 
+    my( $self, $fh, @msgs ) = @_;
10692
 
+
10693
 
+    # Prevent printing headers when only compiling.  Mostly for when
10694
 
+    # tests are deparsed with B::Deparse
10695
 
+    return if $^C;
10696
 
+
10697
 
+    my $msg = join '', @msgs;
10698
 
+
10699
 
+    local( $\, $", $, ) = ( undef, ' ', '' );
10700
 
+
10701
 
+    # Escape each line after the first with a # so we don't
10702
 
+    # confuse Test::Harness.
10703
 
+    $msg =~ s{\n(?!\z)}{\n# }sg;
10704
 
+
10705
 
+    # Stick a newline on the end if it needs it.
10706
 
+    $msg .= "\n" unless $msg =~ /\n\z/;
10707
 
+
10708
 
+    return print $fh $msg;
10709
 
+}
10710
 
+
10711
 
+#line 1506
10712
 
+
10713
 
+sub output {
10714
 
+    my( $self, $fh ) = @_;
10715
 
+
10716
 
+    if( defined $fh ) {
10717
 
+        $self->{Out_FH} = $self->_new_fh($fh);
10718
 
+    }
10719
 
+    return $self->{Out_FH};
10720
 
+}
10721
 
+
10722
 
+sub failure_output {
10723
 
+    my( $self, $fh ) = @_;
10724
 
+
10725
 
+    if( defined $fh ) {
10726
 
+        $self->{Fail_FH} = $self->_new_fh($fh);
10727
 
+    }
10728
 
+    return $self->{Fail_FH};
10729
 
+}
10730
 
+
10731
 
+sub todo_output {
10732
 
+    my( $self, $fh ) = @_;
10733
 
+
10734
 
+    if( defined $fh ) {
10735
 
+        $self->{Todo_FH} = $self->_new_fh($fh);
10736
 
+    }
10737
 
+    return $self->{Todo_FH};
10738
 
+}
10739
 
+
10740
 
+sub _new_fh {
10741
 
+    my $self = shift;
10742
 
+    my($file_or_fh) = shift;
10743
 
+
10744
 
+    my $fh;
10745
 
+    if( $self->is_fh($file_or_fh) ) {
10746
 
+        $fh = $file_or_fh;
10747
 
+    }
10748
 
+    elsif( ref $file_or_fh eq 'SCALAR' ) {
10749
 
+        # Scalar refs as filehandles was added in 5.8.
10750
 
+        if( $] >= 5.008 ) {
10751
 
+            open $fh, ">>", $file_or_fh
10752
 
+              or $self->croak("Can't open scalar ref $file_or_fh: $!");
10753
 
+        }
10754
 
+        # Emulate scalar ref filehandles with a tie.
10755
 
+        else {
10756
 
+            $fh = Test::Builder::IO::Scalar->new($file_or_fh)
10757
 
+              or $self->croak("Can't tie scalar ref $file_or_fh");
10758
 
+        }
10759
 
+    }
10760
 
+    else {
10761
 
+        open $fh, ">", $file_or_fh
10762
 
+          or $self->croak("Can't open test output log $file_or_fh: $!");
10763
 
+        _autoflush($fh);
10764
 
+    }
10765
 
+
10766
 
+    return $fh;
10767
 
+}
10768
 
+
10769
 
+sub _autoflush {
10770
 
+    my($fh) = shift;
10771
 
+    my $old_fh = select $fh;
10772
 
+    $| = 1;
10773
 
+    select $old_fh;
10774
 
+
10775
 
+    return;
10776
 
+}
10777
 
+
10778
 
+my( $Testout, $Testerr );
10779
 
+
10780
 
+sub _dup_stdhandles {
10781
 
+    my $self = shift;
10782
 
+
10783
 
+    $self->_open_testhandles;
10784
 
+
10785
 
+    # Set everything to unbuffered else plain prints to STDOUT will
10786
 
+    # come out in the wrong order from our own prints.
10787
 
+    _autoflush($Testout);
10788
 
+    _autoflush( \*STDOUT );
10789
 
+    _autoflush($Testerr);
10790
 
+    _autoflush( \*STDERR );
10791
 
+
10792
 
+    $self->reset_outputs;
10793
 
+
10794
 
+    return;
10795
 
+}
10796
 
+
10797
 
+sub _open_testhandles {
10798
 
+    my $self = shift;
10799
 
+
10800
 
+    return if $self->{Opened_Testhandles};
10801
 
+
10802
 
+    # We dup STDOUT and STDERR so people can change them in their
10803
 
+    # test suites while still getting normal test output.
10804
 
+    open( $Testout, ">&STDOUT" ) or die "Can't dup STDOUT:  $!";
10805
 
+    open( $Testerr, ">&STDERR" ) or die "Can't dup STDERR:  $!";
10806
 
+
10807
 
+    #    $self->_copy_io_layers( \*STDOUT, $Testout );
10808
 
+    #    $self->_copy_io_layers( \*STDERR, $Testerr );
10809
 
+
10810
 
+    $self->{Opened_Testhandles} = 1;
10811
 
+
10812
 
+    return;
10813
 
+}
10814
 
+
10815
 
+sub _copy_io_layers {
10816
 
+    my( $self, $src, $dst ) = @_;
10817
 
+
10818
 
+    $self->_try(
10819
 
+        sub {
10820
 
+            require PerlIO;
10821
 
+            my @src_layers = PerlIO::get_layers($src);
10822
 
+
10823
 
+            binmode $dst, join " ", map ":$_", @src_layers if @src_layers;
10824
 
+        }
10825
 
+    );
10826
 
+
10827
 
+    return;
10828
 
+}
10829
 
+
10830
 
+#line 1631
10831
 
+
10832
 
+sub reset_outputs {
10833
 
+    my $self = shift;
10834
 
+
10835
 
+    $self->output        ($Testout);
10836
 
+    $self->failure_output($Testerr);
10837
 
+    $self->todo_output   ($Testout);
10838
 
+
10839
 
+    return;
10840
 
+}
10841
 
+
10842
 
+#line 1657
10843
 
+
10844
 
+sub _message_at_caller {
10845
 
+    my $self = shift;
10846
 
+
10847
 
+    local $Level = $Level + 1;
10848
 
+    my( $pack, $file, $line ) = $self->caller;
10849
 
+    return join( "", @_ ) . " at $file line $line.\n";
10850
 
+}
10851
 
+
10852
 
+sub carp {
10853
 
+    my $self = shift;
10854
 
+    return warn $self->_message_at_caller(@_);
10855
 
+}
10856
 
+
10857
 
+sub croak {
10858
 
+    my $self = shift;
10859
 
+    return die $self->_message_at_caller(@_);
10860
 
+}
10861
 
+
10862
 
+
10863
 
+#line 1697
10864
 
+
10865
 
+sub current_test {
10866
 
+    my( $self, $num ) = @_;
10867
 
+
10868
 
+    lock( $self->{Curr_Test} );
10869
 
+    if( defined $num ) {
10870
 
+        $self->{Curr_Test} = $num;
10871
 
+
10872
 
+        # If the test counter is being pushed forward fill in the details.
10873
 
+        my $test_results = $self->{Test_Results};
10874
 
+        if( $num > @$test_results ) {
10875
 
+            my $start = @$test_results ? @$test_results : 0;
10876
 
+            for( $start .. $num - 1 ) {
10877
 
+                $test_results->[$_] = &share(
10878
 
+                    {
10879
 
+                        'ok'      => 1,
10880
 
+                        actual_ok => undef,
10881
 
+                        reason    => 'incrementing test number',
10882
 
+                        type      => 'unknown',
10883
 
+                        name      => undef
10884
 
+                    }
10885
 
+                );
10886
 
+            }
10887
 
+        }
10888
 
+        # If backward, wipe history.  Its their funeral.
10889
 
+        elsif( $num < @$test_results ) {
10890
 
+            $#{$test_results} = $num - 1;
10891
 
+        }
10892
 
+    }
10893
 
+    return $self->{Curr_Test};
10894
 
+}
10895
 
+
10896
 
+#line 1739
10897
 
+
10898
 
+sub summary {
10899
 
+    my($self) = shift;
10900
 
+
10901
 
+    return map { $_->{'ok'} } @{ $self->{Test_Results} };
10902
 
+}
10903
 
+
10904
 
+#line 1794
10905
 
+
10906
 
+sub details {
10907
 
+    my $self = shift;
10908
 
+    return @{ $self->{Test_Results} };
10909
 
+}
10910
 
+
10911
 
+#line 1823
10912
 
+
10913
 
+sub todo {
10914
 
+    my( $self, $pack ) = @_;
10915
 
+
10916
 
+    return $self->{Todo} if defined $self->{Todo};
10917
 
+
10918
 
+    local $Level = $Level + 1;
10919
 
+    my $todo = $self->find_TODO($pack);
10920
 
+    return $todo if defined $todo;
10921
 
+
10922
 
+    return '';
10923
 
+}
10924
 
+
10925
 
+#line 1845
10926
 
+
10927
 
+sub find_TODO {
10928
 
+    my( $self, $pack ) = @_;
10929
 
+
10930
 
+    $pack = $pack || $self->caller(1) || $self->exported_to;
10931
 
+    return unless $pack;
10932
 
+
10933
 
+    no strict 'refs';    ## no critic
10934
 
+    return ${ $pack . '::TODO' };
10935
 
+}
10936
 
+
10937
 
+#line 1863
10938
 
+
10939
 
+sub in_todo {
10940
 
+    my $self = shift;
10941
 
+
10942
 
+    local $Level = $Level + 1;
10943
 
+    return( defined $self->{Todo} || $self->find_TODO ) ? 1 : 0;
10944
 
+}
10945
 
+
10946
 
+#line 1913
10947
 
+
10948
 
+sub todo_start {
10949
 
+    my $self = shift;
10950
 
+    my $message = @_ ? shift : '';
10951
 
+
10952
 
+    $self->{Start_Todo}++;
10953
 
+    if( $self->in_todo ) {
10954
 
+        push @{ $self->{Todo_Stack} } => $self->todo;
10955
 
+    }
10956
 
+    $self->{Todo} = $message;
10957
 
+
10958
 
+    return;
10959
 
+}
10960
 
+
10961
 
+#line 1935
10962
 
+
10963
 
+sub todo_end {
10964
 
+    my $self = shift;
10965
 
+
10966
 
+    if( !$self->{Start_Todo} ) {
10967
 
+        $self->croak('todo_end() called without todo_start()');
10968
 
+    }
10969
 
+
10970
 
+    $self->{Start_Todo}--;
10971
 
+
10972
 
+    if( $self->{Start_Todo} && @{ $self->{Todo_Stack} } ) {
10973
 
+        $self->{Todo} = pop @{ $self->{Todo_Stack} };
10974
 
+    }
10975
 
+    else {
10976
 
+        delete $self->{Todo};
10977
 
+    }
10978
 
+
10979
 
+    return;
10980
 
+}
10981
 
+
10982
 
+#line 1968
10983
 
+
10984
 
+sub caller {    ## no critic (Subroutines::ProhibitBuiltinHomonyms)
10985
 
+    my( $self, $height ) = @_;
10986
 
+    $height ||= 0;
10987
 
+
10988
 
+    my $level = $self->level + $height + 1;
10989
 
+    my @caller;
10990
 
+    do {
10991
 
+        @caller = CORE::caller( $level );
10992
 
+        $level--;
10993
 
+    } until @caller;
10994
 
+    return wantarray ? @caller : $caller[0];
10995
 
+}
10996
 
+
10997
 
+#line 1985
10998
 
+
10999
 
+#line 1999
11000
 
+
11001
 
+#'#
11002
 
+sub _sanity_check {
11003
 
+    my $self = shift;
11004
 
+
11005
 
+    $self->_whoa( $self->{Curr_Test} < 0, 'Says here you ran a negative number of tests!' );
11006
 
+    $self->_whoa( $self->{Curr_Test} != @{ $self->{Test_Results} },
11007
 
+        'Somehow you got a different number of results than tests ran!' );
11008
 
+
11009
 
+    return;
11010
 
+}
11011
 
+
11012
 
+#line 2020
11013
 
+
11014
 
+sub _whoa {
11015
 
+    my( $self, $check, $desc ) = @_;
11016
 
+    if($check) {
11017
 
+        local $Level = $Level + 1;
11018
 
+        $self->croak(<<"WHOA");
11019
 
+WHOA!  $desc
11020
 
+This should never happen!  Please contact the author immediately!
11021
 
+WHOA
11022
 
+    }
11023
 
+
11024
 
+    return;
11025
 
+}
11026
 
+
11027
 
+#line 2044
11028
 
+
11029
 
+sub _my_exit {
11030
 
+    $? = $_[0];    ## no critic (Variables::RequireLocalizedPunctuationVars)
11031
 
+
11032
 
+    return 1;
11033
 
+}
11034
 
+
11035
 
+#line 2056
11036
 
+
11037
 
+sub _ending {
11038
 
+    my $self = shift;
11039
 
+
11040
 
+    my $real_exit_code = $?;
11041
 
+
11042
 
+    # Don't bother with an ending if this is a forked copy.  Only the parent
11043
 
+    # should do the ending.
11044
 
+    if( $self->{Original_Pid} != $$ ) {
11045
 
+        return;
11046
 
+    }
11047
 
+
11048
 
+    # Ran tests but never declared a plan or hit done_testing
11049
 
+    if( !$self->{Have_Plan} and $self->{Curr_Test} ) {
11050
 
+        $self->diag("Tests were run but no plan was declared and done_testing() was not seen.");
11051
 
+    }
11052
 
+
11053
 
+    # Exit if plan() was never called.  This is so "require Test::Simple"
11054
 
+    # doesn't puke.
11055
 
+    if( !$self->{Have_Plan} ) {
11056
 
+        return;
11057
 
+    }
11058
 
+
11059
 
+    # Don't do an ending if we bailed out.
11060
 
+    if( $self->{Bailed_Out} ) {
11061
 
+        return;
11062
 
+    }
11063
 
+
11064
 
+    # Figure out if we passed or failed and print helpful messages.
11065
 
+    my $test_results = $self->{Test_Results};
11066
 
+    if(@$test_results) {
11067
 
+        # The plan?  We have no plan.
11068
 
+        if( $self->{No_Plan} ) {
11069
 
+            $self->_output_plan($self->{Curr_Test}) unless $self->no_header;
11070
 
+            $self->{Expected_Tests} = $self->{Curr_Test};
11071
 
+        }
11072
 
+
11073
 
+        # Auto-extended arrays and elements which aren't explicitly
11074
 
+        # filled in with a shared reference will puke under 5.8.0
11075
 
+        # ithreads.  So we have to fill them in by hand. :(
11076
 
+        my $empty_result = &share( {} );
11077
 
+        for my $idx ( 0 .. $self->{Expected_Tests} - 1 ) {
11078
 
+            $test_results->[$idx] = $empty_result
11079
 
+              unless defined $test_results->[$idx];
11080
 
+        }
11081
 
+
11082
 
+        my $num_failed = grep !$_->{'ok'}, @{$test_results}[ 0 .. $self->{Curr_Test} - 1 ];
11083
 
+
11084
 
+        my $num_extra = $self->{Curr_Test} - $self->{Expected_Tests};
11085
 
+
11086
 
+        if( $num_extra != 0 ) {
11087
 
+            my $s = $self->{Expected_Tests} == 1 ? '' : 's';
11088
 
+            $self->diag(<<"FAIL");
11089
 
+Looks like you planned $self->{Expected_Tests} test$s but ran $self->{Curr_Test}.
11090
 
+FAIL
11091
 
+        }
11092
 
+
11093
 
+        if($num_failed) {
11094
 
+            my $num_tests = $self->{Curr_Test};
11095
 
+            my $s = $num_failed == 1 ? '' : 's';
11096
 
+
11097
 
+            my $qualifier = $num_extra == 0 ? '' : ' run';
11098
 
+
11099
 
+            $self->diag(<<"FAIL");
11100
 
+Looks like you failed $num_failed test$s of $num_tests$qualifier.
11101
 
+FAIL
11102
 
+        }
11103
 
+
11104
 
+        if($real_exit_code) {
11105
 
+            $self->diag(<<"FAIL");
11106
 
+Looks like your test exited with $real_exit_code just after $self->{Curr_Test}.
11107
 
+FAIL
11108
 
+
11109
 
+            _my_exit($real_exit_code) && return;
11110
 
+        }
11111
 
+
11112
 
+        my $exit_code;
11113
 
+        if($num_failed) {
11114
 
+            $exit_code = $num_failed <= 254 ? $num_failed : 254;
11115
 
+        }
11116
 
+        elsif( $num_extra != 0 ) {
11117
 
+            $exit_code = 255;
11118
 
+        }
11119
 
+        else {
11120
 
+            $exit_code = 0;
11121
 
+        }
11122
 
+
11123
 
+        _my_exit($exit_code) && return;
11124
 
+    }
11125
 
+    elsif( $self->{Skip_All} ) {
11126
 
+        _my_exit(0) && return;
11127
 
+    }
11128
 
+    elsif($real_exit_code) {
11129
 
+        $self->diag(<<"FAIL");
11130
 
+Looks like your test exited with $real_exit_code before it could output anything.
11131
 
+FAIL
11132
 
+        _my_exit($real_exit_code) && return;
11133
 
+    }
11134
 
+    else {
11135
 
+        $self->diag("No tests run!\n");
11136
 
+        _my_exit(255) && return;
11137
 
+    }
11138
 
+
11139
 
+    $self->_whoa( 1, "We fell off the end of _ending()" );
11140
 
+}
11141
 
+
11142
 
+END {
11143
 
+    $Test->_ending if defined $Test and !$Test->no_ending;
11144
 
+}
11145
 
+
11146
 
+#line 2236
11147
 
+
11148
 
+1;
11149
 
+
11150
 
Index: 0.8/modules/nginx-echo/test/inc/Test/Builder/Module.pm
11151
 
===================================================================
11152
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
11153
 
+++ 0.8/modules/nginx-echo/test/inc/Test/Builder/Module.pm      2010-10-31 07:35:23.648338001 +0000
11154
 
@@ -0,0 +1,81 @@
11155
 
+#line 1
11156
 
+package Test::Builder::Module;
11157
 
+
11158
 
+use strict;
11159
 
+
11160
 
+use Test::Builder;
11161
 
+
11162
 
+require Exporter;
11163
 
+our @ISA = qw(Exporter);
11164
 
+
11165
 
+our $VERSION = '0.92';
11166
 
+$VERSION = eval $VERSION;      ## no critic (BuiltinFunctions::ProhibitStringyEval)
11167
 
+
11168
 
+# 5.004's Exporter doesn't have export_to_level.
11169
 
+my $_export_to_level = sub {
11170
 
+    my $pkg   = shift;
11171
 
+    my $level = shift;
11172
 
+    (undef) = shift;    # redundant arg
11173
 
+    my $callpkg = caller($level);
11174
 
+    $pkg->export( $callpkg, @_ );
11175
 
+};
11176
 
+
11177
 
+#line 82
11178
 
+
11179
 
+sub import {
11180
 
+    my($class) = shift;
11181
 
+
11182
 
+    # Don't run all this when loading ourself.
11183
 
+    return 1 if $class eq 'Test::Builder::Module';
11184
 
+
11185
 
+    my $test = $class->builder;
11186
 
+
11187
 
+    my $caller = caller;
11188
 
+
11189
 
+    $test->exported_to($caller);
11190
 
+
11191
 
+    $class->import_extra( \@_ );
11192
 
+    my(@imports) = $class->_strip_imports( \@_ );
11193
 
+
11194
 
+    $test->plan(@_);
11195
 
+
11196
 
+    $class->$_export_to_level( 1, $class, @imports );
11197
 
+}
11198
 
+
11199
 
+sub _strip_imports {
11200
 
+    my $class = shift;
11201
 
+    my $list  = shift;
11202
 
+
11203
 
+    my @imports = ();
11204
 
+    my @other   = ();
11205
 
+    my $idx     = 0;
11206
 
+    while( $idx <= $#{$list} ) {
11207
 
+        my $item = $list->[$idx];
11208
 
+
11209
 
+        if( defined $item and $item eq 'import' ) {
11210
 
+            push @imports, @{ $list->[ $idx + 1 ] };
11211
 
+            $idx++;
11212
 
+        }
11213
 
+        else {
11214
 
+            push @other, $item;
11215
 
+        }
11216
 
+
11217
 
+        $idx++;
11218
 
+    }
11219
 
+
11220
 
+    @$list = @other;
11221
 
+
11222
 
+    return @imports;
11223
 
+}
11224
 
+
11225
 
+#line 145
11226
 
+
11227
 
+sub import_extra { }
11228
 
+
11229
 
+#line 175
11230
 
+
11231
 
+sub builder {
11232
 
+    return Test::Builder->new;
11233
 
+}
11234
 
+
11235
 
+1;
11236
 
Index: 0.8/modules/nginx-echo/test/inc/Test/More.pm
11237
 
===================================================================
11238
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
11239
 
+++ 0.8/modules/nginx-echo/test/inc/Test/More.pm        2010-10-31 07:35:47.678338000 +0000
11240
 
@@ -0,0 +1,735 @@
11241
 
+#line 1
11242
 
+package Test::More;
11243
 
+
11244
 
+use 5.006;
11245
 
+use strict;
11246
 
+use warnings;
11247
 
+
11248
 
+#---- perlcritic exemptions. ----#
11249
 
+
11250
 
+# We use a lot of subroutine prototypes
11251
 
+## no critic (Subroutines::ProhibitSubroutinePrototypes)
11252
 
+
11253
 
+# Can't use Carp because it might cause use_ok() to accidentally succeed
11254
 
+# even though the module being used forgot to use Carp.  Yes, this
11255
 
+# actually happened.
11256
 
+sub _carp {
11257
 
+    my( $file, $line ) = ( caller(1) )[ 1, 2 ];
11258
 
+    return warn @_, " at $file line $line\n";
11259
 
+}
11260
 
+
11261
 
+our $VERSION = '0.92';
11262
 
+$VERSION = eval $VERSION;    ## no critic (BuiltinFunctions::ProhibitStringyEval)
11263
 
+
11264
 
+use Test::Builder::Module;
11265
 
+our @ISA    = qw(Test::Builder::Module);
11266
 
+our @EXPORT = qw(ok use_ok require_ok
11267
 
+  is isnt like unlike is_deeply
11268
 
+  cmp_ok
11269
 
+  skip todo todo_skip
11270
 
+  pass fail
11271
 
+  eq_array eq_hash eq_set
11272
 
+  $TODO
11273
 
+  plan
11274
 
+  done_testing
11275
 
+  can_ok isa_ok new_ok
11276
 
+  diag note explain
11277
 
+  BAIL_OUT
11278
 
+);
11279
 
+
11280
 
+#line 163
11281
 
+
11282
 
+sub plan {
11283
 
+    my $tb = Test::More->builder;
11284
 
+
11285
 
+    return $tb->plan(@_);
11286
 
+}
11287
 
+
11288
 
+# This implements "use Test::More 'no_diag'" but the behavior is
11289
 
+# deprecated.
11290
 
+sub import_extra {
11291
 
+    my $class = shift;
11292
 
+    my $list  = shift;
11293
 
+
11294
 
+    my @other = ();
11295
 
+    my $idx   = 0;
11296
 
+    while( $idx <= $#{$list} ) {
11297
 
+        my $item = $list->[$idx];
11298
 
+
11299
 
+        if( defined $item and $item eq 'no_diag' ) {
11300
 
+            $class->builder->no_diag(1);
11301
 
+        }
11302
 
+        else {
11303
 
+            push @other, $item;
11304
 
+        }
11305
 
+
11306
 
+        $idx++;
11307
 
+    }
11308
 
+
11309
 
+    @$list = @other;
11310
 
+
11311
 
+    return;
11312
 
+}
11313
 
+
11314
 
+#line 216
11315
 
+
11316
 
+sub done_testing {
11317
 
+    my $tb = Test::More->builder;
11318
 
+    $tb->done_testing(@_);
11319
 
+}
11320
 
+
11321
 
+#line 289
11322
 
+
11323
 
+sub ok ($;$) {
11324
 
+    my( $test, $name ) = @_;
11325
 
+    my $tb = Test::More->builder;
11326
 
+
11327
 
+    return $tb->ok( $test, $name );
11328
 
+}
11329
 
+
11330
 
+#line 367
11331
 
+
11332
 
+sub is ($$;$) {
11333
 
+    my $tb = Test::More->builder;
11334
 
+
11335
 
+    return $tb->is_eq(@_);
11336
 
+}
11337
 
+
11338
 
+sub isnt ($$;$) {
11339
 
+    my $tb = Test::More->builder;
11340
 
+
11341
 
+    return $tb->isnt_eq(@_);
11342
 
+}
11343
 
+
11344
 
+*isn't = \&isnt;
11345
 
+
11346
 
+#line 411
11347
 
+
11348
 
+sub like ($$;$) {
11349
 
+    my $tb = Test::More->builder;
11350
 
+
11351
 
+    return $tb->like(@_);
11352
 
+}
11353
 
+
11354
 
+#line 426
11355
 
+
11356
 
+sub unlike ($$;$) {
11357
 
+    my $tb = Test::More->builder;
11358
 
+
11359
 
+    return $tb->unlike(@_);
11360
 
+}
11361
 
+
11362
 
+#line 471
11363
 
+
11364
 
+sub cmp_ok($$$;$) {
11365
 
+    my $tb = Test::More->builder;
11366
 
+
11367
 
+    return $tb->cmp_ok(@_);
11368
 
+}
11369
 
+
11370
 
+#line 506
11371
 
+
11372
 
+sub can_ok ($@) {
11373
 
+    my( $proto, @methods ) = @_;
11374
 
+    my $class = ref $proto || $proto;
11375
 
+    my $tb = Test::More->builder;
11376
 
+
11377
 
+    unless($class) {
11378
 
+        my $ok = $tb->ok( 0, "->can(...)" );
11379
 
+        $tb->diag('    can_ok() called with empty class or reference');
11380
 
+        return $ok;
11381
 
+    }
11382
 
+
11383
 
+    unless(@methods) {
11384
 
+        my $ok = $tb->ok( 0, "$class->can(...)" );
11385
 
+        $tb->diag('    can_ok() called with no methods');
11386
 
+        return $ok;
11387
 
+    }
11388
 
+
11389
 
+    my @nok = ();
11390
 
+    foreach my $method (@methods) {
11391
 
+        $tb->_try( sub { $proto->can($method) } ) or push @nok, $method;
11392
 
+    }
11393
 
+
11394
 
+    my $name = (@methods == 1) ? "$class->can('$methods[0]')" :
11395
 
+                                 "$class->can(...)"           ;
11396
 
+
11397
 
+    my $ok = $tb->ok( !@nok, $name );
11398
 
+
11399
 
+    $tb->diag( map "    $class->can('$_') failed\n", @nok );
11400
 
+
11401
 
+    return $ok;
11402
 
+}
11403
 
+
11404
 
+#line 572
11405
 
+
11406
 
+sub isa_ok ($$;$) {
11407
 
+    my( $object, $class, $obj_name ) = @_;
11408
 
+    my $tb = Test::More->builder;
11409
 
+
11410
 
+    my $diag;
11411
 
+
11412
 
+    if( !defined $object ) {
11413
 
+        $obj_name = 'The thing' unless defined $obj_name;
11414
 
+        $diag = "$obj_name isn't defined";
11415
 
+    }
11416
 
+    else {
11417
 
+        my $whatami = ref $object ? 'object' : 'class';
11418
 
+        # We can't use UNIVERSAL::isa because we want to honor isa() overrides
11419
 
+        my( $rslt, $error ) = $tb->_try( sub { $object->isa($class) } );
11420
 
+        if($error) {
11421
 
+            if( $error =~ /^Can't call method "isa" on unblessed reference/ ) {
11422
 
+                # Its an unblessed reference
11423
 
+                $obj_name = 'The reference' unless defined $obj_name;
11424
 
+                if( !UNIVERSAL::isa( $object, $class ) ) {
11425
 
+                    my $ref = ref $object;
11426
 
+                    $diag = "$obj_name isn't a '$class' it's a '$ref'";
11427
 
+                }
11428
 
+            }
11429
 
+            elsif( $error =~ /Can't call method "isa" without a package/ ) {
11430
 
+                # It's something that can't even be a class
11431
 
+                $diag = "$obj_name isn't a class or reference";
11432
 
+            }
11433
 
+            else {
11434
 
+                die <<WHOA;
11435
 
+WHOA! I tried to call ->isa on your $whatami and got some weird error.
11436
 
+Here's the error.
11437
 
+$error
11438
 
+WHOA
11439
 
+            }
11440
 
+        }
11441
 
+        else {
11442
 
+            $obj_name = "The $whatami" unless defined $obj_name;
11443
 
+            if( !$rslt ) {
11444
 
+                my $ref = ref $object;
11445
 
+                $diag = "$obj_name isn't a '$class' it's a '$ref'";
11446
 
+            }
11447
 
+        }
11448
 
+    }
11449
 
+
11450
 
+    my $name = "$obj_name isa $class";
11451
 
+    my $ok;
11452
 
+    if($diag) {
11453
 
+        $ok = $tb->ok( 0, $name );
11454
 
+        $tb->diag("    $diag\n");
11455
 
+    }
11456
 
+    else {
11457
 
+        $ok = $tb->ok( 1, $name );
11458
 
+    }
11459
 
+
11460
 
+    return $ok;
11461
 
+}
11462
 
+
11463
 
+#line 650
11464
 
+
11465
 
+sub new_ok {
11466
 
+    my $tb = Test::More->builder;
11467
 
+    $tb->croak("new_ok() must be given at least a class") unless @_;
11468
 
+
11469
 
+    my( $class, $args, $object_name ) = @_;
11470
 
+
11471
 
+    $args ||= [];
11472
 
+    $object_name = "The object" unless defined $object_name;
11473
 
+
11474
 
+    my $obj;
11475
 
+    my( $success, $error ) = $tb->_try( sub { $obj = $class->new(@$args); 1 } );
11476
 
+    if($success) {
11477
 
+        local $Test::Builder::Level = $Test::Builder::Level + 1;
11478
 
+        isa_ok $obj, $class, $object_name;
11479
 
+    }
11480
 
+    else {
11481
 
+        $tb->ok( 0, "new() died" );
11482
 
+        $tb->diag("    Error was:  $error");
11483
 
+    }
11484
 
+
11485
 
+    return $obj;
11486
 
+}
11487
 
+
11488
 
+#line 690
11489
 
+
11490
 
+sub pass (;$) {
11491
 
+    my $tb = Test::More->builder;
11492
 
+
11493
 
+    return $tb->ok( 1, @_ );
11494
 
+}
11495
 
+
11496
 
+sub fail (;$) {
11497
 
+    my $tb = Test::More->builder;
11498
 
+
11499
 
+    return $tb->ok( 0, @_ );
11500
 
+}
11501
 
+
11502
 
+#line 753
11503
 
+
11504
 
+sub use_ok ($;@) {
11505
 
+    my( $module, @imports ) = @_;
11506
 
+    @imports = () unless @imports;
11507
 
+    my $tb = Test::More->builder;
11508
 
+
11509
 
+    my( $pack, $filename, $line ) = caller;
11510
 
+
11511
 
+    my $code;
11512
 
+    if( @imports == 1 and $imports[0] =~ /^\d+(?:\.\d+)?$/ ) {
11513
 
+        # probably a version check.  Perl needs to see the bare number
11514
 
+        # for it to work with non-Exporter based modules.
11515
 
+        $code = <<USE;
11516
 
+package $pack;
11517
 
+use $module $imports[0];
11518
 
+1;
11519
 
+USE
11520
 
+    }
11521
 
+    else {
11522
 
+        $code = <<USE;
11523
 
+package $pack;
11524
 
+use $module \@{\$args[0]};
11525
 
+1;
11526
 
+USE
11527
 
+    }
11528
 
+
11529
 
+    my( $eval_result, $eval_error ) = _eval( $code, \@imports );
11530
 
+    my $ok = $tb->ok( $eval_result, "use $module;" );
11531
 
+
11532
 
+    unless($ok) {
11533
 
+        chomp $eval_error;
11534
 
+        $@ =~ s{^BEGIN failed--compilation aborted at .*$}
11535
 
+                {BEGIN failed--compilation aborted at $filename line $line.}m;
11536
 
+        $tb->diag(<<DIAGNOSTIC);
11537
 
+    Tried to use '$module'.
11538
 
+    Error:  $eval_error
11539
 
+DIAGNOSTIC
11540
 
+
11541
 
+    }
11542
 
+
11543
 
+    return $ok;
11544
 
+}
11545
 
+
11546
 
+sub _eval {
11547
 
+    my( $code, @args ) = @_;
11548
 
+
11549
 
+    # Work around oddities surrounding resetting of $@ by immediately
11550
 
+    # storing it.
11551
 
+    my( $sigdie, $eval_result, $eval_error );
11552
 
+    {
11553
 
+        local( $@, $!, $SIG{__DIE__} );    # isolate eval
11554
 
+        $eval_result = eval $code;              ## no critic (BuiltinFunctions::ProhibitStringyEval)
11555
 
+        $eval_error  = $@;
11556
 
+        $sigdie      = $SIG{__DIE__} || undef;
11557
 
+    }
11558
 
+    # make sure that $code got a chance to set $SIG{__DIE__}
11559
 
+    $SIG{__DIE__} = $sigdie if defined $sigdie;
11560
 
+
11561
 
+    return( $eval_result, $eval_error );
11562
 
+}
11563
 
+
11564
 
+#line 822
11565
 
+
11566
 
+sub require_ok ($) {
11567
 
+    my($module) = shift;
11568
 
+    my $tb = Test::More->builder;
11569
 
+
11570
 
+    my $pack = caller;
11571
 
+
11572
 
+    # Try to deterine if we've been given a module name or file.
11573
 
+    # Module names must be barewords, files not.
11574
 
+    $module = qq['$module'] unless _is_module_name($module);
11575
 
+
11576
 
+    my $code = <<REQUIRE;
11577
 
+package $pack;
11578
 
+require $module;
11579
 
+1;
11580
 
+REQUIRE
11581
 
+
11582
 
+    my( $eval_result, $eval_error ) = _eval($code);
11583
 
+    my $ok = $tb->ok( $eval_result, "require $module;" );
11584
 
+
11585
 
+    unless($ok) {
11586
 
+        chomp $eval_error;
11587
 
+        $tb->diag(<<DIAGNOSTIC);
11588
 
+    Tried to require '$module'.
11589
 
+    Error:  $eval_error
11590
 
+DIAGNOSTIC
11591
 
+
11592
 
+    }
11593
 
+
11594
 
+    return $ok;
11595
 
+}
11596
 
+
11597
 
+sub _is_module_name {
11598
 
+    my $module = shift;
11599
 
+
11600
 
+    # Module names start with a letter.
11601
 
+    # End with an alphanumeric.
11602
 
+    # The rest is an alphanumeric or ::
11603
 
+    $module =~ s/\b::\b//g;
11604
 
+
11605
 
+    return $module =~ /^[a-zA-Z]\w*$/ ? 1 : 0;
11606
 
+}
11607
 
+
11608
 
+#line 899
11609
 
+
11610
 
+our( @Data_Stack, %Refs_Seen );
11611
 
+my $DNE = bless [], 'Does::Not::Exist';
11612
 
+
11613
 
+sub _dne {
11614
 
+    return ref $_[0] eq ref $DNE;
11615
 
+}
11616
 
+
11617
 
+## no critic (Subroutines::RequireArgUnpacking)
11618
 
+sub is_deeply {
11619
 
+    my $tb = Test::More->builder;
11620
 
+
11621
 
+    unless( @_ == 2 or @_ == 3 ) {
11622
 
+        my $msg = <<'WARNING';
11623
 
+is_deeply() takes two or three args, you gave %d.
11624
 
+This usually means you passed an array or hash instead
11625
 
+of a reference to it
11626
 
+WARNING
11627
 
+        chop $msg;    # clip off newline so carp() will put in line/file
11628
 
+
11629
 
+        _carp sprintf $msg, scalar @_;
11630
 
+
11631
 
+        return $tb->ok(0);
11632
 
+    }
11633
 
+
11634
 
+    my( $got, $expected, $name ) = @_;
11635
 
+
11636
 
+    $tb->_unoverload_str( \$expected, \$got );
11637
 
+
11638
 
+    my $ok;
11639
 
+    if( !ref $got and !ref $expected ) {    # neither is a reference
11640
 
+        $ok = $tb->is_eq( $got, $expected, $name );
11641
 
+    }
11642
 
+    elsif( !ref $got xor !ref $expected ) {    # one's a reference, one isn't
11643
 
+        $ok = $tb->ok( 0, $name );
11644
 
+        $tb->diag( _format_stack({ vals => [ $got, $expected ] }) );
11645
 
+    }
11646
 
+    else {                                     # both references
11647
 
+        local @Data_Stack = ();
11648
 
+        if( _deep_check( $got, $expected ) ) {
11649
 
+            $ok = $tb->ok( 1, $name );
11650
 
+        }
11651
 
+        else {
11652
 
+            $ok = $tb->ok( 0, $name );
11653
 
+            $tb->diag( _format_stack(@Data_Stack) );
11654
 
+        }
11655
 
+    }
11656
 
+
11657
 
+    return $ok;
11658
 
+}
11659
 
+
11660
 
+sub _format_stack {
11661
 
+    my(@Stack) = @_;
11662
 
+
11663
 
+    my $var       = '$FOO';
11664
 
+    my $did_arrow = 0;
11665
 
+    foreach my $entry (@Stack) {
11666
 
+        my $type = $entry->{type} || '';
11667
 
+        my $idx = $entry->{'idx'};
11668
 
+        if( $type eq 'HASH' ) {
11669
 
+            $var .= "->" unless $did_arrow++;
11670
 
+            $var .= "{$idx}";
11671
 
+        }
11672
 
+        elsif( $type eq 'ARRAY' ) {
11673
 
+            $var .= "->" unless $did_arrow++;
11674
 
+            $var .= "[$idx]";
11675
 
+        }
11676
 
+        elsif( $type eq 'REF' ) {
11677
 
+            $var = "\${$var}";
11678
 
+        }
11679
 
+    }
11680
 
+
11681
 
+    my @vals = @{ $Stack[-1]{vals} }[ 0, 1 ];
11682
 
+    my @vars = ();
11683
 
+    ( $vars[0] = $var ) =~ s/\$FOO/     \$got/;
11684
 
+    ( $vars[1] = $var ) =~ s/\$FOO/\$expected/;
11685
 
+
11686
 
+    my $out = "Structures begin differing at:\n";
11687
 
+    foreach my $idx ( 0 .. $#vals ) {
11688
 
+        my $val = $vals[$idx];
11689
 
+        $vals[$idx]
11690
 
+          = !defined $val ? 'undef'
11691
 
+          : _dne($val)    ? "Does not exist"
11692
 
+          : ref $val      ? "$val"
11693
 
+          :                 "'$val'";
11694
 
+    }
11695
 
+
11696
 
+    $out .= "$vars[0] = $vals[0]\n";
11697
 
+    $out .= "$vars[1] = $vals[1]\n";
11698
 
+
11699
 
+    $out =~ s/^/    /msg;
11700
 
+    return $out;
11701
 
+}
11702
 
+
11703
 
+sub _type {
11704
 
+    my $thing = shift;
11705
 
+
11706
 
+    return '' if !ref $thing;
11707
 
+
11708
 
+    for my $type (qw(ARRAY HASH REF SCALAR GLOB CODE Regexp)) {
11709
 
+        return $type if UNIVERSAL::isa( $thing, $type );
11710
 
+    }
11711
 
+
11712
 
+    return '';
11713
 
+}
11714
 
+
11715
 
+#line 1059
11716
 
+
11717
 
+sub diag {
11718
 
+    return Test::More->builder->diag(@_);
11719
 
+}
11720
 
+
11721
 
+sub note {
11722
 
+    return Test::More->builder->note(@_);
11723
 
+}
11724
 
+
11725
 
+#line 1085
11726
 
+
11727
 
+sub explain {
11728
 
+    return Test::More->builder->explain(@_);
11729
 
+}
11730
 
+
11731
 
+#line 1151
11732
 
+
11733
 
+## no critic (Subroutines::RequireFinalReturn)
11734
 
+sub skip {
11735
 
+    my( $why, $how_many ) = @_;
11736
 
+    my $tb = Test::More->builder;
11737
 
+
11738
 
+    unless( defined $how_many ) {
11739
 
+        # $how_many can only be avoided when no_plan is in use.
11740
 
+        _carp "skip() needs to know \$how_many tests are in the block"
11741
 
+          unless $tb->has_plan eq 'no_plan';
11742
 
+        $how_many = 1;
11743
 
+    }
11744
 
+
11745
 
+    if( defined $how_many and $how_many =~ /\D/ ) {
11746
 
+        _carp
11747
 
+          "skip() was passed a non-numeric number of tests.  Did you get the arguments backwards?";
11748
 
+        $how_many = 1;
11749
 
+    }
11750
 
+
11751
 
+    for( 1 .. $how_many ) {
11752
 
+        $tb->skip($why);
11753
 
+    }
11754
 
+
11755
 
+    no warnings 'exiting';
11756
 
+    last SKIP;
11757
 
+}
11758
 
+
11759
 
+#line 1238
11760
 
+
11761
 
+sub todo_skip {
11762
 
+    my( $why, $how_many ) = @_;
11763
 
+    my $tb = Test::More->builder;
11764
 
+
11765
 
+    unless( defined $how_many ) {
11766
 
+        # $how_many can only be avoided when no_plan is in use.
11767
 
+        _carp "todo_skip() needs to know \$how_many tests are in the block"
11768
 
+          unless $tb->has_plan eq 'no_plan';
11769
 
+        $how_many = 1;
11770
 
+    }
11771
 
+
11772
 
+    for( 1 .. $how_many ) {
11773
 
+        $tb->todo_skip($why);
11774
 
+    }
11775
 
+
11776
 
+    no warnings 'exiting';
11777
 
+    last TODO;
11778
 
+}
11779
 
+
11780
 
+#line 1293
11781
 
+
11782
 
+sub BAIL_OUT {
11783
 
+    my $reason = shift;
11784
 
+    my $tb     = Test::More->builder;
11785
 
+
11786
 
+    $tb->BAIL_OUT($reason);
11787
 
+}
11788
 
+
11789
 
+#line 1332
11790
 
+
11791
 
+#'#
11792
 
+sub eq_array {
11793
 
+    local @Data_Stack = ();
11794
 
+    _deep_check(@_);
11795
 
+}
11796
 
+
11797
 
+sub _eq_array {
11798
 
+    my( $a1, $a2 ) = @_;
11799
 
+
11800
 
+    if( grep _type($_) ne 'ARRAY', $a1, $a2 ) {
11801
 
+        warn "eq_array passed a non-array ref";
11802
 
+        return 0;
11803
 
+    }
11804
 
+
11805
 
+    return 1 if $a1 eq $a2;
11806
 
+
11807
 
+    my $ok = 1;
11808
 
+    my $max = $#$a1 > $#$a2 ? $#$a1 : $#$a2;
11809
 
+    for( 0 .. $max ) {
11810
 
+        my $e1 = $_ > $#$a1 ? $DNE : $a1->[$_];
11811
 
+        my $e2 = $_ > $#$a2 ? $DNE : $a2->[$_];
11812
 
+
11813
 
+        push @Data_Stack, { type => 'ARRAY', idx => $_, vals => [ $e1, $e2 ] };
11814
 
+        $ok = _deep_check( $e1, $e2 );
11815
 
+        pop @Data_Stack if $ok;
11816
 
+
11817
 
+        last unless $ok;
11818
 
+    }
11819
 
+
11820
 
+    return $ok;
11821
 
+}
11822
 
+
11823
 
+sub _deep_check {
11824
 
+    my( $e1, $e2 ) = @_;
11825
 
+    my $tb = Test::More->builder;
11826
 
+
11827
 
+    my $ok = 0;
11828
 
+
11829
 
+    # Effectively turn %Refs_Seen into a stack.  This avoids picking up
11830
 
+    # the same referenced used twice (such as [\$a, \$a]) to be considered
11831
 
+    # circular.
11832
 
+    local %Refs_Seen = %Refs_Seen;
11833
 
+
11834
 
+    {
11835
 
+        # Quiet uninitialized value warnings when comparing undefs.
11836
 
+        no warnings 'uninitialized';
11837
 
+
11838
 
+        $tb->_unoverload_str( \$e1, \$e2 );
11839
 
+
11840
 
+        # Either they're both references or both not.
11841
 
+        my $same_ref = !( !ref $e1 xor !ref $e2 );
11842
 
+        my $not_ref = ( !ref $e1 and !ref $e2 );
11843
 
+
11844
 
+        if( defined $e1 xor defined $e2 ) {
11845
 
+            $ok = 0;
11846
 
+        }
11847
 
+        elsif( !defined $e1 and !defined $e2 ) {
11848
 
+            # Shortcut if they're both defined.
11849
 
+            $ok = 1;
11850
 
+        }
11851
 
+        elsif( _dne($e1) xor _dne($e2) ) {
11852
 
+            $ok = 0;
11853
 
+        }
11854
 
+        elsif( $same_ref and( $e1 eq $e2 ) ) {
11855
 
+            $ok = 1;
11856
 
+        }
11857
 
+        elsif($not_ref) {
11858
 
+            push @Data_Stack, { type => '', vals => [ $e1, $e2 ] };
11859
 
+            $ok = 0;
11860
 
+        }
11861
 
+        else {
11862
 
+            if( $Refs_Seen{$e1} ) {
11863
 
+                return $Refs_Seen{$e1} eq $e2;
11864
 
+            }
11865
 
+            else {
11866
 
+                $Refs_Seen{$e1} = "$e2";
11867
 
+            }
11868
 
+
11869
 
+            my $type = _type($e1);
11870
 
+            $type = 'DIFFERENT' unless _type($e2) eq $type;
11871
 
+
11872
 
+            if( $type eq 'DIFFERENT' ) {
11873
 
+                push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] };
11874
 
+                $ok = 0;
11875
 
+            }
11876
 
+            elsif( $type eq 'ARRAY' ) {
11877
 
+                $ok = _eq_array( $e1, $e2 );
11878
 
+            }
11879
 
+            elsif( $type eq 'HASH' ) {
11880
 
+                $ok = _eq_hash( $e1, $e2 );
11881
 
+            }
11882
 
+            elsif( $type eq 'REF' ) {
11883
 
+                push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] };
11884
 
+                $ok = _deep_check( $$e1, $$e2 );
11885
 
+                pop @Data_Stack if $ok;
11886
 
+            }
11887
 
+            elsif( $type eq 'SCALAR' ) {
11888
 
+                push @Data_Stack, { type => 'REF', vals => [ $e1, $e2 ] };
11889
 
+                $ok = _deep_check( $$e1, $$e2 );
11890
 
+                pop @Data_Stack if $ok;
11891
 
+            }
11892
 
+            elsif($type) {
11893
 
+                push @Data_Stack, { type => $type, vals => [ $e1, $e2 ] };
11894
 
+                $ok = 0;
11895
 
+            }
11896
 
+            else {
11897
 
+                _whoa( 1, "No type in _deep_check" );
11898
 
+            }
11899
 
+        }
11900
 
+    }
11901
 
+
11902
 
+    return $ok;
11903
 
+}
11904
 
+
11905
 
+sub _whoa {
11906
 
+    my( $check, $desc ) = @_;
11907
 
+    if($check) {
11908
 
+        die <<"WHOA";
11909
 
+WHOA!  $desc
11910
 
+This should never happen!  Please contact the author immediately!
11911
 
+WHOA
11912
 
+    }
11913
 
+}
11914
 
+
11915
 
+#line 1465
11916
 
+
11917
 
+sub eq_hash {
11918
 
+    local @Data_Stack = ();
11919
 
+    return _deep_check(@_);
11920
 
+}
11921
 
+
11922
 
+sub _eq_hash {
11923
 
+    my( $a1, $a2 ) = @_;
11924
 
+
11925
 
+    if( grep _type($_) ne 'HASH', $a1, $a2 ) {
11926
 
+        warn "eq_hash passed a non-hash ref";
11927
 
+        return 0;
11928
 
+    }
11929
 
+
11930
 
+    return 1 if $a1 eq $a2;
11931
 
+
11932
 
+    my $ok = 1;
11933
 
+    my $bigger = keys %$a1 > keys %$a2 ? $a1 : $a2;
11934
 
+    foreach my $k ( keys %$bigger ) {
11935
 
+        my $e1 = exists $a1->{$k} ? $a1->{$k} : $DNE;
11936
 
+        my $e2 = exists $a2->{$k} ? $a2->{$k} : $DNE;
11937
 
+
11938
 
+        push @Data_Stack, { type => 'HASH', idx => $k, vals => [ $e1, $e2 ] };
11939
 
+        $ok = _deep_check( $e1, $e2 );
11940
 
+        pop @Data_Stack if $ok;
11941
 
+
11942
 
+        last unless $ok;
11943
 
+    }
11944
 
+
11945
 
+    return $ok;
11946
 
+}
11947
 
+
11948
 
+#line 1522
11949
 
+
11950
 
+sub eq_set {
11951
 
+    my( $a1, $a2 ) = @_;
11952
 
+    return 0 unless @$a1 == @$a2;
11953
 
+
11954
 
+    no warnings 'uninitialized';
11955
 
+
11956
 
+    # It really doesn't matter how we sort them, as long as both arrays are
11957
 
+    # sorted with the same algorithm.
11958
 
+    #
11959
 
+    # Ensure that references are not accidentally treated the same as a
11960
 
+    # string containing the reference.
11961
 
+    #
11962
 
+    # Have to inline the sort routine due to a threading/sort bug.
11963
 
+    # See [rt.cpan.org 6782]
11964
 
+    #
11965
 
+    # I don't know how references would be sorted so we just don't sort
11966
 
+    # them.  This means eq_set doesn't really work with refs.
11967
 
+    return eq_array(
11968
 
+        [ grep( ref, @$a1 ), sort( grep( !ref, @$a1 ) ) ],
11969
 
+        [ grep( ref, @$a2 ), sort( grep( !ref, @$a2 ) ) ],
11970
 
+    );
11971
 
+}
11972
 
+
11973
 
+#line 1735
11974
 
+
11975
 
+1;
11976
 
Index: 0.8/modules/nginx-echo/test/valgrind.suppress
11977
 
===================================================================
11978
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
11979
 
+++ 0.8/modules/nginx-echo/test/valgrind.suppress       2010-10-31 07:35:23.648338001 +0000
11980
 
@@ -0,0 +1,88 @@
11981
 
+{
11982
 
+   init_request_leak
11983
 
+   Memcheck:Leak
11984
 
+   fun:memalign
11985
 
+   fun:posix_memalign
11986
 
+   fun:ngx_memalign
11987
 
+   fun:ngx_palloc_block
11988
 
+   fun:ngx_palloc
11989
 
+   fun:ngx_pcalloc
11990
 
+   fun:ngx_create_temp_buf
11991
 
+   fun:ngx_http_init_request
11992
 
+   fun:ngx_epoll_process_events
11993
 
+   fun:ngx_process_events_and_timers
11994
 
+   fun:ngx_single_process_cycle
11995
 
+   fun:main
11996
 
+}
11997
 
+{
11998
 
+   nginx-core-process-init
11999
 
+   Memcheck:Leak
12000
 
+   fun:malloc
12001
 
+   fun:ngx_alloc
12002
 
+   fun:ngx_event_process_init
12003
 
+   fun:ngx_single_process_cycle
12004
 
+   fun:main
12005
 
+}
12006
 
+{
12007
 
+   nginx-core-crc32-init
12008
 
+   Memcheck:Leak
12009
 
+   fun:malloc
12010
 
+   fun:ngx_alloc
12011
 
+   fun:ngx_crc32_table_init
12012
 
+   fun:main
12013
 
+}
12014
 
+{
12015
 
+   palloc_large_for_init_request
12016
 
+   Memcheck:Leak
12017
 
+   fun:malloc
12018
 
+   fun:ngx_alloc
12019
 
+   fun:ngx_palloc_large
12020
 
+   fun:ngx_palloc
12021
 
+   fun:ngx_pcalloc
12022
 
+   fun:ngx_http_init_request
12023
 
+   fun:ngx_epoll_process_events
12024
 
+   fun:ngx_process_events_and_timers
12025
 
+   fun:ngx_single_process_cycle
12026
 
+   fun:main
12027
 
+}
12028
 
+{
12029
 
+   palloc_large_for_create_temp_buf
12030
 
+   Memcheck:Leak
12031
 
+   fun:malloc
12032
 
+   fun:ngx_alloc
12033
 
+   fun:ngx_palloc_large
12034
 
+   fun:ngx_palloc
12035
 
+   fun:ngx_create_temp_buf
12036
 
+   fun:ngx_http_init_request
12037
 
+   fun:ngx_epoll_process_events
12038
 
+   fun:ngx_process_events_and_timers
12039
 
+   fun:ngx_single_process_cycle
12040
 
+   fun:main
12041
 
+}
12042
 
+{
12043
 
+   accept_create_pool
12044
 
+   Memcheck:Leak
12045
 
+   fun:memalign
12046
 
+   fun:posix_memalign
12047
 
+   fun:ngx_memalign
12048
 
+   fun:ngx_create_pool
12049
 
+   fun:ngx_event_accept
12050
 
+   fun:ngx_epoll_process_events
12051
 
+   fun:ngx_process_events_and_timers
12052
 
+   fun:ngx_single_process_cycle
12053
 
+   fun:main
12054
 
+}
12055
 
+{
12056
 
+   create_pool_for_init_req
12057
 
+   Memcheck:Leak
12058
 
+   fun:memalign
12059
 
+   fun:posix_memalign
12060
 
+   fun:ngx_memalign
12061
 
+   fun:ngx_create_pool
12062
 
+   fun:ngx_http_init_request
12063
 
+   fun:ngx_epoll_process_events
12064
 
+   fun:ngx_process_events_and_timers
12065
 
+   fun:ngx_single_process_cycle
12066
 
+   fun:main
12067
 
+}
12068
 
+
12069
 
Index: 0.8/modules/nginx-echo/util/build.sh
12070
 
===================================================================
12071
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
12072
 
+++ 0.8/modules/nginx-echo/util/build.sh        2010-10-31 07:35:23.648338001 +0000
12073
 
@@ -0,0 +1,66 @@
12074
 
+#!/bin/bash
12075
 
+
12076
 
+# this file is mostly meant to be used by the author himself.
12077
 
+
12078
 
+root=`pwd`
12079
 
+cd ~/work
12080
 
+version=$1
12081
 
+opts=$2
12082
 
+home=~
12083
 
+
12084
 
+if [ ! -s "nginx-$version.tar.gz" ]; then
12085
 
+    wget "http://sysoev.ru/nginx/nginx-$version.tar.gz" -O nginx-$version.tar.gz || exit 1
12086
 
+    tar -xzvf nginx-$version.tar.gz || exit 1
12087
 
+    if [ "$version" = "0.8.41" ]; then
12088
 
+        cp $root/../no-pool-nginx/nginx-$version-no_pool.patch ./
12089
 
+        patch -p0 < nginx-$version-no_pool.patch || exit 1
12090
 
+    fi
12091
 
+fi
12092
 
+
12093
 
+#tar -xzvf nginx-$version.tar.gz || exit 1
12094
 
+#cp $root/../no-pool-nginx/nginx-0.8.41-no_pool.patch ./
12095
 
+#patch -p0 < nginx-0.8.41-no_pool.patch || exit 1
12096
 
+
12097
 
+cd nginx-$version/
12098
 
+if [[ "$BUILD_CLEAN" -eq 1 || ! -f Makefile \
12099
 
+        || "$root/config" -nt Makefile
12100
 
+        || "$root/util/build.sh" -nt Makefile ]]; then
12101
 
+    ./configure --prefix=/opt/nginx \
12102
 
+            --with-cc-opt="-DDEBUG_MALLOC" \
12103
 
+            --with-http_stub_status_module \
12104
 
+            --without-mail_pop3_module \
12105
 
+            --without-mail_imap_module \
12106
 
+            --without-mail_smtp_module \
12107
 
+            --without-http_upstream_ip_hash_module \
12108
 
+            --without-http_empty_gif_module \
12109
 
+            --without-http_memcached_module \
12110
 
+            --without-http_referer_module \
12111
 
+            --without-http_autoindex_module \
12112
 
+            --without-http_auth_basic_module \
12113
 
+            --without-http_userid_module \
12114
 
+          --with-http_addition_module \
12115
 
+          --add-module=$root/../ndk-nginx-module \
12116
 
+          --add-module=$root/../set-misc-nginx-module \
12117
 
+          --add-module=$root/../eval-nginx-module \
12118
 
+          --add-module=$root/../xss-nginx-module \
12119
 
+          --add-module=$root/../rds-json-nginx-module \
12120
 
+          --add-module=$root/../headers-more-nginx-module \
12121
 
+          --add-module=$root $opts \
12122
 
+          --with-debug
12123
 
+          #--add-module=$root/../lz-session-nginx-module \
12124
 
+          #--add-module=$home/work/ndk \
12125
 
+          #--add-module=$home/work/ndk/examples/http/set_var \
12126
 
+          #--add-module=$root/../eval-nginx-module \
12127
 
+          #--add-module=/home/agentz/work/nginx_eval_module-1.0.1 \
12128
 
+  #--without-http_ssi_module  # we cannot disable ssi because echo_location_async depends on it (i dunno why?!)
12129
 
+
12130
 
+fi
12131
 
+if [ -f /opt/nginx/sbin/nginx ]; then
12132
 
+    rm -f /opt/nginx/sbin/nginx
12133
 
+fi
12134
 
+if [ -f /opt/nginx/logs/nginx.pid ]; then
12135
 
+    kill `cat /opt/nginx/logs/nginx.pid`
12136
 
+fi
12137
 
+make -j3
12138
 
+make install
12139
 
+
12140
 
Index: 0.8/modules/nginx-echo/util/releng
12141
 
===================================================================
12142
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
12143
 
+++ 0.8/modules/nginx-echo/util/releng  2010-10-31 07:35:23.648338001 +0000
12144
 
@@ -0,0 +1,7 @@
12145
 
+#!/bin/bash
12146
 
+
12147
 
+./update-readme
12148
 
+ack '(?<=\#define)\s*DDEBUG\s*[12]' src
12149
 
+echo =======================================
12150
 
+ack '(?<=This document describes echo-nginx-module v)\d+\.\d+' README
12151
 
+
12152
 
Index: 0.8/modules/nginx-echo/util/update-readme.sh
12153
 
===================================================================
12154
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
12155
 
+++ 0.8/modules/nginx-echo/util/update-readme.sh        2010-10-31 07:35:23.648338001 +0000
12156
 
@@ -0,0 +1,6 @@
12157
 
+#!/bin/bash
12158
 
+
12159
 
+perl util/wiki2pod.pl doc/manpage.wiki > /tmp/a.pod \
12160
 
+    && pod2text /tmp/a.pod > README \
12161
 
+    && perl -i -pe 's{(https?://.*?)>}{$1 >}g' README
12162
 
+
12163
 
Index: 0.8/modules/nginx-echo/util/wiki2pod.pl
12164
 
===================================================================
12165
 
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
12166
 
+++ 0.8/modules/nginx-echo/util/wiki2pod.pl     2010-10-31 07:35:23.648338001 +0000
12167
 
@@ -0,0 +1,131 @@
12168
 
+#!/usr/bin/env perl
12169
 
+
12170
 
+use strict;
12171
 
+use warnings;
12172
 
+use bytes;
12173
 
+
12174
 
+my @nl_counts;
12175
 
+my $last_nl_count_level;
12176
 
+
12177
 
+my @bl_counts;
12178
 
+my $last_bl_count_level;
12179
 
+
12180
 
+sub fmt_pos ($) {
12181
 
+    (my $s = $_[0]) =~ s{\#(.*)}{/"$1"};
12182
 
+    $s;
12183
 
+}
12184
 
+
12185
 
+sub fmt_mark ($$) {
12186
 
+    my ($tag, $s) = @_;
12187
 
+    my $max_level = 0;
12188
 
+    while ($s =~ /([<>])\1*/g) {
12189
 
+        my $level = length $&;
12190
 
+        if ($level > $max_level) {
12191
 
+            $max_level = $level;
12192
 
+        }
12193
 
+    }
12194
 
+
12195
 
+    my $times = $max_level + 1;
12196
 
+    if ($times > 1) {
12197
 
+        $s = " $s ";
12198
 
+    }
12199
 
+    return $tag . ('<' x $times) . $s . ('>' x $times);
12200
 
+}
12201
 
+
12202
 
+print "=encoding utf-8\n\n";
12203
 
+
12204
 
+while (<>) {
12205
 
+    if ($. == 1) {
12206
 
+        # strip the leading U+FEFF byte in MS-DOS text files
12207
 
+        my $first = ord(substr($_, 0, 1));
12208
 
+        #printf STDERR "0x%x", $first;
12209
 
+        #my $second = ord(substr($_, 2, 1));
12210
 
+        #printf STDERR "0x%x", $second;
12211
 
+        if ($first == 0xEF) {
12212
 
+            substr($_, 0, 1, '');
12213
 
+            #warn "Hit!";
12214
 
+        }
12215
 
+    }
12216
 
+    s{\[(http[^ \]]+) ([^\]]*)\]}{$2 (L<$1>)}gi;
12217
 
+    s{ \[\[ ( [^\]\|]+ ) \| ([^\]]*) \]\] }{"L<$2|" . fmt_pos($1) . ">"}gixe;
12218
 
+    s{<code>(.*?)</code>}{fmt_mark('C', $1)}gie;
12219
 
+    s{'''(.*?)'''}{fmt_mark('B', $1)}ge;
12220
 
+    s{''(.*?)''}{fmt_mark('I', $1)}ge;
12221
 
+    if (s{^\s*<[^>]+>\s*$}{}) {
12222
 
+        next;
12223
 
+    }
12224
 
+
12225
 
+    if (/^\s*$/) {
12226
 
+        print "\n";
12227
 
+        next;
12228
 
+    }
12229
 
+
12230
 
+=begin cmt
12231
 
+
12232
 
+    if ($. == 1) {
12233
 
+        warn $_;
12234
 
+        for my $i (0..length($_) - 1) {
12235
 
+            my $chr = substr($_, $i, 1);
12236
 
+            warn "chr ord($i): ".ord($chr)." \"$chr\"\n";
12237
 
+        }
12238
 
+    }
12239
 
+
12240
 
+=end cmt
12241
 
+=cut
12242
 
+
12243
 
+    if (/(=+) (.*) \1$/) {
12244
 
+        #warn "HERE! $_" if $. == 1;
12245
 
+        my ($level, $title) = (length $1, $2);
12246
 
+        collapse_lists();
12247
 
+
12248
 
+        print "\n=head$level $title\n\n";
12249
 
+    } elsif (/^(\#+) (.*)/) {
12250
 
+        my ($level, $txt) = (length($1) - 1, $2);
12251
 
+        if (defined $last_nl_count_level && $level != $last_nl_count_level) {
12252
 
+            print "\n=back\n\n";
12253
 
+        }
12254
 
+        $last_nl_count_level = $level;
12255
 
+        $nl_counts[$level] ||= 0;
12256
 
+        if ($nl_counts[$level] == 0) {
12257
 
+            print "\n=over\n\n";
12258
 
+        }
12259
 
+        $nl_counts[$level]++;
12260
 
+        print "\n=item $nl_counts[$level].\n\n";
12261
 
+        print "$txt\n";
12262
 
+    } elsif (/^(\*+) (.*)/) {
12263
 
+        my ($level, $txt) = (length($1) - 1, $2);
12264
 
+        if (defined $last_bl_count_level && $level != $last_bl_count_level) {
12265
 
+            print "\n=back\n\n";
12266
 
+        }
12267
 
+        $last_bl_count_level = $level;
12268
 
+        $bl_counts[$level] ||= 0;
12269
 
+        if ($bl_counts[$level] == 0) {
12270
 
+            print "\n=over\n\n";
12271
 
+        }
12272
 
+        $bl_counts[$level]++;
12273
 
+        print "\n=item *\n\n";
12274
 
+        print "$txt\n";
12275
 
+    } else {
12276
 
+        collapse_lists();
12277
 
+        print;
12278
 
+    }
12279
 
+}
12280
 
+
12281
 
+collapse_lists();
12282
 
+
12283
 
+sub collapse_lists {
12284
 
+    while (defined $last_nl_count_level && $last_nl_count_level >= 0) {
12285
 
+        print "\n=back\n\n";
12286
 
+        $last_nl_count_level--;
12287
 
+    }
12288
 
+    undef $last_nl_count_level;
12289
 
+    undef @nl_counts;
12290
 
+
12291
 
+    while (defined $last_bl_count_level && $last_bl_count_level >= 0) {
12292
 
+        print "\n=back\n\n";
12293
 
+        $last_bl_count_level--;
12294
 
+    }
12295
 
+    undef $last_bl_count_level;
12296
 
+    undef @bl_counts;
12297
 
+}
12298
 
+