http_spew 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -6,3 +6,6 @@
6
6
  /tmp
7
7
  /GIT-VERSION-FILE
8
8
  /.manifest
9
+ /lib/http_spew/version.rb
10
+ # http://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
11
+ /Gemfile.lock
data/.manifest CHANGED
@@ -7,6 +7,7 @@ ChangeLog
7
7
  GIT-VERSION-FILE
8
8
  GIT-VERSION-GEN
9
9
  GNUmakefile
10
+ Gemfile
10
11
  LATEST
11
12
  LICENSE
12
13
  NEWS
@@ -14,20 +15,25 @@ README
14
15
  http_spew.gemspec
15
16
  lib/http_spew.rb
16
17
  lib/http_spew/chunky_pipe.rb
18
+ lib/http_spew/class_methods.rb
17
19
  lib/http_spew/content_md5.rb
18
20
  lib/http_spew/headers.rb
19
21
  lib/http_spew/hit_n_run.rb
20
22
  lib/http_spew/input_spray.rb
21
23
  lib/http_spew/request.rb
24
+ lib/http_spew/version.rb
22
25
  pkg.mk
23
26
  setup.rb
24
27
  test/content-md5.ru
25
28
  test/helper.rb
26
29
  test/mirror.ru
30
+ test/response_code.ru
27
31
  test/sha1.ru
28
32
  test/test_content_md5.rb
29
33
  test/test_hit_n_run.rb
30
34
  test/test_input_spray.rb
35
+ test/test_input_spray_with_md5.rb
31
36
  test/test_mirror.rb
32
37
  test/test_request.rb
38
+ test/test_unexpected_response.rb
33
39
  test/test_upload.rb
data/ChangeLog CHANGED
@@ -1,5 +1,308 @@
1
1
  ChangeLog from http://bogomips.org/http_spew.git
2
2
 
