http_spew 0.4.1 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/GIT-VERSION-FILE CHANGED
@@ -1 +1 @@
1
- GIT_VERSION = 0.4.1
1
+ GIT_VERSION = 0.7.1
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.4.1.GIT
4
+ DEF_VER=v0.7.1
5
5
 
6
6
  LF='
7
7
  '
data/GNUmakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  all::
2
- RSYNC_DEST := bogomips.org:/srv/bogomips/http_spew
3
- rfproject := rainbows
2
+ RSYNC_DEST := yhbt.net:/srv/yhbt/http_spew
4
3
  rfpackage := http_spew
5
4
 
6
5
  RUBY_VERSION_FILE = lib/http_spew/version.rb
@@ -10,7 +9,7 @@ include pkg.mk
10
9
  $(RUBY_VERSION_FILE): GIT-VERSION-FILE
11
10
  @$(RM) -f $@+
12
11
  @echo >> $@+ '# -*- encoding: binary -*-'
13
- @echo >> $@+ 'HTTP_Spew.const_set :VERSION, "$(GIT_VERSION)"'
12
+ @echo >> $@+ 'HTTP_Spew.const_set :VERSION, "$(GIT_VERSION)".freeze'
14
13
  @mv $@+ $@
15
14
 
16
15
  build: $(RUBY_VERSION_FILE)
data/LATEST CHANGED
@@ -1,4 +1,6 @@
1
- === http_spew 0.4.1 / 2012-09-23 00:01 UTC
1
+ === http_spew 0.7.1 / 2022-01-16 08:45 UTC
2
2
 
3
- Fix formatting of user-supplied headers.
3
+ 2 changes since v0.7.0:
4
+ doc: s/bogomips.org/yhbt.net/
5
+ fix mismatched indentation warnings
4
6
 
data/LICENSE CHANGED
@@ -1,17 +1,14 @@
1
- HTTP Spew is copyrighted Free Software by all contributors, see logs in
1
+ http_spew is copyrighted Free Software by all contributors, see logs in
2
2
  revision control for names and email addresses of all of them.
3
3
 
4
4
  You can redistribute it and/or modify it under the terms of the GNU
5
- General Public License, version 2 *or* 3 ({GPLv3}[link:COPYING]) as
6
- published by the Free Software Foundation. The project leader
7
- (Eric Wong) reserves the right to relicense HTTP Spew under future versions
8
- of the GPL.
5
+ General Public License as published by the Free Software Foundation;
6
+ either version 2 of the License, or (at your option) any later version.
9
7
 
10
- HTTP Spew is distributed in the hope that it will be useful, but WITHOUT
8
+ http_spew is distributed in the hope that it will be useful, but WITHOUT
11
9
  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
13
- License for more details.
10
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11
+ for more details.
14
12
 
15
- You should have received a copy of the GNU General Public License
16
- along with HTTP Spew; if not, write to the Free Software Foundation, Inc.,
17
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
13
+ You should have received a copy of the GNU General Public License along
14
+ with this program; if not, see https://www.gnu.org/licenses/gpl-2.0.txt
data/NEWS CHANGED
@@ -1,3 +1,58 @@
1
+ === http_spew 0.7.1 / 2022-01-16 08:45 UTC
2
+
3
+ 2 changes since v0.7.0:
4
+ doc: s/bogomips.org/yhbt.net/
5
+ fix mismatched indentation warnings
6
+
7
+ === http_spew 0.7.0 - modernizing for newer Rubies / 2017-06-12 22:00 UTC
8
+
9
+ Ruby 2.3+ is now required as kgio dependencies are gone.
10
+ This is unlikely to be a problem in practice, as hardly
11
+ anybody uses this at the moment.
12
+
13
+ 5 changes since 0.6.0:
14
+
15
+ remove most uses of kgio
16
+ make kgio entirely optional
17
+ update to only use public email address
18
+ http_spew: remove kgio require at top level
19
+ gemspec: remove olddoc as a build dependency
20
+
21
+ === http_spew 0.6.0 - even less / 2016-11-05 02:15 UTC
22
+
23
+ Rack "to_path" support is dropped since few servers can
24
+ take advantage of this in a non-buggy way.
25
+
26
+ As a consolation, #each should be less GC-intensive by
27
+ with explicit calls to String#clear to reduce memory
28
+ pressure.
29
+
30
+ 3 changes since 0.5.0
31
+
32
+ pkg.mk: avoid network for "gem install"
33
+ request: drop to_path support
34
+ explicitly clear large buf when it is obviously safe
35
+
36
+ === http_spew 0.5.0 / 2016-10-31 20:43 UTC
37
+
38
+ This release requires Ruby 2.1 or later.
39
+
40
+ 13 changes since 0.4.1:
41
+
42
+ gemspec: require kcar >= 0.3.1
43
+ test/helper: explicit redirect for Ruby 2.0.0
44
+ update packaging + docs (website)
45
+ allow all future GPL versions
46
+ add benchmark scripts
47
+ relax dependency on unicorn
48
+ declare empty classes with constant assignment
49
+ test_upload: use object_id to check matches
50
+ use frozen string literals for Ruby 2.1+
51
+ merge into kcar project and mailing list
52
+ dedicated mailing list
53
+ rely on opt_str_freeze in more places
54
+ use monotonic clock for timing
55
+
1
56
  === http_spew 0.4.1 / 2012-09-23 00:01 UTC
