rack 1.3.6 → 1.3.7

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

Potentially problematic release.


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

data/COPYING CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007, 2008, 2009, 2010 Christian Neukirchen <purl.org/net/chneukirchen>
1
+ Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012 Christian Neukirchen <purl.org/net/chneukirchen>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to
data/KNOWN-ISSUES CHANGED
@@ -1,3 +1,12 @@
1
+ = Known issues with Rack and ECMA-262
2
+
3
+ * Many users expect the escape() function defined in ECMA-262 to be compatible
4
+ with URI. Confusion is especially strong because the documentation for the
5
+ escape function includes a reference to the URI specifications. ECMA-262
6
+ escape is not however a URI escape function, it is a javascript escape
7
+ function, and is not fully compatible. Most notably, for characters outside of
8
+ the BMP. Users should use the more correct encodeURI functions.
9
+
1
10
  = Known issues with Rack and Web servers
2
11
 
3
12
  * Lighttpd sets wrong SCRIPT_NAME and PATH_INFO if you mount your
data/README.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- = Rack, a modular Ruby webserver interface
1
+ = Rack, a modular Ruby webserver interface {<img src="https://secure.travis-ci.org/rack/rack.png" alt="Build Status" />}[http://travis-ci.org/rack/rack] {<img src="https://gemnasium.com/rack/rack.png" alt="Dependency Status" />}[https://gemnasium.com/rack/rack]
2
2
 
3
3
  Rack provides a minimal, modular and adaptable interface for developing
4
4
  web applications in Ruby. By wrapping HTTP requests and responses in
@@ -27,8 +27,11 @@ These web servers include Rack handlers in their distributions:
27
27
  * Fuzed
28
28
  * Glassfish v3
29
29
  * Phusion Passenger (which is mod_rack for Apache and for nginx)
30
+ * Puma
30
31
  * Rainbows!
31
32
  * Unicorn
33
+ * unixrack
34
+ * uWSGI
32
35
  * Zbatery
33
36
 
34
37
  Any valid Rack app will run the same on all these handlers, without
@@ -132,7 +135,11 @@ at my site:
132
135
 
133
136
  Testing Rack requires the bacon testing framework:
134
137
 
135
- gem install bacon
138
+ bundle install --without extra # to be able to run the fast tests
139
+
140
+ Or:
141
+
142
+ bundle install # this assumes that you have installed native extensions!
136
143
 
137
144
  There are two rake-based test tasks:
138
145
 
@@ -373,10 +380,99 @@ run on port 11211) and memcache-client installed.
373
380
  * October 17, 2011: Twentieth public release 1.3.5
374
381
  * Fix annoying warnings caused by the backport in 1.3.4
375
382
 
376
- * December 28th, 2011: Twenty third public release: 1.3.6
383
+ * December 28th, 2011: Twenty first public release: 1.1.3.
377
384
  * Security fix. http://www.ocert.org/advisories/ocert-2011-003.html
378
385
  Further information here: http://jruby.org/2011/12/27/jruby-1-6-5-1
379
386
 
387
+ * December 28th, 2011: Twenty fourth public release 1.4.0
388
+ * Ruby 1.8.6 support has officially been dropped. Not all tests pass.
389
+ * Raise sane error messages for broken config.ru
390
+ * Allow combining run and map in a config.ru
391
+ * Rack::ContentType will not set Content-Type for responses without a body
392
+ * Status code 205 does not send a response body
393
+ * Rack::Response::Helpers will not rely on instance variables
394
+ * Rack::Utils.build_query no longer outputs '=' for nil query values
395
+ * Various mime types added
396
+ * Rack::MockRequest now supports HEAD
397
+ * Rack::Directory now supports files that contain RFC3986 reserved chars
398
+ * Rack::File now only supports GET and HEAD requests
399
+ * Rack::Server#start now passes the block to Rack::Handler::<h>#run
400
+ * Rack::Static now supports an index option
401
+ * Added the Teapot status code
402
+ * rackup now defaults to Thin instead of Mongrel (if installed)
403
+ * Support added for HTTP_X_FORWARDED_SCHEME
404
+ * Numerous bug fixes, including many fixes for new and alternate rubies
405
+
406
+ * January 22nd, 2012: Twenty fifth public release 1.4.1
407
+ * Alter the keyspace limit calculations to reduce issues with nested params
408
+ * Add a workaround for multipart parsing where files contain unescaped "%"
409
+ * Added Rack::Response::Helpers#method_not_allowed? (code 405)
410
+ * Rack::File now returns 404 for illegal directory traversals
411
+ * Rack::File now returns 405 for illegal methods (non HEAD/GET)
412
+ * Rack::Cascade now catches 405 by default, as well as 404
413
+ * Cookies missing '--' no longer cause an exception to be raised
414
+ * Various style changes and documentation spelling errors
415
+ * Rack::BodyProxy always ensures to execute its block
416
+ * Additional test coverage around cookies and secrets
417
+ * Rack::Session::Cookie can now be supplied either secret or old_secret
418
+ * Tests are no longer dependent on set order
419
+ * Rack::Static no longer defaults to serving index files
420
+ * Rack.release was fixed
421
+
422
+ * January 6th, 2013: Twenty sixth public release 1.1.4
423
+ * Add warnings when users do not provide a session secret
424
+
425
+ * January 6th, 2013: Twenty seventh public release 1.2.6
426
+ * Add warnings when users do not provide a session secret
427
+ * Fix parsing performance for unquoted filenames
428
+
429
+ * January 6th, 2013: Twenty eighth public release 1.3.7
430
+ * Add warnings when users do not provide a session secret
431
+ * Fix parsing performance for unquoted filenames
432
+ * Updated URI backports
433
+ * Fix URI backport version matching, and silence constant warnings
434
+ * Correct parameter parsing with empty values
435
+ * Correct rackup '-I' flag, to allow multiple uses
436
+ * Correct rackup pidfile handling
437
+ * Report rackup line numbers correctly
438
+ * Fix request loops caused by non-stale nonces with time limits
439
+ * Fix reloader on Windows
440
+ * Prevent infinite recursions from Response#to_ary
441
+ * Various middleware better conforms to the body close specification
442
+ * Updated language for the body close specification
443
+ * Additional notes regarding ECMA escape compatibility issues
444
+ * Fix the parsing of multiple ranges in range headers
445
+
446
+ * January 6th, 2013: Twenty ninth public release 1.4.2
447
+ * Add warnings when users do not provide a session secret
448
+ * Fix parsing performance for unquoted filenames
449
+ * Updated URI backports
450
+ * Fix URI backport version matching, and silence constant warnings
451
+ * Correct parameter parsing with empty values
452
+ * Correct rackup '-I' flag, to allow multiple uses
453
+ * Correct rackup pidfile handling
454
+ * Report rackup line numbers correctly
455
+ * Fix request loops caused by non-stale nonces with time limits
456
+ * Fix reloader on Windows
457
+ * Prevent infinite recursions from Response#to_ary
458
+ * Various middleware better conforms to the body close specification
459
+ * Updated language for the body close specification
460
+ * Additional notes regarding ECMA escape compatibility issues
461
+ * Fix the parsing of multiple ranges in range headers
462
+ * Prevent errors from empty parameter keys
463
+ * Added PATCH verb to Rack::Request
464
+ * Various documentation updates
465
+ * Fix session merge semantics (fixes rack-test)
466
+ * Rack::Static :index can now handle multiple directories
467
+ * All tests now utilize Rack::Lint (special thanks to Lars Gierth)
468
+ * Rack::File cache_control parameter is now deprecated, and removed by 1.5
469
+ * Correct Rack::Directory script name escaping
470
+ * Rack::Static supports header rules for sophisticated configurations
471
+ * Multipart parsing now works without a Content-Length header
472
+ * New logos courtesy of Zachary Scott!
473
+ * Rack::BodyProxy now explicitly defines #each, useful for C extensions
474
+ * Cookies that are not URI escaped no longer cause exceptions
475
+
380
476
  == Contact