3
+ commit 84eaf4df3da4ef55e21649b971f2f246ab556b52
4
+ Author: Eric Wong <normalperson@yhbt.net>
5
+ Date: Tue May 10 14:40:53 2011 -0700
6
+
7
+ http_spew 0.2.0 - bugfixes and improvements
8
+
9
+ InputSpray may now be layered on top of ContentMD5 successfully.
10
+ Improved multi-threading abilities and tests.
11
+
12
+ Do not consider the API remotely stable, yet.
13
+
14
+ commit 02e333f025fb31e3384941c8c890b94394f98c34
15
+ Author: Eric Wong <normalperson@yhbt.net>
16
+ Date: Mon May 9 12:07:14 2011 -0700
17
+
18
+ test: disable rewindable input for Unicorn
19
+
20
+ In most cases, it's a feature we do not want.
21
+
22
+ commit c4b3c9d4231655b053472d47046d5512e55a79a9
23
+ Author: Eric Wong <normalperson@yhbt.net>
24
+ Date: Mon May 9 19:03:27 2011 +0000
25
+
26
+ request: fix bad constant reference
27
+
28
+ Oops :<
29
+
30
+ commit 8849da940687bd6433cf4b18cb4096f6867f3b3e
31
+ Author: Eric Wong <normalperson@yhbt.net>
32
+ Date: Mon May 9 18:32:28 2011 +0000
33
+
34
+ content_md5: switch back to pipe for tee-ability
35
+
36
+ This will save memory bandwidth down the line on Linux
37
+
38
+ commit 8c47ed775e7d40291660354bff29efea3d6f177a
39
+ Author: Eric Wong <normalperson@yhbt.net>
40
+ Date: Sat May 7 02:20:28 2011 +0000
41
+
42
+ content_md5: small garbage reduction
43
+
44
+ Might help somewhere...
45
+
46
+ commit ec0ff47df8c4068da2977cc9cb444caaac17b571
47
+ Author: Eric Wong <normalperson@yhbt.net>
48
+ Date: Sat May 7 01:52:16 2011 +0000
49
+
50
+ improve reliability of input_spray
51
+
52
+ Use pipes to avoid race condidions and deadlocks
53
+
54
+ commit 8157576344b9fcf98e85aafcc958c6ebe385b55b
55
+ Author: Eric Wong <normalperson@yhbt.net>
56
+ Date: Fri May 6 20:38:43 2011 +0000
57
+
58
+ update to kgio 2.4.0
59
+
60
+ Kgio.poll no longer returns EINTR
61
+
62
+ commit af2462a69eae821d6160fd2c2e571a589088977c
63
+ Author: Eric Wong <normalperson@yhbt.net>
64
+ Date: Thu May 5 16:53:53 2011 -0700
65
+
66
+ chunky_pipe: better error checking
67
+
68
+ commit 58ec2331a74ae472694fddae03f4ff454a801444
69
+ Author: Eric Wong <normalperson@yhbt.net>
70
+ Date: Thu May 5 16:53:04 2011 -0700
71
+
72
+ test_request: longer timeout for tests
73
+
74
+ This increases reliability, hopefully
75
+
76
+ commit 014cfa31df238a6b7a7e1652442613eed841b757
77
+ Author: Eric Wong <normalperson@yhbt.net>
78
+ Date: Wed May 4 18:11:22 2011 -0700
79
+
80
+ class_methods: fix race/deadlock issues
81
+
82
+ commit 254b6917d15d4bc013d9ca6889dc802b41592903
83
+ Author: Eric Wong <normalperson@yhbt.net>
84
+ Date: Wed Apr 20 17:53:14 2011 -0700
85
+
86
+ GNUmakefile: version file is added to gem
87
+
88
+ It's kinda useless otherwise...
89
+
90
+ commit 20fc09850031fb0a26f71ceafd15c93b924582f9
91
+ Author: Eric Wong <normalperson@yhbt.net>
92
+ Date: Wed Apr 20 17:44:01 2011 -0700
93
+
94
+ content_md5: stop using a pipe + thread
95
+
96
+ Too complex.
97
+
98
+ commit 7ac87f270c2583262ea923960c7cbb8a9b1f2dea
99
+ Author: Eric Wong <normalperson@yhbt.net>
100
+ Date: Wed Apr 20 17:43:41 2011 -0700
101
+
102
+ test_upload: more robust test case
103
+
104
+ Servers can be super fast.
105
+
106
+ commit 5e7e0553b7b435940afd478b83ebc265f31fdcf3
107
+ Author: Eric Wong <normalperson@yhbt.net>
108
+ Date: Wed Apr 20 17:43:13 2011 -0700
109
+
110
+ test_upload: fix timeout for seconds
111
+
112
+ HTTP_Spew.wait is now in seconds, not milliseconds
113
+
114
+ commit 33ae61959c657b5c60c2b7d9adb137ad46b24a15
115
+ Author: Eric Wong <normalperson@yhbt.net>
116
+ Date: Wed Apr 20 16:53:13 2011 -0700
117
+
118
+ request: handle EOF on read properly
119
+
120
+ "eof!" was never a method :x
121
+
122
+ commit f524b02e638cf6ec138f92538cf169edf524326c
123
+ Author: Eric Wong <normalperson@yhbt.net>
124
+ Date: Wed Apr 20 16:52:34 2011 -0700
125
+
126
+ content_md5: use kgio_write instead of IO#write
127
+
128
+ for consistency, not that we really take advantage
129
+ of kgio_write at the moment...
130
+
131
+ commit 3dd4f8c357b687bc1bf47785253e84cd7caa4661
132
+ Author: Eric Wong <normalperson@yhbt.net>
133
+ Date: Wed Apr 20 16:52:07 2011 -0700
134
+
135
+ input_spray: kill needless ivar assignment
136
+
137
+ Nothing references the writer thread.
138
+
139
+ commit b3dc72f3762b737bfc5dbed2d3d296a61879dc09
140
+ Author: Eric Wong <normalperson@yhbt.net>
141
+ Date: Wed Apr 20 16:51:23 2011 -0700
142
+
143
+ test/helper: use Thread.abort_on_exception = true
144
+
145
+ We want to detect failures during testing and fail hard
146
+
147
+ commit bde96ebe0c31508a4128542fce8753ba5e7403d7
148
+ Author: Eric Wong <normalperson@yhbt.net>
149
+ Date: Wed Apr 20 16:09:18 2011 -0700
150
+
151
+ remove timed_queue class
152
+
153
+ We don't need it, a mutex is all we need.
154
+
155
+ commit 9e5c03f84e4961d236398d416d526eef8344f98f
156
+ Author: Eric Wong <normalperson@yhbt.net>
157
+ Date: Wed Apr 20 15:42:53 2011 -0700
158
+
159
+ auto-generate version.rb file based on git version
160
+
161
+ It's helpful for people using in-development versions
162
+ of the code.
163
+
164
+ commit 6657070f27364688bba503c7e58add6dfe4dd8bf
165
+ Author: Eric Wong <normalperson@yhbt.net>
166
+ Date: Wed Apr 20 15:34:20 2011 -0700
167
+
168
+ reorganize class methods and exceptions
169
+
170
+ Also none of the exceptions we raise are for programming/user
171
+ so don't bother generating an expensive backtrace.
172
+
173
+ commit 100c23e10910bb8daff5507fd69f5d951b804672
174
+ Author: Eric Wong <normalperson@yhbt.net>
175
+ Date: Wed Apr 20 15:12:27 2011 -0700
176
+
177
+ request: support limiting accepted response codes
178
+
179
+ Some apps don't want certain responses and we won't
180
+ treat that as valid.
181
+
182
+ commit 67e23586d908eeed510508405dcdbee1dc5f9033
183
+ Author: Eric Wong <normalperson@yhbt.net>
184
+ Date: Wed Apr 20 14:49:06 2011 -0700
185
+
186
+ wait_mt: simplify
187
+
188
+ commit d2e882bdf763319ab149044440fe9bf108d46bc4
189
+ Author: Eric Wong <normalperson@yhbt.net>
190
+ Date: Wed Apr 20 14:29:26 2011 -0700
191
+
192
+ timed_queue: fix unused variable warning
193
+
194
+ commit 87cb2f5328bc20245a53a8462e6c1324c977189e
195
+ Author: Eric Wong <normalperson@yhbt.net>
196
+ Date: Wed Apr 20 14:17:20 2011 -0700
197
+
198
+ content_md5: make this a read-only IO-like object
199
+
200
+ It's easier to use this way since we'll also be able
201
+ to see the Content-MD5 and number of bytes digested.
202
+
203
+ commit 4a3f8cae93d12ebea896227e789d01912a3d55f3
204
+ Author: Eric Wong <normalperson@yhbt.net>
205
+ Date: Wed Apr 20 14:01:39 2011 -0700
206
+
207
+ chunky_pipe: autoclose on EOF
208
+
209
+ Since env["rack.input"] can't reliably be closed any
210
+ other way.
211
+
212
+ commit 0b6c35740ab1dd0ac64183b9ae0f1a5c710e4022
213
+ Author: Eric Wong <normalperson@yhbt.net>
214
+ Date: Thu Apr 14 15:19:02 2011 -0700
215
+
216
+ timeouts are in seconds, not milliseconds
217
+
218
+ It's more consistent with other Ruby code. poll(2)
219
+ (and epoll(2)) remain exception to that, so the
220
+ Ruby wrappers for those calls still use milliseconds.
221
+
222
+ commit 9e727f32a6e3506994934b9a48204ffdf0003bca
223
+ Author: Eric Wong <normalperson@yhbt.net>
224
+ Date: Thu Apr 14 21:56:48 2011 +0000
225
+
226
+ allow combining input_spray + MD5 filter
227
+
228
+ This is required for large parallel uploads.
229
+
230
+ commit 3aa054616bae3184ead01e78d70a707b59f71e63
231
+ Author: Eric Wong <normalperson@yhbt.net>
232
+ Date: Thu Apr 14 20:59:59 2011 +0000
233
+
234
+ Gemfile: depend on Unicorn 3 for tests
235
+
236
+ We need trailer support
237
+
238
+ commit b35f0d9bcdd3551bcdf2b5285c92197767fde496
239
+ Author: Eric Wong <normalperson@yhbt.net>
240
+ Date: Fri Apr 8 06:15:04 2011 +0000
241
+
242
+ timed_queue: Queue-like class that works with a timeout
243
+
244
+ commit 3db374e644014726092913eee7a4f09d15f6762c
245
+ Author: Eric Wong <normalperson@yhbt.net>
246
+ Date: Tue Apr 5 17:37:54 2011 -0700
247
+
248
+ chunky_pipe: rdoc purpose of this class
249
+
250
+ commit aac364b03306b1ba6ac65c325d946db4b0af02bd
251
+ Author: Eric Wong <normalperson@yhbt.net>
252
+ Date: Tue Apr 5 17:34:01 2011 -0700
253
+
254
+ content_md5: cleaner pack invocation (Ruby 1.9-only)
255
+
256
+ No need to String#strip!
257
+
258
+ commit c78c63adde85f0b329b44484ae652b0acdee4063
259
+ Author: Eric Wong <normalperson@yhbt.net>
260
+ Date: Fri Mar 18 14:23:51 2011 -0700
261
+
262
+ doc: various rdoc updates
263
+
264
+ Forcing myself to relearn my own library
265
+
266
+ commit cfa6c6b329ae44ae4f30a590d45e5df4c2d14d9a
267
+ Author: Eric Wong <normalperson@yhbt.net>
268
+ Date: Thu Mar 17 14:44:06 2011 -0700
269
+
270
+ tests: cleanup shadow warnings under 1.9.2
271
+
272
+ commit 1216ec9fc829c8c65f0f8ae2376138b90a1083d3
273
+ Author: Eric Wong <normalperson@yhbt.net>
274
+ Date: Thu Mar 17 21:19:54 2011 +0000
275
+
276
+ pkg.mk: new task for checking Ruby warnings
277
+
278
+ commit efcb08f789d60ebec26eadaada9af09593fedb9b
279
+ Author: Eric Wong <normalperson@yhbt.net>
280
+ Date: Wed Mar 16 01:11:05 2011 +0000
281
+
282
+ remove needless assignments
283
+
284
+ More noise
285
+
286
+ commit 6ce8e006a94a852ed76ffeababa1cac2ed7889c0
287
+ Author: Eric Wong <normalperson@yhbt.net>
288
+ Date: Wed Mar 16 01:06:22 2011 +0000
289
+
290
+ content_md5: improve readability
291
+
292
+ commit 61c1164c39886a198d33697c14c9abf099e3e08f
293
+ Author: Eric Wong <normalperson@yhbt.net>
294
+ Date: Wed Mar 16 00:31:25 2011 +0000
295
+
296
+ use bundler
297
+
298
+ commit 2318ee6a732f71d38a7749c0663bf780135be49c
299
+ Author: Eric Wong <normalperson@yhbt.net>
300
+ Date: Mon Feb 28 04:27:26 2011 +0000
301
+
302
+ gemspec: bump kcar version
303
+
304
+ Newer is better
305
+
3
306
  commit 32223af63dc5296cd09f2472ad60263d98f379a2
