rbkb 0.6.10 → 0.6.11
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/README.rdoc +5 -7
- data/lib/rbkb.rb +1 -1
- data/lib/rbkb/extends.rb +63 -2
- data/rbkb.gemspec +4 -4
- metadata +2 -18
- data/lib/rbkb/http.rb +0 -21
- data/lib/rbkb/http/base.rb +0 -172
- data/lib/rbkb/http/body.rb +0 -214
- data/lib/rbkb/http/common.rb +0 -74
- data/lib/rbkb/http/headers.rb +0 -370
- data/lib/rbkb/http/parameters.rb +0 -104
- data/lib/rbkb/http/request.rb +0 -58
- data/lib/rbkb/http/response.rb +0 -86
- data/test/test_http.rb +0 -27
- data/test/test_http_helper.rb +0 -60
- data/test/test_http_request.rb +0 -136
- data/test/test_http_response.rb +0 -222
data/History.txt
CHANGED
data/README.rdoc
CHANGED
@@ -17,9 +17,8 @@ related to pen-testing and reversing.
|
|
17
17
|
|
18
18
|
rbkb is inspired by Matasano BlackBag (a set of similar tools written in C).
|
19
19
|
|
20
|
-
See:
|
21
|
-
|
22
|
-
* http://www.matasano.com/log/552/code-release-blackbag-09-binary-protocol-reversing-unix-thingies/
|
20
|
+
See:
|
21
|
+
blackbag - http://github.com/emonti/rbkb/raw/master/reference/blackbag-0.9.1.tgz
|
23
22
|
|
24
23
|
Things go into the black bag as they are stolen (as a compliment!) or dreamed
|
25
24
|
up, usually for simplifying some repetetive task or a desire for a new tool.
|
@@ -81,10 +80,9 @@ your irb sessions and own scripts. See 'lib_usage.rdoc' for more info.
|
|
81
80
|
|
82
81
|
=== Gem Installation
|
83
82
|
|
84
|
-
rbkb is available as a gem
|
83
|
+
rbkb is available as a gem on gemcutter.org:
|
85
84
|
|
86
|
-
gem
|
87
|
-
gem install emonti-rbkb
|
85
|
+
gem install rbkb --source http://gemcutter.org
|
88
86
|
|
89
87
|
|
90
88
|
==== Gem Install Note
|
@@ -101,7 +99,7 @@ executable bin/* files somewhere unexpected. To find out where these are and
|
|
101
99
|
either add them to your PATH or copy/symlink them somewhere else like
|
102
100
|
/usr/local/bin/ do this:
|
103
101
|
|
104
|
-
gem contents
|
102
|
+
gem contents rbkb
|
105
103
|
|
106
104
|
|
107
105
|
=== Manual installation:
|
data/lib/rbkb.rb
CHANGED
data/lib/rbkb/extends.rb
CHANGED
@@ -19,7 +19,7 @@ end
|
|
19
19
|
# Generates a random string of 'size' bytes (8 by default)
|
20
20
|
def random_string(size = 8)
|
21
21
|
chars = (0..255).map {|c| c.chr }
|
22
|
-
(1..size).collect {|a| chars[rand(chars.size)]}
|
22
|
+
(1..size).collect {|a| chars[rand(chars.size)]}.join
|
23
23
|
end
|
24
24
|
|
25
25
|
# Simple syntactic sugar to pass any object to a block
|
@@ -194,6 +194,32 @@ class String
|
|
194
194
|
e
|
195
195
|
end
|
196
196
|
|
197
|
+
|
198
|
+
# Produces a character frequency distribution histogram in descending
|
199
|
+
# order. Example:
|
200
|
+
#
|
201
|
+
# pp some_english_text.char_frequency()
|
202
|
+
#
|
203
|
+
# [[" ", 690],
|
204
|
+
# ["e", 354],
|
205
|
+
# ["t", 242],
|
206
|
+
# ["o", 233],
|
207
|
+
# ["i", 218],
|
208
|
+
# ["a", 176],
|
209
|
+
# ["s", 172],
|
210
|
+
# ["r", 172],
|
211
|
+
# ["n", 167],
|
212
|
+
# ["d", 106],
|
213
|
+
# ...
|
214
|
+
# ]
|
215
|
+
#
|
216
|
+
def char_frequency
|
217
|
+
hits = {}
|
218
|
+
self.each_byte {|c| hits[c.chr] ||= 0; hits[c.chr] += 1 }
|
219
|
+
hits.to_a.sort {|a,b| b[1] <=> a[1] }
|
220
|
+
end
|
221
|
+
|
222
|
+
|
197
223
|
# xor against a key. key will be repeated or truncated to self.size.
|
198
224
|
def xor(k)
|
199
225
|
s=self
|
@@ -205,12 +231,30 @@ class String
|
|
205
231
|
out.string
|
206
232
|
end
|
207
233
|
|
234
|
+
|
235
|
+
# (en|de)ciphers using a substition cipher en/decoder ring in the form of a
|
236
|
+
# hash with orig => substitute mappings
|
237
|
+
def substitution(keymap)
|
238
|
+
split('').map {|c| (sub=keymap[c]) ? sub : c }.join
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
# (en|de)crypts using a substition xor en/decoder ring in the form of
|
243
|
+
# a hash with orig => substitute mappings. Used in conjunction with
|
244
|
+
# char_frequency, this sometimes provides a shorter way to derive a single
|
245
|
+
# character xor key used in conjunction with char_frequency.
|
246
|
+
def substitution_xor(keymap)
|
247
|
+
split('').map {|c| (sub=keymap[c]) ? sub.xor(c) : c }.join
|
248
|
+
end
|
249
|
+
|
250
|
+
|
208
251
|
# convert bytes to number then xor against another byte-string or number
|
209
252
|
def ^(x)
|
210
253
|
x = x.dat_to_num unless x.is_a? Numeric
|
211
254
|
(self.dat_to_num ^ x)#.to_bytes
|
212
255
|
end
|
213
256
|
|
257
|
+
|
214
258
|
# Byte rotation as found in lame ciphers.
|
215
259
|
# This was cribbed from Timur Duehr with only a minor change.
|
216
260
|
def rotate_bytes(k=0)
|
@@ -223,9 +267,11 @@ class String
|
|
223
267
|
return r
|
224
268
|
end
|
225
269
|
|
270
|
+
|
226
271
|
# String randomizer
|
227
272
|
def randomize ; self.split('').randomize.to_s ; end
|
228
273
|
|
274
|
+
|
229
275
|
# In-place string randomizer
|
230
276
|
def randomize! ; self.replace(randomize) end
|
231
277
|
|
@@ -552,7 +598,6 @@ class String
|
|
552
598
|
end
|
553
599
|
r
|
554
600
|
end
|
555
|
-
|
556
601
|
|
557
602
|
|
558
603
|
# Returns a reference to actual constant for a given name in namespace
|
@@ -579,6 +624,22 @@ class Symbol
|
|
579
624
|
end
|
580
625
|
|
581
626
|
class Array
|
627
|
+
|
628
|
+
# Should be in the std library.
|
629
|
+
#
|
630
|
+
# keys = [:one, :two, :three]
|
631
|
+
# vals = [1, 2, 3]
|
632
|
+
#
|
633
|
+
# keys.zip(vals).to_hash
|
634
|
+
# #=> {:two=>2, :three=>3, :one=>1}})
|
635
|
+
#
|
636
|
+
# keys.to_hash(vals)
|
637
|
+
# #=> {:two=>2, :three=>3, :one=>1}})
|
638
|
+
def to_hash(vals=nil)
|
639
|
+
a = vals ? self.zip(vals) : self
|
640
|
+
a.inject({}) {|hash, i| hash[i[0]] = i[1]; hash}
|
641
|
+
end
|
642
|
+
|
582
643
|
# randomizes the order of contents in the Array (self)
|
583
644
|
def randomize ; self.sort_by { rand } ; end
|
584
645
|
|
data/rbkb.gemspec
CHANGED
@@ -2,23 +2,23 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{rbkb}
|
5
|
-
s.version = "0.6.
|
5
|
+
s.version = "0.6.11"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Eric Monti"]
|
9
|
-
s.date = %q{2009-10-
|
9
|
+
s.date = %q{2009-10-07}
|
10
10
|
s.description = %q{Rbkb is a collection of ruby-based pen-testing and reversing tools. Inspired by Matasano Blackbag.}
|
11
11
|
s.email = %q{emonti@matasano.com}
|
12
12
|
s.executables = ["b64", "bgrep", "blit", "c", "crc32", "d64", "dedump", "feed", "hexify", "len", "plugsrv", "rex", "rstrings", "slice", "telson", "unhexify", "urldec", "urlenc", "xor"]
|
13
13
|
s.extra_rdoc_files = ["History.txt", "README.rdoc", "bin/b64", "bin/bgrep", "bin/blit", "bin/c", "bin/crc32", "bin/d64", "bin/dedump", "bin/feed", "bin/hexify", "bin/len", "bin/plugsrv", "bin/rex", "bin/rstrings", "bin/slice", "bin/telson", "bin/unhexify", "bin/urldec", "bin/urlenc", "bin/xor", "cli_usage.rdoc", "lib_usage.rdoc"]
|
14
|
-
s.files = ["History.txt", "README.rdoc", "Rakefile", "bin/b64", "bin/bgrep", "bin/blit", "bin/c", "bin/crc32", "bin/d64", "bin/dedump", "bin/feed", "bin/hexify", "bin/len", "bin/plugsrv", "bin/rex", "bin/rstrings", "bin/slice", "bin/telson", "bin/unhexify", "bin/urldec", "bin/urlenc", "bin/xor", "cli_usage.rdoc", "doctor-bag.jpg", "lib/rbkb.rb", "lib/rbkb/cli.rb", "lib/rbkb/cli/b64.rb", "lib/rbkb/cli/bgrep.rb", "lib/rbkb/cli/blit.rb", "lib/rbkb/cli/chars.rb", "lib/rbkb/cli/crc32.rb", "lib/rbkb/cli/d64.rb", "lib/rbkb/cli/dedump.rb", "lib/rbkb/cli/feed.rb", "lib/rbkb/cli/hexify.rb", "lib/rbkb/cli/len.rb", "lib/rbkb/cli/rstrings.rb", "lib/rbkb/cli/slice.rb", "lib/rbkb/cli/telson.rb", "lib/rbkb/cli/unhexify.rb", "lib/rbkb/cli/urldec.rb", "lib/rbkb/cli/urlenc.rb", "lib/rbkb/cli/xor.rb", "lib/rbkb/extends.rb", "lib/rbkb/
|
14
|
+
s.files = ["History.txt", "README.rdoc", "Rakefile", "bin/b64", "bin/bgrep", "bin/blit", "bin/c", "bin/crc32", "bin/d64", "bin/dedump", "bin/feed", "bin/hexify", "bin/len", "bin/plugsrv", "bin/rex", "bin/rstrings", "bin/slice", "bin/telson", "bin/unhexify", "bin/urldec", "bin/urlenc", "bin/xor", "cli_usage.rdoc", "doctor-bag.jpg", "lib/rbkb.rb", "lib/rbkb/cli.rb", "lib/rbkb/cli/b64.rb", "lib/rbkb/cli/bgrep.rb", "lib/rbkb/cli/blit.rb", "lib/rbkb/cli/chars.rb", "lib/rbkb/cli/crc32.rb", "lib/rbkb/cli/d64.rb", "lib/rbkb/cli/dedump.rb", "lib/rbkb/cli/feed.rb", "lib/rbkb/cli/hexify.rb", "lib/rbkb/cli/len.rb", "lib/rbkb/cli/rstrings.rb", "lib/rbkb/cli/slice.rb", "lib/rbkb/cli/telson.rb", "lib/rbkb/cli/unhexify.rb", "lib/rbkb/cli/urldec.rb", "lib/rbkb/cli/urlenc.rb", "lib/rbkb/cli/xor.rb", "lib/rbkb/extends.rb", "lib/rbkb/plug.rb", "lib/rbkb/plug/blit.rb", "lib/rbkb/plug/cli.rb", "lib/rbkb/plug/feed_import.rb", "lib/rbkb/plug/peer.rb", "lib/rbkb/plug/plug.rb", "lib/rbkb/plug/proxy.rb", "lib/rbkb/plug/unix_domain.rb", "lib_usage.rdoc", "rbkb.gemspec", "spec/rbkb_spec.rb", "spec/spec_helper.rb", "tasks/ann.rake", "tasks/bones.rake", "tasks/gem.rake", "tasks/git.rake", "tasks/notes.rake", "tasks/post_load.rake", "tasks/rdoc.rake", "tasks/rubyforge.rake", "tasks/setup.rb", "tasks/spec.rake", "tasks/svn.rake", "tasks/test.rake", "test/test_cli_b64.rb", "test/test_cli_bgrep.rb", "test/test_cli_blit.rb", "test/test_cli_chars.rb", "test/test_cli_crc32.rb", "test/test_cli_d64.rb", "test/test_cli_dedump.rb", "test/test_cli_feed.rb", "test/test_cli_helper.rb", "test/test_cli_hexify.rb", "test/test_cli_len.rb", "test/test_cli_rstrings.rb", "test/test_cli_slice.rb", "test/test_cli_telson.rb", "test/test_cli_unhexify.rb", "test/test_cli_urldec.rb", "test/test_cli_urlenc.rb", "test/test_cli_xor.rb", "test/test_helper.rb", "test/test_rbkb.rb"]
|
15
15
|
s.homepage = %q{http://emonti.github.com/rbkb}
|
16
16
|
s.rdoc_options = ["--line-numbers", "--main", "README.rdoc"]
|
17
17
|
s.require_paths = ["lib"]
|
18
18
|
s.rubyforge_project = %q{rbkb}
|
19
19
|
s.rubygems_version = %q{1.3.4}
|
20
20
|
s.summary = %q{Rbkb is a collection of ruby-based pen-testing and reversing tools}
|
21
|
-
s.test_files = ["test/test_cli_b64.rb", "test/test_cli_bgrep.rb", "test/test_cli_blit.rb", "test/test_cli_chars.rb", "test/test_cli_crc32.rb", "test/test_cli_d64.rb", "test/test_cli_dedump.rb", "test/test_cli_feed.rb", "test/test_cli_helper.rb", "test/test_cli_hexify.rb", "test/test_cli_len.rb", "test/test_cli_rstrings.rb", "test/test_cli_slice.rb", "test/test_cli_telson.rb", "test/test_cli_unhexify.rb", "test/test_cli_urldec.rb", "test/test_cli_urlenc.rb", "test/test_cli_xor.rb", "test/test_helper.rb", "test/
|
21
|
+
s.test_files = ["test/test_cli_b64.rb", "test/test_cli_bgrep.rb", "test/test_cli_blit.rb", "test/test_cli_chars.rb", "test/test_cli_crc32.rb", "test/test_cli_d64.rb", "test/test_cli_dedump.rb", "test/test_cli_feed.rb", "test/test_cli_helper.rb", "test/test_cli_hexify.rb", "test/test_cli_len.rb", "test/test_cli_rstrings.rb", "test/test_cli_slice.rb", "test/test_cli_telson.rb", "test/test_cli_unhexify.rb", "test/test_cli_urldec.rb", "test/test_cli_urlenc.rb", "test/test_cli_xor.rb", "test/test_helper.rb", "test/test_rbkb.rb"]
|
22
22
|
|
23
23
|
if s.respond_to? :specification_version then
|
24
24
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbkb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Monti
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-07 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -125,14 +125,6 @@ files:
|
|
125
125
|
- lib/rbkb/cli/urlenc.rb
|
126
126
|
- lib/rbkb/cli/xor.rb
|
127
127
|
- lib/rbkb/extends.rb
|
128
|
-
- lib/rbkb/http.rb
|
129
|
-
- lib/rbkb/http/base.rb
|
130
|
-
- lib/rbkb/http/body.rb
|
131
|
-
- lib/rbkb/http/common.rb
|
132
|
-
- lib/rbkb/http/headers.rb
|
133
|
-
- lib/rbkb/http/parameters.rb
|
134
|
-
- lib/rbkb/http/request.rb
|
135
|
-
- lib/rbkb/http/response.rb
|
136
128
|
- lib/rbkb/plug.rb
|
137
129
|
- lib/rbkb/plug/blit.rb
|
138
130
|
- lib/rbkb/plug/cli.rb
|
@@ -176,10 +168,6 @@ files:
|
|
176
168
|
- test/test_cli_urlenc.rb
|
177
169
|
- test/test_cli_xor.rb
|
178
170
|
- test/test_helper.rb
|
179
|
-
- test/test_http.rb
|
180
|
-
- test/test_http_helper.rb
|
181
|
-
- test/test_http_request.rb
|
182
|
-
- test/test_http_response.rb
|
183
171
|
- test/test_rbkb.rb
|
184
172
|
has_rdoc: true
|
185
173
|
homepage: http://emonti.github.com/rbkb
|
@@ -231,8 +219,4 @@ test_files:
|
|
231
219
|
- test/test_cli_urlenc.rb
|
232
220
|
- test/test_cli_xor.rb
|
233
221
|
- test/test_helper.rb
|
234
|
-
- test/test_http.rb
|
235
|
-
- test/test_http_helper.rb
|
236
|
-
- test/test_http_request.rb
|
237
|
-
- test/test_http_response.rb
|
238
222
|
- test/test_rbkb.rb
|
data/lib/rbkb/http.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
|
2
|
-
# ???Why???? would anyone create their own HTTP implementation in ruby with
|
3
|
-
# so many options out there? Short answer: Net:HTTP and others just don't cut
|
4
|
-
# it in lots of edge cases. I needed something I could control completely.
|
5
|
-
|
6
|
-
module Rbkb
|
7
|
-
module Http
|
8
|
-
VERSION = "0.0.3"
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
require 'time' # gives us Time.httpdate parser and output methods
|
13
|
-
|
14
|
-
require "rbkb/http/common.rb"
|
15
|
-
require "rbkb/http/base.rb"
|
16
|
-
require "rbkb/http/request.rb"
|
17
|
-
require "rbkb/http/response.rb"
|
18
|
-
require "rbkb/http/headers.rb"
|
19
|
-
require "rbkb/http/body.rb"
|
20
|
-
require "rbkb/http/parameters.rb"
|
21
|
-
|
data/lib/rbkb/http/base.rb
DELETED
@@ -1,172 +0,0 @@
|
|
1
|
-
module Rbkb::Http
|
2
|
-
|
3
|
-
# A base class containing some common features for Request and Response
|
4
|
-
# objects.
|
5
|
-
#
|
6
|
-
# Don't use this class directly, it's intended for being overridden
|
7
|
-
# from its derived classes or mixins.
|
8
|
-
class Base
|
9
|
-
include CommonInterface
|
10
|
-
|
11
|
-
def self.parse(*args)
|
12
|
-
new(*args)
|
13
|
-
end
|
14
|
-
|
15
|
-
# Initializes a new Base object
|
16
|
-
def initialize(*args)
|
17
|
-
_common_init(*args)
|
18
|
-
end
|
19
|
-
|
20
|
-
# This method parses just HTTP message body. Expects body to be split
|
21
|
-
# from the headers before-hand.
|
22
|
-
def capture_body(bstr)
|
23
|
-
self.body ||= default_body_obj
|
24
|
-
@body.capture(bstr)
|
25
|
-
end
|
26
|
-
|
27
|
-
# XXX stub
|
28
|
-
def first_entity
|
29
|
-
@first_entity
|
30
|
-
end
|
31
|
-
|
32
|
-
# XXX stub
|
33
|
-
def first_entity=(f)
|
34
|
-
@first_entity=(f)
|
35
|
-
end
|
36
|
-
|
37
|
-
# This method parses only HTTP response headers. Expects headers to be
|
38
|
-
# split from the body before-hand.
|
39
|
-
def capture_headers(hstr)
|
40
|
-
self.headers ||= default_headers_obj
|
41
|
-
|
42
|
-
if @body and not @body.capture_complete?
|
43
|
-
return
|
44
|
-
elsif @headers.capture_complete?
|
45
|
-
self.first_entity, @headers = default_headers_obj.capture_full_headers(hstr)
|
46
|
-
else
|
47
|
-
@headers.capture(hstr)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# This method returns the content length from Headers. This is
|
52
|
-
# mostly useful if you are using a BoundBody object for the body.
|
53
|
-
#
|
54
|
-
# Returns nil if no "Content-Length" is not found.
|
55
|
-
#
|
56
|
-
# The opts parameter :ignore_content_length affects this method and
|
57
|
-
# will cause it always to return nil. This is useful, for example,
|
58
|
-
# for the responses to the HTTP HEAD request method, which return
|
59
|
-
# a Content-Length without actual content.
|
60
|
-
#
|
61
|
-
def content_length(hdrs=@headers)
|
62
|
-
raise "headers is nil?" if not hdrs
|
63
|
-
if( (not @opts[:ignore_content_length]) and
|
64
|
-
hdrs.get_header_value("Content-Length").to_s =~ /^(\d+)$/ )
|
65
|
-
|
66
|
-
$1.to_i
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def attach_new_header(hdr_obj=nil)
|
71
|
-
self.headers = hdr_obj
|
72
|
-
return hdr_obj
|
73
|
-
end
|
74
|
-
|
75
|
-
def attach_new_body(body_obj=nil)
|
76
|
-
self.body = body_obj
|
77
|
-
return body_obj
|
78
|
-
end
|
79
|
-
|
80
|
-
# XXX doc override!
|
81
|
-
def default_headers_obj(*args)
|
82
|
-
Header.new(*args)
|
83
|
-
end
|
84
|
-
|
85
|
-
# XXX doc override!
|
86
|
-
def default_body_obj(*args)
|
87
|
-
Body.new(*args)
|
88
|
-
end
|
89
|
-
|
90
|
-
# This method will non-destructively reset the capture state on this
|
91
|
-
# object and all child entities. Note, however, If child entities are not
|
92
|
-
# defined, it may instantiate new ones.
|
93
|
-
# See also: capture_complete?, reset_capture!
|
94
|
-
def reset_capture
|
95
|
-
if @headers
|
96
|
-
@headers.reset_capture if not @headers.capture_complete?
|
97
|
-
else
|
98
|
-
attach_new_header()
|
99
|
-
end
|
100
|
-
|
101
|
-
if @body
|
102
|
-
@body.reset_capture if not @body.capture_complete?
|
103
|
-
else
|
104
|
-
attach_new_body()
|
105
|
-
end
|
106
|
-
@capture_state = nil
|
107
|
-
self
|
108
|
-
end
|
109
|
-
|
110
|
-
# This method will destructively reset the capture state on this object.
|
111
|
-
# It does so by initializing fresh child entities and discarding the old
|
112
|
-
# ones. See also: capture_complete?, reset_capture
|
113
|
-
def reset_capture!
|
114
|
-
attach_new_header()
|
115
|
-
attach_new_body()
|
116
|
-
@capture_state = nil
|
117
|
-
self
|
118
|
-
end
|
119
|
-
|
120
|
-
# Indicates whether this object is ready to capture fresh data, or is
|
121
|
-
# waiting for additional data or a reset from a previous incomplete or
|
122
|
-
# otherwise broken capture. See also: reset_capture, reset_capture!
|
123
|
-
def capture_complete?
|
124
|
-
if( (@headers and not @headers.capture_complete?) or
|
125
|
-
(@body and not @body.capture_complete?) )
|
126
|
-
return false
|
127
|
-
else
|
128
|
-
true
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
attr_reader :body, :headers
|
133
|
-
|
134
|
-
# This accessor will attempt to always do the "right thing" while
|
135
|
-
# setting this object's body entity.
|
136
|
-
#
|
137
|
-
# See also: default_body_obj
|
138
|
-
def body=(b)
|
139
|
-
if @body
|
140
|
-
@body.data = b
|
141
|
-
elsif b.kind_of? Body
|
142
|
-
@body = b.dup
|
143
|
-
@body.opts = b.opts
|
144
|
-
else
|
145
|
-
@body = default_body_obj(b)
|
146
|
-
end
|
147
|
-
@body.base = self
|
148
|
-
return @body
|
149
|
-
end
|
150
|
-
|
151
|
-
# This accessor will attempt to always do the "right thing" while
|
152
|
-
# setting this object's headers entity.
|
153
|
-
#
|
154
|
-
# See also: default_headers_obj
|
155
|
-
def headers=(h)
|
156
|
-
if @headers
|
157
|
-
@headers.data = h
|
158
|
-
elsif h.kind_of? Headers
|
159
|
-
@headers = h.dup
|
160
|
-
@headers.opts = h.opts
|
161
|
-
else
|
162
|
-
@headers = default_headers_obj(h)
|
163
|
-
end
|
164
|
-
@headers.base = self
|
165
|
-
return @body
|
166
|
-
end
|
167
|
-
|
168
|
-
end
|
169
|
-
|
170
|
-
|
171
|
-
end
|
172
|
-
|
data/lib/rbkb/http/body.rb
DELETED
@@ -1,214 +0,0 @@
|
|
1
|
-
require 'stringio'
|
2
|
-
|
3
|
-
module Rbkb::Http
|
4
|
-
class Body < String
|
5
|
-
include CommonInterface
|
6
|
-
|
7
|
-
def self.parse(str)
|
8
|
-
new().capture(str)
|
9
|
-
end
|
10
|
-
|
11
|
-
attr_reader :expect_length
|
12
|
-
|
13
|
-
def initialize(str=nil, opts=nil)
|
14
|
-
self.opts = opts
|
15
|
-
if Body === str
|
16
|
-
self.replace(str)
|
17
|
-
@opts = str.opts.merge(@opts)
|
18
|
-
elsif String === str
|
19
|
-
super(str)
|
20
|
-
else
|
21
|
-
super()
|
22
|
-
end
|
23
|
-
|
24
|
-
yield(self) if block_given?
|
25
|
-
end
|
26
|
-
|
27
|
-
# The capture method is used when parsing HTTP requests/responses.
|
28
|
-
# This can and probably should be overridden in derived classes.
|
29
|
-
def capture(str)
|
30
|
-
yield(str) if block_given?
|
31
|
-
self.data=(str)
|
32
|
-
end
|
33
|
-
|
34
|
-
# The to_raw method is used when writing HTTP requests/responses.
|
35
|
-
# This can and probably should be overridden in derived classes.
|
36
|
-
def to_raw
|
37
|
-
(block_given?) ? yield(self.data) : self.data
|
38
|
-
end
|
39
|
-
|
40
|
-
attr_reader :base
|
41
|
-
|
42
|
-
def base=(b)
|
43
|
-
if b.nil? or b.is_a? Base
|
44
|
-
@base = b
|
45
|
-
else
|
46
|
-
raise "base must be a Response or Request object or nil"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def data
|
51
|
-
self
|
52
|
-
end
|
53
|
-
|
54
|
-
# Sets internal raw string data without any HTTP decoration.
|
55
|
-
def data=(str)
|
56
|
-
self.replace(str.to_s)
|
57
|
-
end
|
58
|
-
|
59
|
-
# Returns the content length from the HTTP base object if
|
60
|
-
# there is one and content-length is available.
|
61
|
-
def get_content_length
|
62
|
-
@base.content_length if @base
|
63
|
-
end
|
64
|
-
|
65
|
-
# This method will non-destructively reset the capture state on this object.
|
66
|
-
# It is non-destructive in that it will not affect existing captured data
|
67
|
-
# if present.
|
68
|
-
def reset_capture
|
69
|
-
@expect_length = nil
|
70
|
-
@base.reset_capture() if @base and @base.capture_complete?
|
71
|
-
end
|
72
|
-
|
73
|
-
# This method will destructively reset the capture state on this object.
|
74
|
-
# This method is destructive in that it will clear any previously captured
|
75
|
-
# data.
|
76
|
-
def reset_capture!
|
77
|
-
reset_capture()
|
78
|
-
self.data=""
|
79
|
-
end
|
80
|
-
|
81
|
-
def capture_complete?
|
82
|
-
not @expect_length
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
|
87
|
-
# BoundBody is designed for handling an HTTP body when using the usual
|
88
|
-
# "Content-Length: NNN" HTTP header.
|
89
|
-
class BoundBody < Body
|
90
|
-
|
91
|
-
# This method may throw :expect_length with one of the following values
|
92
|
-
# to indicate certain content-length conditions:
|
93
|
-
#
|
94
|
-
# > 0 : Got incomplete data in this capture. The object expects
|
95
|
-
# capture to be called again with more body data.
|
96
|
-
#
|
97
|
-
# < 0 : Got more data than expected, the caller should truncate and
|
98
|
-
# handle the extra data in some way. Note: Calling capture again
|
99
|
-
# on this instance will start a fresh body capture.
|
100
|
-
#
|
101
|
-
# Caller can also detect the above conditions by checking the expect_length
|
102
|
-
# attribute but should still be prepared handle the throw().
|
103
|
-
#
|
104
|
-
# 0/nil: Got exactly what was expected. Caller can proceed with fresh
|
105
|
-
# captures on this or other Body objects.
|
106
|
-
#
|
107
|
-
# See also reset_capture and reset_capture!
|
108
|
-
def capture(str)
|
109
|
-
raise "arg 0 must be a string" unless String === str
|
110
|
-
|
111
|
-
# Start fresh unless we're expecting more data
|
112
|
-
self.data="" unless @expect_length and @expect_length > 0
|
113
|
-
|
114
|
-
if not clen=get_content_length()
|
115
|
-
raise "content-length is unknown. aborting capture"
|
116
|
-
else
|
117
|
-
@expect_length = clen - (self.size + str.size)
|
118
|
-
self << str[0, clen - self.size]
|
119
|
-
if @expect_length > 0
|
120
|
-
throw(:expect_length, @expect_length)
|
121
|
-
elsif @expect_length < 0
|
122
|
-
throw(:expect_length, @expect_length)
|
123
|
-
else
|
124
|
-
reset_capture()
|
125
|
-
end
|
126
|
-
end
|
127
|
-
return self
|
128
|
-
end
|
129
|
-
|
130
|
-
def to_raw(*args)
|
131
|
-
if @base
|
132
|
-
@base.headers.set_header("Content-Length", self.size)
|
133
|
-
end
|
134
|
-
super(*args)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
|
139
|
-
# ChunkedBody is designed for handling an HTTP body when using a
|
140
|
-
# "Transfer-Encoding: chunked" HTTP header.
|
141
|
-
class ChunkedBody < Body
|
142
|
-
DEFAULT_CHUNK_SIZE = 2048
|
143
|
-
|
144
|
-
# Throws :expect_length with 'true' when given incomplete data and expects
|
145
|
-
# to be called again with more body data to parse.
|
146
|
-
#
|
147
|
-
# The caller can also detect this condition by checking the expect_length
|
148
|
-
# attribute but must still handle the throw().
|
149
|
-
#
|
150
|
-
# See also reset_capture and reset_capture!
|
151
|
-
def capture(str)
|
152
|
-
# chunked encoding is gross...
|
153
|
-
if @expect_length
|
154
|
-
sio = StringIO.new(@last_chunk.to_s + str)
|
155
|
-
else
|
156
|
-
sio = StringIO.new(str)
|
157
|
-
self.data=""
|
158
|
-
end
|
159
|
-
@last_chunk = nil
|
160
|
-
|
161
|
-
@expect_length = true
|
162
|
-
while not sio.eof?
|
163
|
-
unless m=/^([a-fA-F0-9]+)\s*(;[[:print:]\s]*)?\r?\n$/.match(line=sio.readline)
|
164
|
-
raise "invalid chunk at #{line.chomp.inspect}"
|
165
|
-
end
|
166
|
-
if (chunksz = m[1].hex) == 0
|
167
|
-
@expect_length = false
|
168
|
-
# XXX ignore Trailer headers
|
169
|
-
break
|
170
|
-
end
|
171
|
-
|
172
|
-
if ( (not sio.eof?) and
|
173
|
-
(chunk=sio.read(chunksz)) and
|
174
|
-
chunk.size == chunksz and
|
175
|
-
(not sio.eof?) and (extra = sio.readline) and
|
176
|
-
(not sio.eof?) and (extra << sio.readline)
|
177
|
-
)
|
178
|
-
if extra =~ /^\r?\n\r?\n$/
|
179
|
-
yield(chunk) if block_given?
|
180
|
-
self << chunk
|
181
|
-
else
|
182
|
-
raise "expected CRLF"
|
183
|
-
end
|
184
|
-
else
|
185
|
-
@last_chunk = line + chunk.to_s + extra.to_s
|
186
|
-
break
|
187
|
-
end
|
188
|
-
end
|
189
|
-
throw(:expect_length, @expect_length) if @expect_length
|
190
|
-
return self
|
191
|
-
end
|
192
|
-
|
193
|
-
|
194
|
-
def to_raw(csz=nil)
|
195
|
-
csz ||= (@opts[:output_chunk_size] || DEFAULT_CHUNK_SIZE)
|
196
|
-
unless csz.kind_of? Integer and csz > 0
|
197
|
-
raise "chunk size must be an integer >= 1"
|
198
|
-
end
|
199
|
-
|
200
|
-
out=[]
|
201
|
-
i=0
|
202
|
-
while i <= self.size
|
203
|
-
chunk = self[i, csz]
|
204
|
-
out << "#{chunk.size.to_s(16)}\r\n#{chunk}\r\n\r\n"
|
205
|
-
yield(self, out.last) if block_given?
|
206
|
-
i+=csz
|
207
|
-
end
|
208
|
-
out << "0\r\n"
|
209
|
-
yield(self, out.last) if block_given?
|
210
|
-
return out.join
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|