381
477
 
382
478
  Please post bugs, suggestions and patches to
@@ -457,7 +553,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
457
553
 
458
554
  == Links
459
555
 
460
- Rack:: <http://rack.rubyforge.org/>
556
+ Rack:: <http://rack.github.com/>
461
557
  Official Rack repositories:: <http://github.com/rack>
462
558
  Rack Bug Tracking:: <http://github.com/rack/rack/issues>
463
559
  rack-devel mailing list:: <http://groups.google.com/group/rack-devel>
data/SPEC CHANGED
@@ -156,7 +156,9 @@ The Body must respond to +each+
156
156
  and must only yield String values.
157
157
  The Body itself should not be an instance of String, as this will
158
158
  break in Ruby 1.9.
159
- If the Body responds to +close+, it will be called after iteration.
159
+ If the Body responds to +close+, it will be called after iteration. If
160
+ the body is replaced by a middleware after action, the original body
161
+ must be closed first, if it repsonds to close.
160
162
  If the Body responds to +to_path+, it must return a String
161
163
  identifying the location of a file whose contents are identical
162
164
  to that produced by calling +each+; this may be used by the
data/contrib/rack.png ADDED
Binary file
data/contrib/rack.svg ADDED
@@ -0,0 +1,150 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Created with Inkscape (http://www.inkscape.org/) -->
3
+
4
+ <svg
5
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
6
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
7
+ xmlns:cc="http://creativecommons.org/ns#"
8
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
9
+ xmlns:svg="http://www.w3.org/2000/svg"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
12
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13
+ width="744.09448819"
14
+ height="1052.3622047"
15
+ id="svg2"
16
+ version="1.1"
17
+ inkscape:version="0.48.3.1 r9886"
18
+ sodipodi:docname="rack.svg">
19
+ <defs
20
+ id="defs4">
21
+ <linearGradient
22
+ id="linearGradient3837"
23
+ osb:paint="solid">
24
+ <stop
25
+ style="stop-color:#000000;stop-opacity:1;"
26
+ offset="0"
27
+ id="stop3839" />
28
+ </linearGradient>
29
+ </defs>
30
+ <sodipodi:namedview
31
+ id="base"
32
+ pagecolor="#ffffff"
33
+ bordercolor="#666666"
34
+ borderopacity="1.0"
35
+ inkscape:pageopacity="0.0"
36
+ inkscape:pageshadow="2"
37
+ inkscape:zoom="0.98994949"
38
+ inkscape:cx="230.49849"
39
+ inkscape:cy="656.46253"
40
+ inkscape:document-units="px"
41
+ inkscape:current-layer="layer1"
42
+ showgrid="false"
43
+ showguides="false"
44
+ inkscape:guide-bbox="true"
45
+ inkscape:window-width="1920"
46
+ inkscape:window-height="1056"
47
+ inkscape:window-x="1920"
48
+ inkscape:window-y="24"
49
+ inkscape:window-maximized="1">
50
+ <sodipodi:guide
51
+ orientation="1,0"
52
+ position="645.99255,757.10933"
53
+ id="guide2995" />
54
+ <sodipodi:guide
55
+ orientation="1,0"
56
+ position="488.40876,686.90373"
57
+ id="guide2997" />
58
+ <sodipodi:guide
59
+ orientation="1,0"
60
+ position="176.7767,748.52304"
61
+ id="guide2999" />
62
+ <sodipodi:guide
63
+ orientation="1,0"
64
+ position="355.71429,782.85714"
65
+ id="guide3005" />
66
+ <sodipodi:guide
67
+ orientation="0,1"
68
+ position="527.14286,642.85714"
69
+ id="guide3007" />
70
+ <sodipodi:guide
71
+ orientation="0,1"
72
+ position="431.42857,507.85714"
73
+ id="guide3009" />
74
+ <sodipodi:guide
75
+ orientation="0,1"
76
+ position="488.40876,783.57143"
77
+ id="guide3011" />
78
+ <sodipodi:guide
79
+ orientation="0,1"
80
+ position="505,372.85714"
81
+ id="guide3013" />
82
+ </sodipodi:namedview>
83
+ <metadata
84
+ id="metadata7">
85
+ <rdf:RDF>
86
+ <cc:Work
87
+ rdf:about="">
88
+ <dc:format>image/svg+xml</dc:format>
89
+ <dc:type
90
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
91
+ <dc:title></dc:title>
92
+ </cc:Work>
93
+ </rdf:RDF>
94
+ </metadata>
95
+ <g
96
+ inkscape:label="Layer 1"
97
+ inkscape:groupmode="layer"
98
+ id="layer1">
99
+ <path
100
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans;stroke-opacity:1;stroke-miterlimit:30;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round"
101
+ d="m 176.28125,201.03125 0,0.625 0,395.125 0,0.375 0.34375,0.0937 312.34375,91.6875 0.625,0.1875 0,-0.65625 0.125,-419.09375 0,-0.40625 -0.40625,-0.0937 -312.4375,-67.71875 -0.59375,-0.125 z m 1,1.21875 311.4375,67.5 -0.125,418.0625 -311.3125,-91.375 0,-394.1875 z"
102
+ id="path2985"
103
+ inkscape:connector-curvature="0" />
104
+ <path
105
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans;stroke-opacity:1;stroke-miterlimit:30;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round"
106
+ d="m 647.21875,206.59375 -0.6875,0.28125 -157.59375,62.21875 -0.3125,0.125 0,0.34375 0.1875,419.125 0,0.75 0.6875,-0.28125 156.0625,-63.1875 0.3125,-0.125 0,-0.34375 1.34375,-418.15625 0,-0.75 z m -1,1.4375 -1.34375,417.125 -155.0625,62.78125 -0.1875,-418.03125 156.59375,-61.875 z"
107
+ id="path2993"
108
+ inkscape:connector-curvature="0" />
109
+ <path
110
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans;stroke-opacity:1;stroke-miterlimit:30;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round"
111
+ d="m 355.6875,137.40625 -0.15625,0.0625 L 176.96875,201.0625 177.3125,202 355.75,138.4375 646.78125,207.53125 647,206.5625 l -291.15625,-69.125 -0.15625,-0.0312 z"
112
+ id="path3003"
113
+ inkscape:connector-curvature="0" />
114
+ <path
115
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans;stroke-opacity:1;stroke-miterlimit:30;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round"
116
+ d="m 355.71875,277.53125 -0.125,0.0312 -178.9375,47.96875 -1.8125,0.46875 1.8125,0.5 311.625,83.5 0.15625,0.0312 0.125,-0.0625 157.59375,-53.65625 1.5625,-0.53125 -1.59375,-0.4375 -290.28125,-77.78125 -0.125,-0.0312 z m 0,1.03125 L 644.3125,355.90625 488.375,409 178.71875,326 l 177,-47.4375 z"
117
+ id="path3015"
118
+ inkscape:connector-curvature="0" />
119
+ <path
120
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans;stroke-opacity:1;stroke-miterlimit:30;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round"
121
+ d="m 355.21875,240.9375 0,37.125 1,0 0,-37.125 -1,0 z"
122
+ id="path3017"
123
+ inkscape:connector-curvature="0" />
124
+ <path
125
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans;stroke-opacity:1;stroke-miterlimit:30;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round"
126
+ d="m 176.28125,202.65625 0,393.28125 1,0 0,-393.28125 -1,0 z"
127
+ id="path3019"
128
+ inkscape:connector-curvature="0" />
129
+ <path
130
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans;stroke-opacity:1;stroke-miterlimit:30;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round"
131
+ d="m 355.71875,409 -0.125,0.0312 L 177,455.8125 l -1.78125,0.46875 1.78125,0.5 L 488.28125,545 l 0.15625,0.0312 0.125,-0.0625 156.71875,-56.25 1.46875,-0.53125 -1.53125,-0.40625 -289.375,-78.75 -0.125,-0.0312 z m 0,1.03125 287.6875,78.28125 L 488.375,544 179.03125,456.3125 355.71875,410.03125 z"
132
+ id="path3021"
133
+ inkscape:connector-curvature="0" />
134
+ <path
135
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans;stroke-opacity:1;stroke-miterlimit:30;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round"
136
+ d="m 355.71875,544 -0.15625,0.0312 -178.5625,52.3125 0.28125,0.96875 178.4375,-52.28125 289.59375,80.25 0.28125,-0.96875 -289.75,-80.28125 -0.125,-0.0312 z"
137
+ id="path3023"
138
+ inkscape:connector-curvature="0" />
139
+ <path
140
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans;stroke-opacity:1;stroke-miterlimit:30;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round"
141
+ d="m 355.21875,374.34375 0,35.15625 1,0 0,-35.15625 -1,0 z"
142
+ id="path3025"
143
+ inkscape:connector-curvature="0" />
144
+ <path
145
+ style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans;stroke-opacity:1;stroke-miterlimit:30;stroke-dasharray:none;stroke-linecap:round;stroke-linejoin:round"
146
+ d="m 355.1875,507.03125 0,37.4375 1.03125,0 0,-37.4375 -1.03125,0 z"
147
+ id="path3027"
148
+ inkscape:connector-curvature="0" />
149
+ </g>
150
+ </svg>
@@ -41,7 +41,7 @@ module Rack
41
41
 
42
42
  class Request < Auth::AbstractRequest
43
43
  def basic?
44
- :basic == scheme
44
+ !parts.first.nil? && :basic == scheme
45
45
  end
46
46
 
47
47
  def credentials
@@ -38,7 +38,7 @@ module Rack
38
38
  end
39
39
 
40
40
  def stale?
41
- !self.class.time_limit.nil? && (@timestamp - Time.now.to_i) < self.class.time_limit
41
+ !self.class.time_limit.nil? && (Time.now.to_i - @timestamp) > self.class.time_limit
42
42
  end
43
43
 
44
44
  def fresh?
@@ -8,7 +8,21 @@
8
8
 
9
9
  module URI
10
10
  TBLENCWWWCOMP_ = {} # :nodoc:
11
+ 256.times do |i|
12
+ TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
13
+ end
14
+ TBLENCWWWCOMP_[' '] = '+'
15
+ TBLENCWWWCOMP_.freeze
11
16
  TBLDECWWWCOMP_ = {} # :nodoc:
17
+ 256.times do |i|
18
+ h, l = i>>4, i&15
19
+ TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
20
+ TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
21
+ TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
22
+ TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
23
+ end
24
+ TBLDECWWWCOMP_['+'] = ' '
25
+ TBLDECWWWCOMP_.freeze
12
26
 
13
27
  # Encode given +s+ to URL-encoded form data.
14
28
  #
@@ -26,18 +40,6 @@ module URI
26
40
  '%' + $1.unpack('H2' * Rack::Utils.bytesize($1)).join('%').upcase
27
41
  end.tr(' ', '+')
28
42
  else
29
- if TBLENCWWWCOMP_.empty?
30
- tbl = {}
31
- 256.times do |i|
32
- tbl[i.chr] = '%%%02X' % i
33
- end
34
- tbl[' '] = '+'
35
- begin
36
- TBLENCWWWCOMP_.replace(tbl)
37
- TBLENCWWWCOMP_.freeze
38
- rescue
39
- end
40
- end
41
43
  str.gsub(/[^*\-.0-9A-Z_a-z]/) {|m| TBLENCWWWCOMP_[m]}
42
44
  end
43
45
  end
@@ -48,22 +50,6 @@ module URI
48
50
  #
49
51
  # See URI.encode_www_form_component, URI.decode_www_form
50
52
  def self.decode_www_form_component(str, enc=nil)
51
- if TBLDECWWWCOMP_.empty?
52
- tbl = {}
53
- 256.times do |i|
54
- h, l = i>>4, i&15
55
- tbl['%%%X%X' % [h, l]] = i.chr
56
- tbl['%%%x%X' % [h, l]] = i.chr
57
- tbl['%%%X%x' % [h, l]] = i.chr
58
- tbl['%%%x%x' % [h, l]] = i.chr
59
- end
60
- tbl['+'] = ' '
61
- begin
62
- TBLDECWWWCOMP_.replace(tbl)
63
- TBLDECWWWCOMP_.freeze
64
- rescue
65
- end
66
- end
67
53
  raise ArgumentError, "invalid %-encoding (#{str})" unless /\A(?:%[0-9a-fA-F]{2}|[^%])*\z/ =~ str
68
54
  str.gsub(/\+|%[0-9a-fA-F]{2}/) {|m| TBLDECWWWCOMP_[m]}
69
55
  end
@@ -17,6 +17,19 @@
17
17
  require 'uri/common'
18
18
 
19
19
  module URI
20
+ TBLDECWWWCOMP_ = {} unless const_defined?(:TBLDECWWWCOMP_) #:nodoc:
21
+ if TBLDECWWWCOMP_.empty?
22
+ 256.times do |i|
23
+ h, l = i>>4, i&15
24
+ TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
25
+ TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
26
+ TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
27
+ TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
28
+ end
29
+ TBLDECWWWCOMP_['+'] = ' '
30
+ TBLDECWWWCOMP_.freeze
31
+ end
32
+
20
33
  def self.decode_www_form(str, enc=Encoding::UTF_8)
21
34
  return [] if str.empty?
22
35
  unless /\A#{WFKV_}=#{WFKV_}(?:[;&]#{WFKV_}=#{WFKV_})*\z/o =~ str
@@ -30,26 +43,10 @@ module URI
30
43
  end
31
44
 
32
45
  def self.decode_www_form_component(str, enc=Encoding::UTF_8)
33
- if TBLDECWWWCOMP_.empty?
34
- tbl = {}
35
- 256.times do |i|
36
- h, l = i>>4, i&15
37
- tbl['%%%X%X' % [h, l]] = i.chr
38
- tbl['%%%x%X' % [h, l]] = i.chr
39
- tbl['%%%X%x' % [h, l]] = i.chr
40
- tbl['%%%x%x' % [h, l]] = i.chr
41
- end
42
- tbl['+'] = ' '
43
- begin
44
- TBLDECWWWCOMP_.replace(tbl)
45
- TBLDECWWWCOMP_.freeze
46
- rescue
47
- end
48
- end
49
46
  raise ArgumentError, "invalid %-encoding (#{str})" unless /\A[^%]*(?:%\h\h[^%]*)*\z/ =~ str
50
47
  str.gsub(/\+|%\h\h/, TBLDECWWWCOMP_).force_encoding(enc)
51
48
  end
52
49
 
53
- remove_const :WFKV_
50
+ remove_const :WFKV_ if const_defined?(:WFKV_)
54
51
  WFKV_ = '(?:[^%#=;&]*(?:%\h\h[^%#=;&]*)*)' # :nodoc:
55
52
  end
@@ -0,0 +1,29 @@
1
+ # :stopdoc:
2
+
3
+ require 'uri/common'
4
+
5
+ # Issue:
6
+ # http://bugs.ruby-lang.org/issues/5925
7
+ #
8
+ # Relevant commit:
9
+ # https://github.com/ruby/ruby/commit/edb7cdf1eabaff78dfa5ffedfbc2e91b29fa9ca1
10
+
11
+ module URI
12
+ 256.times do |i|
13
+ TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
14
+ end
15
+ TBLENCWWWCOMP_[' '] = '+'
16
+ TBLENCWWWCOMP_.freeze
17
+
18
+ 256.times do |i|
19
+ h, l = i>>4, i&15
20
+ TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
21
+ TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
22
+ TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
23
+ TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
24
+ end
25
+ TBLDECWWWCOMP_['+'] = ' '
26
+ TBLDECWWWCOMP_.freeze
27
+ end
28
+
29
+ # :startdoc:
@@ -5,6 +5,7 @@ module Rack
5
5
  end
6
6
 
7
7
  def respond_to?(*args)
8
+ return false if args.first.to_s =~ /^to_ary$/
8
9
  super or @body.respond_to?(*args)
9
10
  end
10
11
 
@@ -19,7 +20,16 @@ module Rack
19
20
  @closed
20
21
  end
21
22
 
23
+ # N.B. This method is a special case to address the bug described by #434.
24
+ # We are applying this special case for #each only. Future bugs of this
25
+ # class will be handled by requesting users to patch their ruby
26
+ # implementation, to save adding too many methods in this class.
27
+ def each(*args, &block)
28
+ @body.each(*args, &block)
29
+ end
30
+
22
31
  def method_missing(*args, &block)
32
+ super if args.first.to_s =~ /^to_ary$/
23
33
  @body.__send__(*args, &block)
24
34
  end
25
35
  end
data/lib/rack/builder.rb CHANGED
@@ -38,7 +38,7 @@ module Rack
38
38
  end
39
39
  cfgfile.sub!(/^__END__\n.*/, '')
40
40
  app = eval "Rack::Builder.new {\n" + cfgfile + "\n}.to_app",
41
- TOPLEVEL_BINDING, config
41
+ TOPLEVEL_BINDING, config, 0
42
42
  else
43
43
  require config
44
44
  app = Object.const_get(::File.basename(config, '.rb').capitalize)
data/lib/rack/cascade.rb CHANGED
@@ -19,8 +19,19 @@ module Rack
19
19
  def call(env)
20
20
  result = NotFound
21
21
 
22
+ last_body = nil
23
+
22
24
  @apps.each do |app|
25
+ # The SPEC says that the body must be closed after it has been iterated
26
+ # by the server, or if it is replaced by a middleware action. Cascade
27
+ # replaces the body each time a cascade happens. It is assumed that nil
28
+ # does not respond to close, otherwise the previous application body
29
+ # will be closed. The final application body will not be closed, as it
30
+ # will be passed to the server as a result.
31
+ last_body.close if last_body.respond_to? :close
32
+
23
33
  result = app.call(env)
34
+ last_body = result[2]
24
35
  break unless @catch.include?(result[0].to_i)
25
36
  end
26
37
 
data/lib/rack/deflater.rb CHANGED
@@ -45,6 +45,7 @@ module Rack
45
45
  when "identity"
46
46
  [status, headers, body]
47
47
  when nil
48
+ body.close if body.respond_to?(:close)
48
49
  message = "An acceptable encoding for the requested resource #{request.fullpath} could not be found."
49
50
  [406, {"Content-Type" => "text/plain", "Content-Length" => message.length.to_s}, [message]]
50
51
  end
@@ -64,6 +65,7 @@ module Rack
64
65
  gzip.write(part)
65
66
  gzip.flush
66
67
  }