4
307
  Author: Eric Wong <normalperson@yhbt.net>
5
308
  Date: Thu Feb 24 08:28:34 2011 +0000
data/GIT-VERSION-FILE CHANGED
@@ -1 +1 @@
1
- GIT_VERSION = 0.1.0
1
+ GIT_VERSION = 0.2.0
data/GIT-VERSION-GEN CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v0.1.0.GIT
4
+ DEF_VER=v0.2.0.GIT
5
5
 
6
6
  LF='
7
7
  '
data/GNUmakefile CHANGED
@@ -2,4 +2,15 @@ all::
2
2
  RSYNC_DEST := bogomips.org:/srv/bogomips/http_spew
3
3
  rfproject := rainbows
4
4
  rfpackage := http_spew
5
+
6
+ RUBY_VERSION_FILE = lib/http_spew/version.rb
7
+ pkg_extra += $(RUBY_VERSION_FILE)
5
8
  include pkg.mk
9
+
10
+ $(RUBY_VERSION_FILE): GIT-VERSION-FILE
11
+ @$(RM) -f $@+
12
+ @echo >> $@+ '# -*- encoding: binary -*-'
13
+ @echo >> $@+ 'HTTP_Spew.const_set :VERSION, "$(GIT_VERSION)"'
14
+ @mv $@+ $@
15
+
16
+ build: $(RUBY_VERSION_FILE)
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source(ENV["GEM_SOURCE"] || :rubygems)
2
+ gem 'rack', '~> 1.2'
3
+ gem 'kgio', '~> 2.4'
4
+ gem 'kcar', '~> 0.2'
5
+ gem 'unicorn', '~> 3' # not many servers offer HTTP Trailer handling
data/LATEST CHANGED
@@ -1,4 +1,7 @@
1
- === HTTP spew 0.1.0 - initial release / 2011-02-24 08:50 UTC
1
+ === http_spew 0.2.0 - bugfixes and improvements / 2011-05-10 22:19 UTC
2
2
 
