http_spew 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.olddoc.yml +0 -1
- data/GIT-VERSION-GEN +1 -1
- data/http_spew.gemspec +12 -15
- data/lib/http_spew.rb +1 -1
- data/lib/http_spew/chunky_pipe.rb +14 -3
- data/lib/http_spew/class_methods.rb +31 -3
- data/lib/http_spew/request.rb +37 -19
- data/test/test_hit_n_run.rb +4 -4
- data/test/test_request.rb +4 -6
- data/test/test_unexpected_response.rb +1 -1
- metadata +7 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6f78a87db667171d544603aa12a83475dab20545
|
4
|
+
data.tar.gz: 5f487539a9d355cbc5a979cab70ac4b6788253ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 64a5f0b76fa4d70ffeef6de7120c51677a503bba55972d7063ded10532453bb3c8838cee0445c86da0f5f067a7977a8fd026a31bc399b0bde7c60811c0d33c2c
|
7
|
+
data.tar.gz: e22a30b09ab4b87c395084395da3ff477823cf5e28caf048e77ccd56d86692c3cde5c22629c18751662df2084fea6cfdd3fc3752d5a29b9f0ccb0837163e711a
|
data/.olddoc.yml
CHANGED
data/GIT-VERSION-GEN
CHANGED
data/http_spew.gemspec
CHANGED
@@ -1,23 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'olddoc'
|
4
|
-
extend Olddoc::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[
|
10
|
-
s.authors = ["
|
11
|
-
s.description =
|
12
|
-
s.email = %q{
|
13
|
-
s.extra_rdoc_files =
|
6
|
+
s.version = (ENV['VERSION'] || '0.7.0').dup
|
7
|
+
s.authors = ["http_spew hackers"]
|
8
|
+
s.description = File.read('README').split("\n\n")[1]
|
9
|
+
s.email = %q{http_spew-public@bogomips.org}
|
10
|
+
s.extra_rdoc_files = IO.readlines('.document').map!(&:chomp!).keep_if do |f|
|
11
|
+
File.exist?(f)
|
12
|
+
end
|
14
13
|
s.files = manifest
|
15
|
-
s.homepage =
|
16
|
-
s.summary =
|
14
|
+
s.homepage = 'https://bogomips.org/http_spew/'
|
15
|
+
s.summary = 'HTTP Spew - LAN-only HTTP spam^H^H^H^Hclient for Ruby'
|
17
16
|
s.test_files = Dir["test/test_*.rb"]
|
18
17
|
s.add_dependency(%q<kcar>, [ "~> 0.3", ">= 0.3.1"])
|
19
|
-
s.
|
20
|
-
s.add_development_dependency(%q<olddoc>, "~> 1.0")
|
21
|
-
s.required_ruby_version = '>= 2.1'
|
18
|
+
s.required_ruby_version = '>= 2.3'
|
22
19
|
s.licenses = %w(GPL-2.0+)
|
23
20
|
end
|
data/lib/http_spew.rb
CHANGED
@@ -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 <
|
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(
|
18
|
+
def read(len = 16384, buf = '')
|
15
19
|
check_err!
|
16
|
-
|
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!
|
@@ -53,7 +53,7 @@ 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 =
|
56
|
+
r, w = IO.pipe
|
57
57
|
active = []
|
58
58
|
t = [ timeout ]
|
59
59
|
requests.each_with_index do |req, i|
|
@@ -68,7 +68,7 @@ module HTTP_Spew::ClassMethods
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
begin
|
71
|
-
with_timeout(t) { r.
|
71
|
+
with_timeout(t) { r.wait_readable(t[0]) }
|
72
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]) }
|
@@ -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) {
|
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
|
data/lib/http_spew/request.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
require 'socket'
|
3
|
+
|
2
4
|
# This is the base class actually capable of making a normal HTTP request
|
3
5
|
class HTTP_Spew::Request
|
4
6
|
|
@@ -21,10 +23,9 @@ class HTTP_Spew::Request
|
|
21
23
|
#
|
22
24
|
# +sock+ may be the String representing an address created with
|
23
25
|
# +Socket.pack_sockaddr_un+ or +Socket.pack_sockaddr_in+, or it
|
24
|
-
# may be an actual
|
25
|
-
# (e.g. Kgio::Socket)
|
26
|
+
# may be an actual Socket object
|
26
27
|
def initialize(env, input, sock, allow = nil)
|
27
|
-
@to_io =
|
28
|
+
@to_io = BasicSocket === sock ? sock : start_sock(sock)
|
28
29
|
if Hash === env
|
29
30
|
@buf, @input = env_to_headers(env, input)
|
30
31
|
else
|
@@ -37,18 +38,21 @@ class HTTP_Spew::Request
|
|
37
38
|
# returns :wait_readable or :wait_writable if busy
|
38
39
|
def resume
|
39
40
|
if @buf
|
40
|
-
case
|
41
|
-
when
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
41
|
+
case w = @to_io.write_nonblock(@buf, exception: false)
|
42
|
+
when :wait_writable, :wait_readable
|
43
|
+
return w
|
44
|
+
else # Integer
|
45
|
+
len = @buf.size
|
46
|
+
if w == len
|
47
|
+
@buf = @input ? @input.read(0x4000, @buf) : nil
|
48
|
+
else
|
49
|
+
tmp = @buf.byteslice(w, len - w)
|
50
|
+
@buf.clear
|
51
|
+
@buf = tmp # loop retry, socket buffer could've expanded
|
52
|
+
end
|
47
53
|
end while @buf
|
48
|
-
read_response
|
49
|
-
else
|
50
|
-
read_response
|
51
54
|
end
|
55
|
+
read_response
|
52
56
|
end
|
53
57
|
|
54
58
|
# returns a 3-element Rack response array on successful completion
|
@@ -64,7 +68,7 @@ class HTTP_Spew::Request
|
|
64
68
|
timeout -= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0)
|
65
69
|
while :wait_readable == (rv = read_response) && timeout >= 0.0
|
66
70
|
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
67
|
-
@to_io.
|
71
|
+
@to_io.wait_readable(timeout) if timeout > 0.0
|
68
72
|
timeout -= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0)
|
69
73
|
end
|
70
74
|
rv
|
@@ -78,7 +82,7 @@ class HTTP_Spew::Request
|
|
78
82
|
# Users do not need to call this directly, +resume+ will return the result
|
79
83
|
# of this.
|
80
84
|
def read_response
|
81
|
-
buf = @to_io.
|
85
|
+
buf = @to_io.recv_nonblock(0x4000, Socket::MSG_PEEK, exception: false) or
|
82
86
|
raise HttpSpew::EOF, "upstream server closed connection", []
|
83
87
|
String === buf or return buf
|
84
88
|
|
@@ -91,7 +95,7 @@ class HTTP_Spew::Request
|
|
91
95
|
end
|
92
96
|
|
93
97
|
# discard the header data from the socket buffer
|
94
|
-
(hdr_len -= buf.size) > 0 and @to_io.
|
98
|
+
(hdr_len -= buf.size) > 0 and @to_io.read(hdr_len, buf)
|
95
99
|
@response = r << self
|
96
100
|
end
|
97
101
|
|
@@ -102,10 +106,15 @@ class HTTP_Spew::Request
|
|
102
106
|
# Called by Rack servers to write the response to a client
|
103
107
|
def each
|
104
108
|
buf = ""
|
105
|
-
|
109
|
+
case @to_io.read_nonblock(0x4000, buf, exception: false)
|
110
|
+
when :wait_readable
|
111
|
+
@to_io.wait_readable
|
112
|
+
when nil
|
113
|
+
buf.clear
|
114
|
+
return
|
115
|
+
else
|
106
116
|
yield buf
|
107
|
-
end
|
108
|
-
buf.clear
|
117
|
+
end while true
|
109
118
|
end
|
110
119
|
|
111
120
|
# Used internally by various HTTP_Spew elements to report errors
|
@@ -121,4 +130,13 @@ class HTTP_Spew::Request
|
|
121
130
|
@to_io.close
|
122
131
|
@input = nil
|
123
132
|
end
|
133
|
+
|
134
|
+
def start_sock(ai)
|
135
|
+
ai = Addrinfo.new(ai) unless Addrinfo === ai
|
136
|
+
sock = Socket.new(ai.afamily, :SOCK_STREAM)
|
137
|
+
case sock.connect_nonblock(ai, exception: false)
|
138
|
+
when 0, :wait_writable
|
139
|
+
end
|
140
|
+
sock
|
141
|
+
end
|
124
142
|
end
|
data/test/test_hit_n_run.rb
CHANGED
@@ -19,7 +19,8 @@ class TestHitNRun < Test::Unit::TestCase
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_request_with_existing_socket
|
22
|
-
sock =
|
22
|
+
sock = TCPSocket.new(@addr, @port)
|
23
|
+
assert(BasicSocket === sock)
|
23
24
|
req = HTTP_Spew::HitNRun.new(@env, nil, sock)
|
24
25
|
assert_equal sock, req.to_io
|
25
26
|
assert_nothing_raised { req.close }
|
@@ -30,8 +31,7 @@ class TestHitNRun < Test::Unit::TestCase
|
|
30
31
|
req = HTTP_Spew::HitNRun.new(@env, nil, @sockaddr)
|
31
32
|
sym = req.resume
|
32
33
|
if sym == :wait_writable
|
33
|
-
|
34
|
-
assert_equal [ req ], set.keys
|
34
|
+
assert req.to_io.wait_writable(0.1)
|
35
35
|
sym = req.resume
|
36
36
|
end
|
37
37
|
assert_equal HTTP_Spew::HitNRun::RESPONSE.object_id, sym.object_id
|
@@ -40,7 +40,7 @@ class TestHitNRun < Test::Unit::TestCase
|
|
40
40
|
def test_request_loop
|
41
41
|
req = HTTP_Spew::HitNRun.new(@env, nil, @sockaddr)
|
42
42
|
until Array === (rv = req.resume)
|
43
|
-
|
43
|
+
req.__send__(rv)
|
44
44
|
end
|
45
45
|
assert_equal HTTP_Spew::HitNRun::RESPONSE.object_id, rv.object_id
|
46
46
|
end
|
data/test/test_request.rb
CHANGED
@@ -19,7 +19,7 @@ class TestRequest < Test::Unit::TestCase
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_request_with_existing_socket
|
22
|
-
sock =
|
22
|
+
sock = TCPSocket.new(@addr, @port)
|
23
23
|
req = HTTP_Spew::Request.new(@env, nil, sock)
|
24
24
|
assert_equal sock, req.to_io
|
25
25
|
assert_nothing_raised { req.close }
|
@@ -31,13 +31,11 @@ class TestRequest < Test::Unit::TestCase
|
|
31
31
|
sym = req.resume
|
32
32
|
assert_kind_of(Symbol, sym)
|
33
33
|
if sym == :wait_writable
|
34
|
-
|
35
|
-
assert_equal [ req ], set.keys
|
34
|
+
assert req.to_io.wait_writable(1)
|
36
35
|
sym = req.resume
|
37
36
|
end
|
38
37
|
assert_equal :wait_readable, sym
|
39
|
-
|
40
|
-
assert_equal [ req ], set.keys
|
38
|
+
assert req.to_io.wait_readable(1)
|
41
39
|
rv = req.resume
|
42
40
|
assert_equal req, rv[2]
|
43
41
|
end
|
@@ -45,7 +43,7 @@ class TestRequest < Test::Unit::TestCase
|
|
45
43
|
def test_request_loop
|
46
44
|
req = HTTP_Spew::Request.new(@env, nil, @sockaddr)
|
47
45
|
until Array === (rv = req.resume)
|
48
|
-
|
46
|
+
req.to_io.__send__(rv) # wait_readable/wait_writable
|
49
47
|
end
|
50
48
|
assert_kind_of Array, rv
|
51
49
|
assert_equal 3, rv.size
|
@@ -19,7 +19,7 @@ class TestRequest < Test::Unit::TestCase
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_request_with_existing_socket
|
22
|
-
sock =
|
22
|
+
sock = TCPSocket.new(@addr, @port)
|
23
23
|
req = HTTP_Spew::Request.new(@env, nil, sock)
|
24
24
|
assert_equal sock, req.to_io
|
25
25
|
assert_nothing_raised { req.close }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: http_spew
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- http_spew hackers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kcar
|
@@ -30,39 +30,11 @@ dependencies:
|
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 0.3.1
|
33
|
-
- !ruby/object:Gem::Dependency
|
34
|
-
name: kgio
|
35
|
-
requirement: !ruby/object:Gem::Requirement
|
36
|
-
requirements:
|
37
|
-
- - "~>"
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '2.6'
|
40
|
-
type: :runtime
|
41
|
-
prerelease: false
|
42
|
-
version_requirements: !ruby/object:Gem::Requirement
|
43
|
-
requirements:
|
44
|
-
- - "~>"
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '2.6'
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: olddoc
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
-
requirements:
|
51
|
-
- - "~>"
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: '1.0'
|
54
|
-
type: :development
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
requirements:
|
58
|
-
- - "~>"
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
version: '1.0'
|
61
33
|
description: |-
|
62
34
|
Do not use HTTP Spew for connecting to servers outside of your LAN.
|
63
35
|
Do not use HTTP Spew without the permission of your server admins.
|
64
36
|
Use HTTP Spew if you wish you could kinda multicast with HTTP...
|
65
|
-
email:
|
37
|
+
email: http_spew-public@bogomips.org
|
66
38
|
executables: []
|
67
39
|
extensions: []
|
68
40
|
extra_rdoc_files:
|
@@ -70,15 +42,6 @@ extra_rdoc_files:
|
|
70
42
|
- README
|
71
43
|
- NEWS
|
72
44
|
- LATEST
|
73
|
-
- lib/http_spew.rb
|
74
|
-
- lib/http_spew/chunky_pipe.rb
|
75
|
-
- lib/http_spew/class_methods.rb
|
76
|
-
- lib/http_spew/content_md5.rb
|
77
|
-
- lib/http_spew/headers.rb
|
78
|
-
- lib/http_spew/hit_n_run.rb
|
79
|
-
- lib/http_spew/input_spray.rb
|
80
|
-
- lib/http_spew/request.rb
|
81
|
-
- lib/http_spew/version.rb
|
82
45
|
files:
|
83
46
|
- ".document"
|
84
47
|
- ".gitignore"
|
@@ -131,7 +94,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
131
94
|
requirements:
|
132
95
|
- - ">="
|
133
96
|
- !ruby/object:Gem::Version
|
134
|
-
version: '2.
|
97
|
+
version: '2.3'
|
135
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
99
|
requirements:
|
137
100
|
- - ">="
|
@@ -139,10 +102,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
102
|
version: '0'
|
140
103
|
requirements: []
|
141
104
|
rubyforge_project:
|
142
|
-
rubygems_version: 2.6.
|
105
|
+
rubygems_version: 2.6.12
|
143
106
|
signing_key:
|
144
107
|
specification_version: 4
|
145
|
-
summary: LAN-only HTTP spam^H^H^H^Hclient for Ruby
|
108
|
+
summary: HTTP Spew - LAN-only HTTP spam^H^H^H^Hclient for Ruby
|
146
109
|
test_files:
|
147
110
|
- test/test_input_spray_with_md5.rb
|
148
111
|
- test/test_mirror.rb
|