68
+ ensure
67
69
  @body.close if @body.respond_to?(:close)
68
70
  gzip.close
69
71
  @writer = nil
@@ -90,9 +92,11 @@ module Rack
90
92
  def each
91
93
  deflater = ::Zlib::Deflate.new(*DEFLATE_ARGS)
92
94
  @body.each { |part| yield deflater.deflate(part, Zlib::SYNC_FLUSH) }
93
- @body.close if @body.respond_to?(:close)
94
95
  yield deflater.finish
95
96
  nil
97
+ ensure
98
+ @body.close if @body.respond_to?(:close)
99
+ deflater.close
96
100
  end
97
101
  end
98
102
  end
data/lib/rack/head.rb CHANGED
@@ -9,6 +9,7 @@ class Head
9
9
  status, headers, body = @app.call(env)
10
10
 
11
11
  if env["REQUEST_METHOD"] == "HEAD"
12
+ body.close if body.respond_to? :close
12
13
  [status, headers, []]
13
14
  else
14
15
  [status, headers, body]
data/lib/rack/lint.rb CHANGED
@@ -528,7 +528,9 @@ module Rack
528
528
  ## The Body itself should not be an instance of String, as this will
529
529
  ## break in Ruby 1.9.
530
530
  ##
531
- ## If the Body responds to +close+, it will be called after iteration.
531
+ ## If the Body responds to +close+, it will be called after iteration. If
532
+ ## the body is replaced by a middleware after action, the original body
533
+ ## must be closed first, if it repsonds to close.
532
534
  # XXX howto: assert("Body has not been closed") { @closed }