3
- Might as well...
3
+ InputSpray may now be layered on top of ContentMD5 successfully.
4
+ Improved multi-threading abilities and tests.
5
+
6
+ Do not consider the API remotely stable, yet.
4
7
 
data/NEWS CHANGED
@@ -1,3 +1,10 @@
1
+ === http_spew 0.2.0 - bugfixes and improvements / 2011-05-10 22:19 UTC
2
+
3
+ InputSpray may now be layered on top of ContentMD5 successfully.
4
+ Improved multi-threading abilities and tests.
5
+
6
+ Do not consider the API remotely stable, yet.
7
+
1
8
  === HTTP spew 0.1.0 - initial release / 2011-02-24 08:50 UTC
2
9
 
3
10
  Might as well...
data/http_spew.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.rdoc_options = rdoc_options
19
19
  s.rubyforge_project = %q{rainbows}
20
20
  s.test_files = Dir["test/test_*.rb"]
21
- s.add_dependency(%q<kcar>, "~> 0.1.2")
22
- s.add_dependency(%q<kgio>, "~> 2.3")
21
+ s.add_dependency(%q<kcar>, "~> 0.2")
22
+ s.add_dependency(%q<kgio>, "~> 2.4")
23
23
  s.add_development_dependency(%q<wrongdoc>, "~> 1.5")