2
57
 
3
58
  Fix formatting of user-supplied headers.
data/README CHANGED
@@ -11,7 +11,6 @@ Use HTTP Spew if you wish you could kinda multicast with HTTP...
11
11
  * No support for DNS resolution (WONTFIX, ever)
12
12
  * No support for HTTPS
13
13
  * No support for keepalive (yet?)
14
- * No support for Ruby 1.8, this is Ruby 1.9-only
15
14
  * Not remotely RFC-compliant
16
15
  * Messes up analytics/reporting on servers
17
16
  * Resets server connections
@@ -33,13 +32,13 @@ It's also completely untested and unused anywhere!
33
32
 
34
33
  You can get the latest source via git from the following locations:
35
34
 
36
- git://bogomips.org/http_spew.git
35
+ git://yhbt.net/http_spew.git
37
36
  git://repo.or.cz/http_spew.git (mirror)
38
37
 
39
38
  You may browse the code from the web and download the latest snapshot
40
39
  tarballs here:
41
40
 
42
- * http://bogomips.org/http_spew.git (cgit)
41
+ * https://yhbt.net/http_spew.git
43
42
  * http://repo.or.cz/w/http_spew.git (gitweb)
44
43
 
45
44
  Inline patches (from "git format-patch") to the mailing list are
@@ -54,8 +53,8 @@ don't email the git mailing list or maintainer with http_spew patches.
54
53
  == Contact
55
54
 
56
55
  All feedback (bug reports, user/development discussion, patches, pull
57
- requests) go to the mailing list: mailto:http.spew@librelist.org
56
+ requests) go to the mailing list: mailto:http_spew-public@yhbt.net
58
57
 
59
- Mailing list archives in mbox format may be downloaded here:
58
+ Mailing list archives may be viewed and downloaded here:
60
59
 
