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 CHANGED
@@ -1,3 +1,7 @@
1
+ == 0.6.11 / 2009-10-06
2
+ * split off rbkb/http as its own package
3
+ * added some new String crypto utilities in extends
4
+
1
5
  == 0.6.10 / 2009-09-30
2
6
  * added -b/--blitsrv argument to plugsrv
3
7
  * added --target-tls/--server-tls arguments to plugsrv for TLS support
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
- * http://www.matasano.com/log/1048/blackbag-091-new-link-and-minor-fixes/
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 from github:
83
+ rbkb is available as a gem on gemcutter.org:
85
84
 
86
- gem sources -a http://gems.github.com #(you only have to do this once)
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 emonti-rbkb
102
+ gem contents rbkb
105
103
 
106
104
 
107
105
  === Manual installation:
data/lib/rbkb.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  module Rbkb
3
3
 
4
4
  # :stopdoc:
5
- VERSION = '0.6.10'
5
+ VERSION = '0.6.11'
6
6
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
7
7
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
8
8
  # :startdoc:
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.10"
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-01}
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/http.rb", "lib/rbkb/http/base.rb", "lib/rbkb/http/body.rb", "lib/rbkb/http/common.rb", "lib/rbkb/http/headers.rb", "lib/rbkb/http/parameters.rb", "lib/rbkb/http/request.rb", "lib/rbkb/http/response.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_http.rb", "test/test_http_helper.rb", "test/test_http_request.rb", "test/test_http_response.rb", "test/test_rbkb.rb"]
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/test_http.rb", "test/test_http_helper.rb", "test/test_http_request.rb", "test/test_http_response.rb", "test/test_rbkb.rb"]
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.10
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-01 00:00:00 -04:00
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
-
@@ -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
-
@@ -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
-