24
24
  end
@@ -1,12 +1,26 @@
1
1
  # -*- encoding: binary -*-
2
2
  #
3
- # HTTP Spew blows chunks (at HTTP servers)!
3
+ # This is a OS-level pipe that overrides IO#read to provide
4
+ # IO#readpartial-like semantics while remaining Rack::Lint-compatible
5
+ # for EOF, meaning we return nil on EOF instead of raising EOFError.
4
6
  class HTTP_Spew::ChunkyPipe < Kgio::Pipe
7
+
8
+ # other threads may force an error to be raised in the +read+
9
+ # method
5
10
  attr_accessor :error
6
11
 
7
- # makes read behave like readpartial without EOFError
12
+ # Override IO#read to behave like IO#readpartial, but still return +nil+
13
+ # on EOF instead of raising EOFError.
8
14
  def read(*args)
9
- defined?(@error) and raise @error
10
- kgio_read(*args)
15
+ check_err!
16
+ kgio_read(*args) || check_err! || close
17
+ end
18
+
19
+ def check_err!
20
+ if defined?(@error)
21
+ closed? or close
22
+ raise @error
23
+ end
24
+ nil
11
25
  end
12
26
  end
@@ -0,0 +1,125 @@
1
+ require "thread"
2
+ require "io/wait"
3
+
4
+ module HTTP_Spew::ClassMethods
5
+ def error_all(requests, error) # :nodoc:
6
+ requests.each { |req| req.error ||= error }
7
+ end
8
+
9
+ def done_early(ready, failed, requests) # :nodoc:
10
+ ready.concat(failed)
11
+ pending = requests - ready
12
+ unless pending.empty?
13
+ error = HTTP_Spew::ConnectionReset.new("prematurely terminated")
14
+ ready.concat(error_all(pending, error))
15
+ end
16
+ ready.uniq!
17
+ ready
18
+ end
19
+
20
+ # Returns an array of requests that are complete, including those
21
+ # that have errored out. Incomplete requests remain in +requests+
22
+ # If +need+ is fullfilled, it closes all incomplete requests and
23
+ # returns all requests.
24
+ def wait_nonblock!(need, requests)
25
+ ready, failed = [], []
26
+ requests.delete_if do |req|
27
+ begin
28
+ case req.resume
29
+ when Symbol # :wait_writable, :wait_readable
30
+ false
31
+ else
32
+ (ready << req).size == need and
33
+ return done_early(ready, failed, requests)
34
+ true
35
+ end
36
+ rescue => e
37
+ req.error = e
38
+ failed << req
39
+ end
40
+ end
41
+ ready.concat(failed).empty? ? nil : ready
42
+ end
43
+
44
+ def with_timeout(t)
45
+ t0 = Time.now
46
+ yield
47
+ ensure
48
+ t[0] -= Time.now - t0
49
+ t[0] = 0.0 if t[0] < 0
50
+ end
51
+
52
+ # Returns an array of requests that are complete, including those
53
+ # that have errored out.
54
+ # If +need+ is fullfilled, it closes all incomplete requests.
55
+ def wait_mt(need, requests, timeout)
56
+ ready, failed = [], []
57
+ r, w = Kgio::Pipe.new
58
+ active = []
59
+ t = [ timeout ]
60
+ requests.each_with_index do |req, i|
61
+ active << Thread.new do
62
+ begin
63
+ rv = req.run(timeout)
64
+ w.write([ i ].pack("v"))
65
+ rv
66
+ rescue => err
67
+ err
68
+ end
69
+ end
70
+ end
71
+ begin
72
+ with_timeout(t) { r.wait(t[0]) }
73
+ req_idx = r.read(2).unpack("v")[0]
74
+ thr = active[req_idx]
75
+ with_timeout(t) { thr.join(t[0]) }
76
+ rv = thr.value
77
+ (Array === rv ? ready : failed) << requests[req_idx]
78
+ ready.size == need and return done_early(ready, failed, requests)
79
+ end until t[0] < 0.0 || (ready.size + failed.size) == requests.size
80
+
81
+ ready.concat(failed)
82
+ pending = requests - ready
83
+ error = HTTP_Spew::TimeoutError.new("request timed out")
84
+ ready.concat(error_all(pending, error))
85
+ ensure
86
+ w.close
87
+ r.close
88
+ end
89
+
90
+ def wait(need, requests, timeout)
91
+ ready, failed = [], []
92
+ pollset = {}
93
+ t = [ timeout ]
94
+ begin
95
+ requests.each do |req|
96
+ begin
97
+ case rv = req.resume
98
+ when Symbol # :wait_writable, :wait_readable
99
+ pollset[req] = rv
100
+ else
101
+ (ready << req).size == need and
102
+ return done_early(ready, failed, requests)
103
+ pollset.delete(req)
104
+ end
105
+ rescue => e
106
+ req.error = e
107
+ failed << req
108
+ pollset.delete(req)
109
+ end
110
+ end
111
+ break if pollset.empty?
112
+
113
+ busy = pollset.keys
114
+ rv = with_timeout(t) { Kgio.poll(pollset, (t[0] * 1000).to_i) } or break
115
+ end while t[0] > 0.0 && requests = rv.keys.concat(busy).uniq!
116
+
117
+ ready.concat(failed)
118
+ unless requests.empty?
119
+ error = HTTP_Spew::TimeoutError.new("request timed out")
120
+ ready.concat(error_all(requests, error))
121
+ ready.uniq!
122
+ end
123
+ ready
124
+ end
125
+ end
@@ -1,52 +1,62 @@
1
1
  # -*- encoding: binary -*-