533
535
 
534
536
 
@@ -12,7 +12,7 @@ module Rack
12
12
  MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
13
13
  TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
14
14
  CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
15
- DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})*/
15
+ DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})/
16
16
  RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i
17
17
  BROKEN_QUOTED = /^#{CONDISP}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i
18
18
  BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i
@@ -31,4 +31,4 @@ module Rack
31
31
  end
32
32
 
33
33
  end
34
- end
34
+ end
data/lib/rack/reloader.rb CHANGED
@@ -101,7 +101,7 @@ module Rack
101
101
  return unless file
102
102
  stat = ::File.stat(file)
103
103
  return file, stat if stat.file?
104
- rescue Errno::ENOENT, Errno::ENOTDIR
104
+ rescue Errno::ENOENT, Errno::ENOTDIR, Errno::ESRCH
105
105
  @cache.delete(file) and false
106
106
  end
107
107
  end
data/lib/rack/response.rb CHANGED
@@ -74,9 +74,10 @@ module Rack
74
74
  if [204, 304].include?(status.to_i)
75
75
  header.delete "Content-Type"
76
76
  header.delete "Content-Length"
77
+ close
77
78
  [status.to_i, header, []]
78
79
  else
79
- [status.to_i, header, self]
80
+ [status.to_i, header, BodyProxy.new(self){}]
80
81
  end