61
- http://bogomips.org/http_spew/archives/
60
+ https://yhbt.net/http_spew-public/
@@ -0,0 +1,52 @@
1
+ # -*- encoding: binary -*-
2
+ require "./test/helper"
3
+ require "benchmark"
4
+
5
+ class TestBMContentMD5 < Test::Unit::TestCase
6
+ def setup
7
+ @addr, @port, @srv = start_server("./test/content-md5.ru", 1)
8
+ @sockaddr = Socket.pack_sockaddr_in(@port, @addr)
9
+ @env = {
10
+ "REQUEST_METHOD" => "PUT",
11
+ "REQUEST_URI" => "/",
12
+ "HTTP_HOST" => "example.com",
13
+ }
14
+ @tmpfiles = []
15
+ @bs = ENV['bs'] ? ENV['bs'].to_i : 1024 * 1024
16
+ @count = ENV['count'] ? ENV['count'].to_i : 1000
17
+ @cmd = %w(dd if=/dev/zero)
18
+ @cmd << "bs=#@bs"
19
+ @cmd << "count=#@count"
20
+ end
21
+
22
+ def teardown
23
+ Process.kill(:QUIT, @srv)
24
+ Process.waitpid2(@srv)
25
+ @tmpfiles.each { |tmp| tmp.closed? or tmp.close! }
26
+ end
27
+
28
+ def test_upload_with_md5
29
+ rd, wr = IO.pipe
30
+ pid = fork do
31
+ $stdout.reopen(wr)
32
+ rd.close
33
+ wr.close
34
+ exec(*@cmd)
35
+ end
36
+ wr.close
37
+ @env["CONTENT_LENGTH"] = (@bs * @count).to_s
38
+ @env["rack.input"] = rd
39
+ input = HTTP_Spew::ContentMD5.new(@env)
40
+ assert_nil @env["CONTENT_LENGTH"]
41
+ assert_equal "chunked", @env["HTTP_TRANSFER_ENCODING"]
42
+ req = HTTP_Spew::Request.new(@env, input, @sockaddr)
43
+ rv = nil
44
+ res = Benchmark.measure do
45
+ rv = req.run(100000)
46
+ end
47
+ assert_equal 200, rv[0].to_i
48
+ pid, status = Process.waitpid2(pid)
49
+ assert status.success?
50
+ p res
51
+ end
52
+ end
@@ -0,0 +1,59 @@
1
+ require "./test/helper"
2
+ require "benchmark"
3
+
4
+ class TestBMContentMD5InputSpray < Test::Unit::TestCase
5
+ def setup
6
+ @nr = 4
7
+ @addr, @port, @srv = start_server("./test/content-md5.ru", @nr)
8
+ @sockaddr = Socket.pack_sockaddr_in(@port, @addr)
9
+ @env = {
10
+ "REQUEST_METHOD" => "PUT",
11
+ "REQUEST_URI" => "/",
12
+ "HTTP_HOST" => "example.com",
13
+ }
14
+ @tmpfiles = []
15
+ @bs = ENV['bs'] ? ENV['bs'].to_i : 1024 * 1024
16
+ @count = ENV['count'] ? ENV['count'].to_i : 1000
17
+ @cmd = %w(dd if=/dev/zero)
18
+ @cmd << "bs=#@bs"
19
+ @cmd << "count=#@count"
20
+ end
21
+
22
+ def teardown
23
+ Process.kill(:QUIT, @srv)
24
+ Process.waitpid2(@srv)
25
+ @tmpfiles.each { |tmp| tmp.closed? or tmp.close! }
26
+ end
27
+
28
+ def test_spray_with_md5
29
+ rd, wr = IO.pipe
30
+ pid = fork do
31
+ $stdout.reopen(wr)
32
+ rd.close
33
+ wr.close
34
+ exec(*@cmd)
35
+ end
36
+ wr.close
37
+ @env["CONTENT_LENGTH"] = (@bs * @count).to_s
38
+ @env["rack.input"] = rd
39
+ input = HTTP_Spew::ContentMD5.new(@env)
40
+ sprayer = HTTP_Spew::InputSpray.new(@env, @nr, input)
41
+ assert_nil @env["CONTENT_LENGTH"]
42
+ assert_equal "chunked", @env["HTTP_TRANSFER_ENCODING"]
43
+ reqs = sprayer.readers.map do |md5_input|
44
+ HTTP_Spew::Request.new(@env, md5_input, @sockaddr)
45
+ end
46
+ assert_equal @nr, reqs.size
47
+ rv = nil
48
+ res = Benchmark.measure do
49
+ rv = HTTP_Spew.wait_mt reqs.size, reqs, 3600
50
+ end
51
+ assert_equal @nr, rv.size
52
+ rv.each do |resp|
53
+ assert_equal 200, resp.response[0].to_i
54
+ end
55
+ pid, status = Process.waitpid2(pid)
56
+ assert status.success?
57
+ p res
58
+ end
59
+ end
data/http_spew.gemspec CHANGED
@@ -1,24 +1,20 @@
1
- ENV["VERSION"] or abort "VERSION= must be specified"
2
- manifest = File.readlines('.manifest').map! { |x| x.chomp! }
3
- require 'wrongdoc'
4
- extend Wrongdoc::Gemspec
5
- name, summary, title = readme_metadata
1
+ manifest = File.exist?('.manifest') ?
2
+ IO.readlines('.manifest').map!(&:chomp!) : `git ls-files`.split("\n")
6
3
 
7
4
  Gem::Specification.new do |s|
8
5
  s.name = %q{http_spew}
9
- s.version = ENV["VERSION"].dup
10
- s.authors = ["HTTP Spew hackers"]
11
- s.date = Time.now.utc.strftime('%Y-%m-%d')
12
- s.description = readme_description
13
- s.email = %q{http.spew@librelist.org}
14
- s.extra_rdoc_files = extra_rdoc_files(manifest)
6
+ s.version = (ENV['VERSION'] || '0.7.1').dup
7
+ s.authors = ["http_spew hackers"]
8
+ s.description = File.read('README').split("\n\n")[1]
9
+ s.email = %q{http_spew-public@yhbt.net}
10
+ s.extra_rdoc_files = IO.readlines('.document').map!(&:chomp!).keep_if do |f|
11
+ File.exist?(f)
12
+ end
15
13
  s.files = manifest
16
- s.homepage = Wrongdoc.config[:rdoc_url]
17
- s.summary = summary
18
- s.rdoc_options = rdoc_options
19
- s.rubyforge_project = %q{rainbows}
14
+ s.homepage = 'https://yhbt.net/http_spew/'
15
+ s.summary = 'HTTP Spew - LAN-only HTTP spam^H^H^H^Hclient for Ruby'
20
16
  s.test_files = Dir["test/test_*.rb"]
21
- s.add_dependency(%q<kcar>, "~> 0.3")
22
- s.add_dependency(%q<kgio>, "~> 2.6")
23
- s.add_development_dependency(%q<wrongdoc>, "~> 1.5")
17
+ s.add_dependency(%q<kcar>, [ "~> 0.3", ">= 0.3.1"])
18
+ s.required_ruby_version = '>= 2.3'
19
+ s.licenses = %w(GPL-2.0+)
24
20
  end
@@ -3,17 +3,28 @@
3
3
  # This is a OS-level pipe that overrides IO#read to provide
4
4
  # IO#readpartial-like semantics while remaining Rack::Lint-compatible
5
5
  # for EOF, meaning we return nil on EOF instead of raising EOFError.
6
- class HTTP_Spew::ChunkyPipe < Kgio::Pipe
6
+ class HTTP_Spew::ChunkyPipe < IO
7
7
 
8
8
  # other threads may force an error to be raised in the +read+
9
9
  # method
10
10
  attr_accessor :error
11
11
 
12
+ class << self
13
+ alias new pipe
14
+ end
15
+
12
16
  # Override IO#read to behave like IO#readpartial, but still return +nil+
13
17
  # on EOF instead of raising EOFError.
14
- def read(*args)
18
+ def read(len = 16384, buf = '')
15
19
  check_err!
16
- kgio_read(*args) || check_err! || close
20
+ case read_nonblock(len, buf, exception: false)
21
+ when nil
22
+ return check_err! || close
23
+ when :wait_readable
24
+ wait_readable # retry
25
+ else
26
+ return buf
27
+ end while true
17
28
  end
18
29
 
19
30
  def check_err!
@@ -41,11 +41,11 @@ module HTTP_Spew::ClassMethods
41
41
  end
42
42
 
43
43
  def with_timeout(t)
44
- t0 = Time.now
44
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
45
45
  yield
46
- ensure
47
- t[0] -= Time.now - t0
48
- t[0] = 0.0 if t[0] < 0
46
+ ensure
47
+ t[0] -= Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
48
+ t[0] = 0.0 if t[0] < 0
49
49
  end
50
50
 
51
51
  # Returns an array of requests that are complete, including those
@@ -53,14 +53,14 @@ module HTTP_Spew::ClassMethods
53
53
  # If +need+ is fullfilled, it closes all incomplete requests.
54
54
  def wait_mt(need, requests, timeout)
55
55
  ready, failed = [], []