2
2
  require "digest/md5"
3
- module HTTP_Spew::ContentMD5
4
- class MismatchError < HTTP_Spew::Error
5
- end
6
- class LengthError < HTTP_Spew::Error
7
- end
8
3
 
9
- def self.input(env)
4
+ # this uses a pipe internally so it can use IO.tee in the "io_splice" RubyGem
5
+ class HTTP_Spew::ContentMD5
6
+ attr_reader :to_io
7
+ attr_reader :content_md5
8
+ attr_reader :bytes_digested
9
+
10
+ CRLF = "\r\n" # :nodoc:
11
+
12
+ def initialize(env)
10
13
  if trailer = env["HTTP_TRAILER"]
11
- have_md5 = trailer.split(/\s*,\s*/).grep(/\AContent-MD5\z/i)[0]
12
- return env["rack.input"] if have_md5 && env["HTTP_CONTENT_MD5"]
13
- end
14
- if trailer
15
- unless have_md5
14
+ unless trailer.split(/\s*,\s*/).grep(/\AContent-MD5\z/i)[0]
16
15
  trailer << (trailer.empty? ? "Content-MD5" : ",Content-MD5")
17
16
  end
18
17
  else
19
18
  env["HTTP_TRAILER"] = "Content-MD5"
20
19
  end