81
82
  end
82
83
  alias to_a finish # For *response
data/lib/rack/server.rb CHANGED
@@ -26,7 +26,7 @@ module Rack
26
26
 
27
27
  opts.on("-I", "--include PATH",
28
28
  "specify $LOAD_PATH (may be used more than once)") { |path|
29
- options[:include] = path.split(":")
29
+ (options[:include] ||= []).concat(path.split(":"))
30
30
  }
31
31
 
32
32
  opts.on("-r", "--require LIBRARY",
@@ -247,11 +247,14 @@ module Rack
247
247
  pp app
248
248
  end
249
249
 
250
+ check_pid! if options[:pid]
251
+
250
252
  # Touch the wrapped app, so that the config.ru is loaded before
251
253
  # daemonization (i.e. before chdir, etc).
252
254
  wrapped_app
253
255
 
254
256
  daemonize_app if options[:daemonize]
257
+
255
258
  write_pid if options[:pid]
256
259
 
257
260
  trap(:INT) do
@@ -274,7 +277,7 @@ module Rack
274
277
  options = default_options
275
278
 
276
279
  # Don't evaluate CGI ISINDEX parameters.
277
- # http://hoohoo.ncsa.uiuc.edu/cgi/cl.html
280
+ # http://www.meb.uni-bonn.de/docs/cgi/cl.html
278
281
  args.clear if ENV.include?("REQUEST_METHOD")
279
282
 
280
283
  options.merge! opt_parser.parse!(args)
@@ -319,5 +322,28 @@ module Rack
319
322
  ::File.open(options[:pid], 'w'){ |f| f.write("#{Process.pid}") }
320
323
  at_exit { ::File.delete(options[:pid]) if ::File.exist?(options[:pid]) }
321
324
  end
325
+
326
+ def check_pid!
327
+ case pidfile_process_status
328
+ when :running, :not_owned
329
+ $stderr.puts "A server is already running. Check #{options[:pid]}."
330
+ exit(1)
331
+ when :dead
332
+ ::File.delete(options[:pid])
333
+ end
334
+ end
335
+
336
+ def pidfile_process_status
337
+ return :exited unless ::File.exist?(options[:pid])
338
+
339
+ pid = ::File.read(options[:pid]).to_i
340
+ Process.kill(0, pid)
341
+ :running
342
+ rescue Errno::ESRCH
343
+ :dead
344
+ rescue Errno::EPERM
345
+ :not_owned
346
+ end
347
+
322
348
  end
323
349
  end
@@ -80,6 +80,15 @@ module Rack
80
80
 
81
81
  def initialize(app, options={})
82
82
  @secret = options[:secret]
83
+ warn <<-MSG unless @secret
84
+ SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
85
+ This poses a security threat. It is strongly recommended that you
86
+ provide a secret to prevent exploits that may be possible from crafted
87
+ cookies. This will not be supported in future versions of Rack, and
88
+ future versions will even invalidate your existing user cookies.
89
+
90
+ Called from: #{caller[0]}.
91
+ MSG
83
92
  @coder = options[:coder] ||= Base64::Marshal.new
84
93
  super(app, options.merge!(:cookie_only => true))
85
94
  end
data/lib/rack/utils.rb CHANGED
@@ -5,11 +5,14 @@ require 'tempfile'
5
5
  require 'rack/multipart'
6
6
 
7
7
  major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
8
+ ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'
8
9
 
9
10
  if major == 1 && minor < 9
10
11
  require 'rack/backports/uri/common_18'
11
- elsif major == 1 && minor == 9 && patch < 3
12
+ elsif major == 1 && minor == 9 && patch == 2 && RUBY_PATCHLEVEL <= 320 && RUBY_ENGINE != 'jruby'
12
13
  require 'rack/backports/uri/common_192'
14
+ elsif major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125
15
+ require 'rack/backports/uri/common_193'
13
16
  else
14
17
  require 'uri/common'
15
18
  end
@@ -60,6 +63,7 @@ module Rack
60
63
  bytes = 0
61
64
 
62
65
  (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
66
+ next if p.empty?
63
67
  k, v = p.split('=', 2).map { |x| unescape(x) }
64
68
 
65
69
  if k
@@ -315,16 +319,16 @@ module Rack
315
319
  def byte_ranges(env, size)
316
320
  # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
317
321
  http_range = env['HTTP_RANGE']
318
- return nil unless http_range
322
+ return nil unless http_range && http_range =~ /bytes=([^;]+)/
319
323
  ranges = []
320
- http_range.split(/,\s*/).each do |range_spec|
321
- matches = range_spec.match(/bytes=(\d*)-(\d*)/)
322
- return nil unless matches
323
- r0,r1 = matches[1], matches[2]
324
+ $1.split(/,\s*/).each do |range_spec|
325
+ return nil unless range_spec =~ /(\d*)-(\d*)/
326
+ r0,r1 = $1, $2
324
327
  if r0.empty?
325
328
  return nil if r1.empty?
326
329
  # suffix-byte-range-spec, represents trailing suffix of file
327
- r0 = [size - r1.to_i, 0].max
330
+ r0 = size - r1.to_i
331
+ r0 = 0 if r0 < 0
328
332
  r1 = size - 1
329
333
  else
330
334
  r0 = r0.to_i
data/rack.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "rack"
3
- s.version = "1.3.6"
3
+ s.version = "1.3.7"
4
4
  s.platform = Gem::Platform::RUBY
5
5
  s.summary = "a modular Ruby webserver interface"
6
6
 
@@ -11,7 +11,7 @@ the simplest way possible, it unifies and distills the API for web
11
11
  servers, web frameworks, and software in between (the so-called
12
12
  middleware) into a single method call.
13
13
 
14
- Also see http://rack.rubyforge.org.
14
+ Also see http://rack.github.com/.
15
15
  EOF
16
16
 
17
17
  s.files = Dir['{bin/*,contrib/*,example/*,lib/**/*,test/**/*}'] +
@@ -24,7 +24,7 @@ EOF
24
24
 
25
25
  s.author = 'Christian Neukirchen'
26
26
  s.email = 'chneukirchen@gmail.com'
27
- s.homepage = 'http://rack.rubyforge.org'
27
+ s.homepage = 'http://rack.github.com/'
28
28
  s.rubyforge_project = 'rack'
29
29
 
30
30
  s.add_development_dependency 'bacon'
@@ -0,0 +1 @@
1
+ run lambda{ |env| [200, {'Content-Type' => 'text/plain'}, [__LINE__.to_s]] }
@@ -63,6 +63,14 @@ describe Rack::Auth::Basic do
63
63
  end
64
64
  end
65
65
 
66
+ should 'return 400 Bad Request for a malformed authorization header' do
67
+ request 'HTTP_AUTHORIZATION' => '' do |response|
68
+ response.should.be.a.client_error
69
+ response.status.should.equal 400
70
+ response.should.not.include 'WWW-Authenticate'
71
+ end
72
+ end
73
+
66
74
  it 'takes realm as optional constructor arg' do
67
75
  app = Rack::Auth::Basic.new(unprotected_app, realm) { true }
68
76
  realm.should == app.realm
@@ -152,6 +152,20 @@ describe Rack::Auth::Digest::MD5 do
152
152
  end
153
153
  end
154
154
 
155
+ should 'not rechallenge if nonce is not stale' do
156
+ begin
157
+ Rack::Auth::Digest::Nonce.time_limit = 10
158
+
159
+ request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', :wait => 1 do |response|
160
+ response.status.should.equal 200
161
+ response.body.to_s.should.equal 'Hi Alice'
162
+ response.headers['WWW-Authenticate'].should.not =~ /\bstale=true\b/
163
+ end
164
+ ensure
165
+ Rack::Auth::Digest::Nonce.time_limit = nil
166
+ end
167
+ end
168
+
155
169
  should 'rechallenge with stale parameter if nonce is stale' do
156
170
  begin
157
171
  Rack::Auth::Digest::Nonce.time_limit = 1
@@ -45,4 +45,8 @@ describe Rack::BodyProxy do
45
45
  proxy.close
46
46
  closed.should.equal true
47
47
  end
48
+
49
+ should 'provide an #each method' do
50
+ Rack::BodyProxy.method_defined?(:each).should.equal true
51
+ end
48
52
  end
data/test/spec_builder.rb CHANGED
@@ -148,5 +148,11 @@ describe Rack::Builder do
148
148
  Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
149
149
  $:.pop
150
150
  end
151
+
152
+ it "sets __LINE__ correctly" do
153
+ app, options = Rack::Builder.parse_file config_file('line.ru')
154
+ options = nil # ignored, prevents warning
155
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal '1'
156
+ end
151
157
  end
152
158
  end
data/test/spec_cascade.rb CHANGED
@@ -42,4 +42,12 @@ describe Rack::Cascade do
42
42
  cascade << app3
43
43
  Rack::MockRequest.new(cascade).get('/foo').should.be.ok
44
44
  end
45
+
46
+ should "close the body on cascade" do
47
+ body = StringIO.new
48
+ closer = lambda { |env| [404, {}, body] }
49
+ cascade = Rack::Cascade.new([closer, app3], [404])
50
+ Rack::MockRequest.new(cascade).get("/foo").should.be.ok
51
+ body.should.be.closed
52
+ end
45
53
  end
data/test/spec_head.rb CHANGED
@@ -2,29 +2,41 @@ require 'rack/head'
2
2
  require 'rack/mock'
3
3
 
4
4
  describe Rack::Head do
5
+
5
6
  def test_response(headers = {})
6
- app = lambda { |env| [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]] }
7
+ body = StringIO.new "foo"
8
+ app = lambda do |env|
9
+ [200, {"Content-type" => "test/plain", "Content-length" => "3"}, body]
10
+ end
7
11
  request = Rack::MockRequest.env_for("/", headers)
8
12
  response = Rack::Head.new(app).call(request)
9
13
 
10
- return response
14
+ return response, body
11
15
  end
12
16
 
13
17
  should "pass GET, POST, PUT, DELETE, OPTIONS, TRACE requests" do
14
18
  %w[GET POST PUT DELETE OPTIONS TRACE].each do |type|
15
- resp = test_response("REQUEST_METHOD" => type)
19
+ resp, _ = test_response("REQUEST_METHOD" => type)
16
20
 
17
21
  resp[0].should.equal(200)
18
22
  resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
19
- resp[2].should.equal(["foo"])
23
+ resp[2].each { |b| b.should.equal("foo") }
20
24
  end
21
25
  end
22
26
 
23
27
  should "remove body from HEAD requests" do
24
- resp = test_response("REQUEST_METHOD" => "HEAD")
28
+ resp, _ = test_response("REQUEST_METHOD" => "HEAD")
29
+
30
+ resp[0].should.equal(200)
31
+ resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
32
+ resp[2].each { |b| flunk "body should be empty" }
33
+ end
25
34
 
35
+ should "close the body when it is removed" do
36
+ resp, body = test_response("REQUEST_METHOD" => "HEAD")
26
37
  resp[0].should.equal(200)
27
38
  resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
28
- resp[2].should.equal([])
39
+ resp[2].each { |b| flunk "body should be empty" }
40
+ body.should.be.closed
29
41
  end
30
42
  end
@@ -295,4 +295,24 @@ describe Rack::Multipart do
295
295
  message.should.equal "value must be a Hash"
296
296
  end
297
297
 
298
+ should "parse very long unquoted multipart file names" do
299
+ data = <<-EOF
300
+ --AaB03x\r
301
+ Content-Type: text/plain\r
302
+ Content-Disposition: attachment; name=file; filename=#{'long' * 100}\r
303
+ \r
304
+ contents\r
305
+ --AaB03x--\r
306
+ EOF
307
+
308
+ options = {
309
+ "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
310
+ "CONTENT_LENGTH" => data.length.to_s,
311
+ :input => StringIO.new(data)
312
+ }
313
+ env = Rack::MockRequest.env_for("/", options)
314
+ params = Rack::Utils::Multipart.parse_multipart(env)
315
+
316
+ params["file"][:filename].should.equal('long' * 100)
317
+ end
298
318
  end
@@ -250,4 +250,34 @@ describe Rack::Response do
250
250
  res.close
251
251
  res.body.should.be.closed
252
252
  end
253
+
254
+ it "calls close on #body when 204, 205, or 304" do
255
+ res = Rack::Response.new
256
+ res.body = StringIO.new
257
+ res.finish
258
+ res.body.should.not.be.closed
259
+
260
+ res.status = 204
261
+ _, _, b = res.finish
262
+ res.body.should.be.closed
263
+ b.should.not == res.body
264
+
265
+ res.body = StringIO.new
266
+ res.status = 205
267
+ _, _, b = res.finish
268
+ res.body.should.be.closed
269
+ b.should.not == res.body
270
+
271
+ res.body = StringIO.new
272
+ res.status = 304
273
+ _, _, b = res.finish
274
+ res.body.should.be.closed
275
+ b.should.not == res.body
276
+ end
277
+
278
+ it "wraps the body from #to_ary to prevent infinite loops" do
279
+ res = Rack::Response.new
280
+ res.finish.last.should.not.respond_to?(:to_ary)
281
+ lambda { res.finish.last.to_ary }.should.raise(NoMethodError)
282
+ end
253
283
  end
data/test/spec_server.rb CHANGED
@@ -10,6 +10,13 @@ describe Rack::Server do
10
10
  lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['success']] }