56
- r, w = Kgio::Pipe.new
56
+ r, w = IO.pipe
57
57
  active = []
58
58
  t = [ timeout ]
59
59
  requests.each_with_index do |req, i|
60
60
  active << Thread.new do
61
61
  begin
62
62
  rv = req.run(timeout)
63
- w.write([ i ].pack("v"))
63
+ w.write([ i ].pack("v".freeze))
64
64
  rv
65
65
  rescue => err
66
66
  err
@@ -68,8 +68,8 @@ module HTTP_Spew::ClassMethods
68
68
  end
69
69
  end
70
70
  begin
71
- with_timeout(t) { r.kgio_wait_readable(t[0]) }
72
- req_idx = r.read(2).unpack("v")[0]
71
+ with_timeout(t) { r.wait_readable(t[0]) }
72
+ req_idx = r.read(2).unpack("v".freeze)[0]
73
73
  thr = active[req_idx]
74
74
  with_timeout(t) { thr.join(t[0]) }
75
75
  rv = thr.value
@@ -81,9 +81,9 @@ module HTTP_Spew::ClassMethods
81
81
  pending = requests - ready
82
82
  error = HTTP_Spew::TimeoutError.new("request timed out")
83
83
  ready.concat(error_all(pending, error))
84
- ensure
85
- w.close
86
- r.close
84
+ ensure
85
+ w.close
86
+ r.close
87
87
  end
88
88
 
89
89
  def wait(need, requests, timeout)
@@ -110,7 +110,7 @@ module HTTP_Spew::ClassMethods
110
110
  break if pollset.empty?
111
111
 
112
112
  busy = pollset.keys
113
- rv = with_timeout(t) { Kgio.poll(pollset, (t[0] * 1000).to_i) } or break
113
+ rv = with_timeout(t) { do_poll(pollset, t[0]) } or break
114
114
  end while t[0] > 0.0 && requests = rv.keys.concat(busy).uniq!
115
115
 
116
116
  ready.concat(failed)
@@ -121,4 +121,32 @@ module HTTP_Spew::ClassMethods
121
121
  end
122
122
  ready
123
123
  end
124
+
125
+ begin
126
+ require 'kgio'
127
+ def do_poll(pollset, sec) # :nodoc:
128
+ Kgio.poll(pollset, (sec * 1000).to_i)
129
+ end
130
+ rescue LoadError
131
+ # emulate Kgio.poll with IO.select
132
+ def do_poll(pollset, sec) # :nodoc:
133
+ rd = []
134
+ wr = []
135
+ pollset.each do |io, events|
136
+ case events
137
+ when :wait_readable
138
+ rd << io
139
+ when :wait_writable
140
+ wr << io
141
+ else
142
+ raise "BUG: unsupported event #{event.inspect} for #{io.inspect}"
143
+ end
144
+ end
145
+ ready = IO.select(rd, wr, nil, sec) or return
146
+ pollset.clear
147
+ ready[0].each { |io| pollset[io] = 1 } # POLLIN
148
+ ready[1].each { |io| pollset[io] = 4 } # POLLOUT
149
+ pollset
150
+ end
151
+ end
124
152
  end
@@ -7,17 +7,16 @@ class HTTP_Spew::ContentMD5
7
7
  attr_reader :content_md5
8
8
  attr_reader :bytes_digested
9
9
 
10
- CRLF = "\r\n" # :nodoc:
11
-
12
10
  def initialize(env, input = env["rack.input"])
13
11
  if trailer = env["HTTP_TRAILER"]
14
12
  unless trailer.split(/\s*,\s*/).grep(/\AContent-MD5\z/i)[0]
15
- trailer << (trailer.empty? ? "Content-MD5" : ",Content-MD5")
13
+ trailer << (trailer.empty? ? "Content-MD5".freeze
14
+ : ",Content-MD5".freeze)
16
15
  end
17
16
  else
18
- env["HTTP_TRAILER"] = "Content-MD5"
17
+ env["HTTP_TRAILER"] = "Content-MD5".freeze
19
18
  end
20
- env["HTTP_TRANSFER_ENCODING"] = "chunked"
19
+ env["HTTP_TRANSFER_ENCODING"] = "chunked".freeze
21
20
  @to_io, wr = HTTP_Spew::ChunkyPipe.new