21
20
  env["HTTP_TRANSFER_ENCODING"] = "chunked"
22
- rd, wr = HTTP_Spew::ChunkyPipe.new
23
- md5 = env.delete("HTTP_CONTENT_MD5")
24
- len = env.delete("CONTENT_LENGTH")
25
- start_write_driver(env["rack.input"], rd, wr, md5, len)
26
- rd
21
+ @to_io, wr = HTTP_Spew::ChunkyPipe.new
22
+ expect_md5 = env.delete("HTTP_CONTENT_MD5")
23
+ expect_len = env.delete("CONTENT_LENGTH")
24
+ start_write_driver(env["rack.input"], wr, expect_md5, expect_len)
25
+ end
26
+
27
+ # compatible with IO#read and Rack::InputWrapper#read
28
+ def read(length, buffer = "")
29
+ # calls HTTP_Spew::ChunkyPipe#read
30
+ @to_io.read(length, buffer)
27
31
  end
28
32
 
29
- def self.start_write_driver(input, rd, wr, expect_md5, expect_len)
33
+ def start_write_driver(input, wr, expect_md5, expect_len) # :nodoc:
30
34
  Thread.new do
31
35
  begin
32
- digest, buf, bytes = Digest::MD5.new, "", 0
36
+ digest = Digest::MD5.new
37
+ buf = ""
38
+ bytes = 0
33
39
  while input.read(0x4000, buf)
34
40
  n = buf.size
35
41
  bytes += n
36
- wr.kgio_write("#{n.to_s(16)}\r\n")
42
+ wr.write("#{n.to_s(16)}\r\n")
37
43
  digest.update(buf)
38
- wr.kgio_write(buf << "\r\n")
44
+ wr.write(buf << CRLF)
39
45
  end
40
46
  if expect_len && expect_len.to_i != bytes
41
- raise LengthError, "expect=#{expect_len} != got=#{bytes}"
47
+ raise HTTP_Spew::LengthError,
48
+ "expect=#{expect_len} != got=#{bytes}", []
42
49
  end
43
- digest = [ digest.digest ].pack("m").strip!
44
- if expect_md5 && expect_md5.strip != digest
45
- raise MismatchError, "expect=#{expect_md5} != got=#{digest}"
50
+ md5 = [ digest.digest ].pack("m0")
51
+ if expect_md5 && expect_md5.strip != md5
52
+ raise HTTP_Spew::ChecksumError,
53
+ "expect=#{expect_md5} != got=#{md5}", []
46
54
  end
47
- wr.write "0\r\nContent-MD5: #{digest}\r\n\r\n"
55
+ wr.write "0\r\nContent-MD5: #{md5}\r\n\r\n"
56
+ @content_md5 = md5
57
+ @bytes_digested = bytes
48
58
  rescue => e
49
- rd.error = e
59
+ @to_io.error = e
50
60
  ensure
51
61
  wr.close
52
62
  end
@@ -10,12 +10,23 @@ module HTTP_Spew::Headers
10
10
  CONTENT_TYPE = "CONTENT_TYPE" # specified by Rack to be !/^HTTP_/
11
11
  # :startdoc:
12
12
 
13
+ # regenerates the request_uri from a Rack +env+
13
14
  def request_uri(env)
14
15
  qs = env[QUERY_STRING]
15
16
  qs.size == 0 ? env[PATH_INFO] : "#{env[PATH_INFO]}?#{qs}"
16
17
  end
17
18
  module_function :request_uri
18
19
 
20
+ # converts a Rack +env+ into an HTTP header buffer
21
+ # If +input+ is a string, this appends +input+ to the header
22
+ # buffer so the client can avoid extra writes. If input is
23
+ # an IO-like object, it is returned as the second element
24
+ # of an array.
25
+ #
26
+ # This, the following code always works and may be used to clobber
27
+ # +input+ if it is merged into +buf+
28
+ #
29
+ # buf, input = env_to_headers(env, input)
19
30
  def env_to_headers(env, input)
20
31
  req = "#{env[REQUEST_METHOD]} " \
21
32
  "#{env[REQUEST_URI] || request_uri(env)} HTTP/1.1\r\n" \