11
11
  end
12
12
 
13
+ def with_stderr
14
+ old, $stderr = $stderr, StringIO.new
15
+ yield $stderr
16
+ ensure
17
+ $stderr = old
18
+ end
19
+
13
20
  it "overrides :config if :app is passed in" do
14
21
  server = Rack::Server.new(:app => "FOO")
15
22
  server.app.should == "FOO"
@@ -71,4 +78,44 @@ describe Rack::Server do
71
78
  open(pidfile) { |f| f.read.should.eql $$.to_s }
72
79
  end
73
80
 
81
+ should "check pid file presence and running process" do
82
+ pidfile = Tempfile.open('pidfile') { |f| f.write($$); break f }.path
83
+ server = Rack::Server.new(:pid => pidfile)
84
+ server.send(:pidfile_process_status).should.eql :running
85
+ end
86
+
87
+ should "check pid file presence and dead process" do
88
+ dead_pid = `echo $$`.to_i
89
+ pidfile = Tempfile.open('pidfile') { |f| f.write(dead_pid); break f }.path
90
+ server = Rack::Server.new(:pid => pidfile)
91
+ server.send(:pidfile_process_status).should.eql :dead
92
+ end
93
+
94
+ should "check pid file presence and exited process" do
95
+ pidfile = Tempfile.open('pidfile') { |f| break f }.path
96
+ ::File.delete(pidfile)
97
+ server = Rack::Server.new(:pid => pidfile)
98
+ server.send(:pidfile_process_status).should.eql :exited
99
+ end
100
+
101
+ should "check pid file presence and not owned process" do
102
+ pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
103
+ server = Rack::Server.new(:pid => pidfile)
104
+ server.send(:pidfile_process_status).should.eql :not_owned
105
+ end
106
+
107
+ should "inform the user about existing pidfiles with running processes" do
108
+ pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
109
+ server = Rack::Server.new(:pid => pidfile)
110
+ with_stderr do |err|
111
+ should.raise(SystemExit) do
112
+ server.start
113
+ end
114
+ err.rewind
115
+ output = err.read
116
+ output.should.match(/already running/)
117
+ output.should.include? pidfile
118
+ end
119
+ end
120
+
74
121
  end