22
21
  expect_md5 = env.delete("HTTP_CONTENT_MD5")
23
22
  expect_len = env.delete("CONTENT_LENGTH")
@@ -41,8 +40,9 @@ class HTTP_Spew::ContentMD5
41
40
  @bytes_digested += n
42
41
  wr.write("#{n.to_s(16)}\r\n")
43
42
  digest.update(buf)
44
- wr.write(buf << CRLF)
43
+ wr.write(buf << "\r\n".freeze)
45
44
  end while input.read(0x4000, buf)
45
+ buf.clear
46
46
  end
47
47
  if expect_len && expect_len.to_i != @bytes_digested
48
48
  raise HTTP_Spew::LengthError,
@@ -1,18 +1,10 @@
1
1
  # -*- encoding: binary -*-
2
2
  module HTTP_Spew::Headers
3
- # :stopdoc:
4
- REQUEST_METHOD = "REQUEST_METHOD"
5
- REQUEST_URI = "REQUEST_URI"
6
- CRLF = "\r\n"
7
- QUERY_STRING = "QUERY_STRING"
8
- PATH_INFO = "PATH_INFO"
9
- CONTENT_TYPE = "CONTENT_TYPE" # specified by Rack to be !/^HTTP_/
10
- # :startdoc:
11
3
 
12
4
  # regenerates the request_uri from a Rack +env+
13
5
  def request_uri(env)
14
- qs = env[QUERY_STRING]
15
- qs.size == 0 ? env[PATH_INFO] : "#{env[PATH_INFO]}?#{qs}"
6
+ qs = env['QUERY_STRING']
7
+ qs.size == 0 ? env['PATH_INFO'] : "#{env['PATH_INFO']}?#{qs}"
16
8
  end
17
9
  module_function :request_uri
18
10
 
@@ -27,26 +19,25 @@ module HTTP_Spew::Headers
27
19
  #
28
20
  # buf, input = env_to_headers(env, input)
29
21
  def env_to_headers(env, input)
30
- req = "#{env[REQUEST_METHOD]} " \
31
- "#{env[REQUEST_URI] || request_uri(env)} HTTP/1.1\r\n" \
22
+ req = "#{env['REQUEST_METHOD']} " \
23
+ "#{env['REQUEST_URI'] || request_uri(env)} HTTP/1.1\r\n" \
32
24
  "Connection: close\r\n"
33
- uscore, dash = "_", "-"
34
25
  env.each do |key,value|
35
26
  %r{\AHTTP_(\w+)\z} =~ key or next
36
27
  key = $1
37
28
  %r{\A(?:VERSION|EXPECT|TRANSFER_ENCODING|CONNECTION|KEEP_ALIVE)\z}x =~
38
29
  key and next
39
30
 
40
- key.tr!(uscore, dash)
31
+ key.tr!('_'.freeze, '-'.freeze)
41
32
  req << "#{key}: #{value}\r\n"
42
33
  end
43
34
  if input
44
35
  req << (input.respond_to?(:size) ?
45
36
  "Content-Length: #{input.size}\r\n" :
46
- "Transfer-Encoding: chunked\r\n")
47
- ct = env[CONTENT_TYPE] and req << "Content-Type: #{ct}\r\n"
37
+ "Transfer-Encoding: chunked\r\n".freeze)
38
+ ct = env['CONTENT_TYPE'] and req << "Content-Type: #{ct}\r\n"
48
39
  end
49
- req << CRLF
40
+ req << "\r\n".freeze
50
41
  String === input ? (req << input) : [ req, input ]
51
42
  end
52
43
  module_function :env_to_headers
@@ -35,6 +35,7 @@ class HTTP_Spew::InputSpray
35
35
  @pipes.delete_if { |rd, wr| write_fail?(rd, wr, buf) }.empty? and
36
36
  raise HTTP_Spew::NoWritersError, "all writers have died", []
37
37
  end
38
+ buf.clear
38
39
  rescue => e
39
40
  @pipes.each { |rd, _| rd.error = e }
40
41
  ensure