@@ -24,6 +24,17 @@ describe Rack::Session::Cookie do
24
24
  Rack::Response.new("Nothing").to_a
25
25
  end
26
26
 
27
+ before do
28
+ @warnings = warnings = []
29
+ Rack::Session::Cookie.class_eval do
30
+ define_method(:warn) { |m| warnings << m }
31
+ end
32
+ end
33
+
34
+ after do
35
+ Rack::Session::Cookie.class_eval { remove_method :warn }
36
+ end
37
+
27
38
  describe 'Base64' do
28
39
  it 'uses base64 to encode' do
29
40
  coder = Rack::Session::Cookie::Base64.new
@@ -57,6 +68,14 @@ describe Rack::Session::Cookie do
57
68
  end
58
69
  end
59
70
 
71
+ it "warns if no secret is given" do
72
+ cookie = Rack::Session::Cookie.new(incrementor)
73
+ @warnings.first.should =~ /no secret/i
74
+ @warnings.clear
75
+ cookie = Rack::Session::Cookie.new(incrementor, :secret => 'abc')
76
+ @warnings.should.be.empty?
77
+ end
78
+
60
79
  it 'uses a coder' do
61
80
  identity = Class.new {
62
81
  attr_reader :calls
@@ -181,19 +181,19 @@ describe Rack::Session::Pool do
181
181
  end
182
182
 
183
183
  it "does not return a cookie if cookie was not read/written" do
184
- app = Rack::Session::Cookie.new(nothing)
184
+ app = Rack::Session::Cookie.new(nothing, :secret => 'abc')
185
185
  res = Rack::MockRequest.new(app).get("/")
186
186
  res["Set-Cookie"].should.be.nil
187
187
  end
188
188
 
189
189
  it "does not return a cookie if cookie was not written (only read)" do
190
- app = Rack::Session::Cookie.new(session_id)
190
+ app = Rack::Session::Cookie.new(session_id, :secret => 'abc')
191
191
  res = Rack::MockRequest.new(app).get("/")
192
192
  res["Set-Cookie"].should.be.nil
193
193
  end
194
194
 
195
195
  it "returns even if not read/written if :expire_after is set" do
196
- app = Rack::Session::Cookie.new(nothing, :expire_after => 3600)
196
+ app = Rack::Session::Cookie.new(nothing, :expire_after => 3600, :secret => 'abc')
197
197
  res = Rack::MockRequest.new(app).get("/")
198
198
  res["Set-Cookie"].should.not.be.nil
199
199
  end
data/test/spec_utils.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  require 'rack/utils'
3
3
  require 'rack/mock'
4
+ require 'timeout'
4
5
 
5
6
  describe Rack::Utils do
6
7
  def kcodeu
@@ -93,6 +94,14 @@ describe Rack::Utils do
93
94
  Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
94
95
  should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
95
96
  Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar"
97
+ Rack::Utils.parse_query("=").should.equal "" => ""
98
+ Rack::Utils.parse_query("=value").should.equal "" => "value"
99
+ Rack::Utils.parse_query("key=").should.equal "key" => ""
100
+ Rack::Utils.parse_query("&key&").should.equal "key" => nil
101
+ Rack::Utils.parse_query(";key;", ";,").should.equal "key" => nil
102
+ Rack::Utils.parse_query(",key,", ";,").should.equal "key" => nil
103
+ Rack::Utils.parse_query(";foo=bar,;", ";,").should.equal "foo" => "bar"
104
+ Rack::Utils.parse_query(",foo=bar;,", ";,").should.equal "foo" => "bar"
96
105
  end
97
106
 
98
107
  should "parse nested query strings correctly" do
@@ -326,6 +335,10 @@ describe Rack::Utils, "byte_range" do
326
335
  Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=499-499"},500).should.equal [(499..499)]
327
336
  end
328
337
 
338
+ should "parse several byte ranges" do
339
+ Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-600,601-999"},1000).should.equal [(500..600),(601..999)]
340
+ end
341
+
329
342
  should "truncate byte ranges" do
330
343
  Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-999"},500).should.equal [(123..499)]
331
344
  Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=600-999"},500).should.equal []
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 3
9
- - 6
10
- version: 1.3.6
9
+ - 7
10
+ version: 1.3.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Christian Neukirchen
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-12-28 00:00:00 Z
18
+ date: 2013-01-07 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: bacon
@@ -81,7 +81,7 @@ dependencies:
81
81
  requirements:
82
82
  - - ">="
83
83
  - !ruby/object:Gem::Version
84
- hash: 1923831981
84
+ hash: 1777625759
85
85
  segments:
86
86
  - 1
87
87
  - 2
@@ -112,7 +112,7 @@ description: |
112
112
  servers, web frameworks, and software in between (the so-called
113
113
  middleware) into a single method call.
114
114
 
115
- Also see http://rack.rubyforge.org.
115
+ Also see http://rack.github.com/.
116
116
 
117
117
  email: chneukirchen@gmail.com
118
118
  executables:
@@ -124,6 +124,8 @@ extra_rdoc_files:
124
124
  - KNOWN-ISSUES
125
125
  files:
126
126
  - bin/rackup
127
+ - contrib/rack.png
128
+ - contrib/rack.svg
127
129
  - contrib/rack_logo.svg
128
130
  - example/lobster.ru
129
131
  - example/protectedlobster.rb
@@ -137,6 +139,7 @@ files:
137
139
  - lib/rack/auth/digest/request.rb
138
140
  - lib/rack/backports/uri/common_18.rb
139
141
  - lib/rack/backports/uri/common_192.rb
142
+ - lib/rack/backports/uri/common_193.rb
140
143
  - lib/rack/body_proxy.rb
141
144
  - lib/rack/builder.rb
142
145
  - lib/rack/cascade.rb
@@ -194,6 +197,7 @@ files:
194
197
  - test/builder/anything.rb
195
198
  - test/builder/comment.ru
196
199
  - test/builder/end.ru
200
+ - test/builder/line.ru
197
201
  - test/builder/options.ru
198
202
  - test/cgi/lighttpd.conf
199
203
  - test/cgi/rackup_stub.rb
@@ -277,7 +281,7 @@ files:
277
281
  - Rakefile
278
282
  - README.rdoc
279
283
  - SPEC
280
- homepage: http://rack.rubyforge.org
284
+ homepage: http://rack.github.com/
281
285
  licenses: []
282
286
 
283
287
  post_install_message:
@@ -306,7 +310,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
306
310
  requirements: []
307
311
 
308
312
  rubyforge_project: rack
309
- rubygems_version: 1.8.12
313
+ rubygems_version: 1.8.24
310
314
  signing_key:
311
315
  specification_version: 3
312
316
  summary: a modular Ruby webserver interface
@@ -357,3 +361,4 @@ test_files:
357
361
  - test/spec_urlmap.rb
358
362
  - test/spec_utils.rb
359
363
  - test/spec_webrick.rb
364
+ has_rdoc: