emonti-rbkb 0.6.2.1 → 0.6.6

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.
Files changed (78) hide show
  1. data/History.txt +32 -0
  2. data/README.rdoc +10 -7
  3. data/Rakefile +47 -0
  4. data/bin/feed +5 -0
  5. data/bin/plugsrv +3 -3
  6. data/cli_usage.rdoc +44 -9
  7. data/doctor-bag.jpg +0 -0
  8. data/lib/rbkb.rb +47 -2
  9. data/lib/rbkb/cli.rb +8 -6
  10. data/lib/rbkb/cli/b64.rb +5 -0
  11. data/lib/rbkb/cli/bgrep.rb +14 -9
  12. data/lib/rbkb/cli/chars.rb +2 -1
  13. data/lib/rbkb/cli/crc32.rb +4 -1
  14. data/lib/rbkb/cli/d64.rb +3 -0
  15. data/lib/rbkb/cli/dedump.rb +5 -3
  16. data/lib/rbkb/cli/feed.rb +223 -0
  17. data/lib/rbkb/cli/hexify.rb +3 -3
  18. data/lib/rbkb/cli/len.rb +12 -9
  19. data/lib/rbkb/cli/rstrings.rb +13 -10
  20. data/lib/rbkb/cli/slice.rb +1 -0
  21. data/lib/rbkb/cli/telson.rb +21 -57
  22. data/lib/rbkb/cli/unhexify.rb +2 -6
  23. data/lib/rbkb/cli/urldec.rb +1 -0
  24. data/lib/rbkb/cli/urlenc.rb +1 -0
  25. data/lib/rbkb/extends.rb +41 -6
  26. data/lib/rbkb/http.rb +20 -0
  27. data/lib/rbkb/http/base.rb +172 -0
  28. data/lib/rbkb/http/body.rb +214 -0
  29. data/lib/rbkb/http/common.rb +74 -0
  30. data/lib/rbkb/http/headers.rb +356 -0
  31. data/lib/rbkb/http/parameters.rb +101 -0
  32. data/lib/rbkb/http/request.rb +58 -0
  33. data/lib/rbkb/http/response.rb +86 -0
  34. data/lib/rbkb/plug.rb +3 -3
  35. data/lib/rbkb/plug/cli.rb +83 -0
  36. data/lib/rbkb/plug/feed_import.rb +74 -0
  37. data/lib/rbkb/plug/plug.rb +36 -19
  38. data/lib/rbkb/plug/unix_domain.rb +75 -0
  39. data/rbkb.gemspec +38 -0
  40. data/spec/rbkb_spec.rb +7 -0
  41. data/spec/spec_helper.rb +16 -0
  42. data/tasks/ann.rake +80 -0
  43. data/tasks/bones.rake +20 -0
  44. data/tasks/gem.rake +201 -0
  45. data/tasks/git.rake +40 -0
  46. data/tasks/notes.rake +27 -0
  47. data/tasks/post_load.rake +34 -0
  48. data/tasks/rdoc.rake +51 -0
  49. data/tasks/rubyforge.rake +55 -0
  50. data/tasks/setup.rb +292 -0
  51. data/tasks/spec.rake +54 -0
  52. data/tasks/svn.rake +47 -0
  53. data/tasks/test.rake +40 -0
  54. data/test/test_cli_b64.rb +35 -0
  55. data/test/test_cli_bgrep.rb +137 -0
  56. data/test/test_cli_blit.rb +11 -0
  57. data/test/test_cli_chars.rb +21 -0
  58. data/test/test_cli_crc32.rb +108 -0
  59. data/test/test_cli_d64.rb +22 -0
  60. data/test/test_cli_dedump.rb +118 -0
  61. data/test/test_cli_feed.rb +11 -0
  62. data/test/test_cli_helper.rb +96 -0
  63. data/test/test_cli_hexify.rb +63 -0
  64. data/test/test_cli_len.rb +96 -0
  65. data/test/test_cli_rstrings.rb +15 -0
  66. data/test/test_cli_slice.rb +73 -0
  67. data/test/test_cli_telson.rb +11 -0
  68. data/test/test_cli_unhexify.rb +43 -0
  69. data/test/test_cli_urldec.rb +50 -0
  70. data/test/test_cli_urlenc.rb +44 -0
  71. data/test/test_cli_xor.rb +71 -0
  72. data/test/test_helper.rb +5 -0
  73. data/test/test_http.rb +27 -0
  74. data/test/test_http_helper.rb +60 -0
  75. data/test/test_http_request.rb +136 -0
  76. data/test/test_http_response.rb +222 -0
  77. data/test/test_rbkb.rb +19 -0
  78. metadata +127 -21
@@ -40,13 +40,9 @@ class Rbkb::Cli::Unhexify < Rbkb::Cli::Executable
40
40
  @opts[:indat] ||= @stdin.read()
41
41
 
42
42
  @opts[:indat].delete!("\r\n")
43
- @opts[:delim] ||= /\s*/
43
+ @opts[:delim] ||= Regexp.new('\s*')
44
44
 
45
- unless out = @opts[:indat].unhexify(@opts[:delim])
46
- bail "Error: Failed parsing as hex"
47
- end
48
-
49
- @stdout << out
45
+ @stdout << @opts[:indat].unhexify(@opts[:delim])
50
46
 
51
47
  self.exit(0)
52
48
  end
@@ -29,6 +29,7 @@ class Rbkb::Cli::Urldec < Rbkb::Cli::Executable
29
29
  # Default to standard input
30
30
  @opts[:indat] ||= @stdin.read()
31
31
  @stdout << @opts[:indat].urldec(:noplus => @opts[:noplus])
32
+ self.exit(0)
32
33
  end
33
34
  end
34
35
 
@@ -30,5 +30,6 @@ class Rbkb::Cli::Urlenc < Rbkb::Cli::Executable
30
30
  # Default to standard input
31
31
  @opts[:indat] ||= @stdin.read()
32
32
  @stdout << @opts[:indat].urlenc(:plus => @opts[:plus]) + "\n"
33
+ self.exit(0)
33
34
  end
34
35
  end
data/lib/rbkb/extends.rb CHANGED
@@ -390,11 +390,9 @@ class String
390
390
  # If strings are null terminated, the trailing null *IS* included
391
391
  # in the end_offset. Unicode matches will also include null bytes.
392
392
  #
393
- # TODO?
393
+ # Todos?
394
394
  # - better unicode support (i.e. not using half-assed unicode)
395
395
  # - support other encodings such as all those the binutils strings does?
396
- # - not sure if we want the trailing null in null terminated strings
397
- # - not sure if we want wide characters to include their null bytes
398
396
  def strings(opts={})
399
397
  opts[:encoding] ||= :both
400
398
  prx = (opts[:valid] || /[\r\n [:print:]]/)
@@ -466,7 +464,7 @@ class String
466
464
 
467
465
  # returns CRC32 checksum for the string object
468
466
  def crc32
469
- ## XXX slower, but here for reference (found on some forum)
467
+ ## pure ruby version. slower, but here for reference (found on some forum)
470
468
  # r = 0xFFFFFFFF
471
469
  # self.each_byte do |b|
472
470
  # r ^= b
@@ -493,6 +491,34 @@ class String
493
491
  ret
494
492
  end
495
493
 
494
+ # Converts a '_' delimited string to CamelCase like 'foo_class' into
495
+ # 'FooClass'.
496
+ # See also: camelize_meth, decamelize
497
+ def camelize
498
+ self.gsub(/(^|_)([a-z])/) { $2.upcase }
499
+ end
500
+
501
+ # Converts a '_' delimited string to method style camelCase like 'foo_method'
502
+ # into 'fooMethod'.
503
+ # See also: camelize, decamelize
504
+ def camelize_meth
505
+ self.gsub(/_([a-z])/) { $1.upcase }
506
+ end
507
+
508
+
509
+ # Converts a CamelCase or camelCase string into '_' delimited form like
510
+ # 'FooBar' or 'fooBar' into 'foo_bar'.
511
+ #
512
+ # Note: This method only handles camel humps. Strings with consecutive
513
+ # uppercase chars like 'FooBAR' will be converted to 'foo_bar'
514
+ #
515
+ # See also: camelize, camelize_meth
516
+ def decamelize
517
+ self.gsub(/(^|[a-z])([A-Z])/) do
518
+ ($1.empty?)? $2 : "#{$1}_#{$2}"
519
+ end.downcase
520
+ end
521
+
496
522
  # convert a string to its idiomatic ruby class name
497
523
  def class_name
498
524
  r = ""
@@ -523,8 +549,14 @@ class String
523
549
  end
524
550
  end
525
551
 
552
+ # Return a self encapsulated in a StringIO object. This is handy.
553
+ def to_stringio
554
+ StringIO.new(self)
555
+ end
556
+
526
557
  end # class String
527
558
 
559
+
528
560
  class Symbol
529
561
  # looks up this symbol as a constant defined in 'ns' (Object by default)
530
562
  def const_lookup(ns=Object)
@@ -535,8 +567,11 @@ end
535
567
  class Array
536
568
  # randomizes the order of contents in the Array (self)
537
569
  def randomize ; self.sort_by { rand } ; end
538
- end
539
570
 
571
+ # Returns a randomly chosen element from self.
572
+ # Drew *is* sparta.
573
+ def rand_elem; self[rand(self.length)] ; end
574
+ end
540
575
 
541
576
  class Float
542
577
  def log2; Math.log(self)/Math.log(2); end
@@ -612,7 +647,7 @@ class Numeric
612
647
  }.join
613
648
  end
614
649
 
615
- # XXX TODO Fixme for new to_bytes/char etc.
650
+ # TODO Fix Numeric.to_guid for new to_bytes/char etc.
616
651
  # def to_guid(order=Rbkb::DEFAULT_BYTE_ORDER)
617
652
  # raw = self.to_bytes(order, 16)
618
653
  # a,b,c,d,*e = raw.unpack("VvvnC6").map{|x| x.to_hex}
data/lib/rbkb/http.rb ADDED
@@ -0,0 +1,20 @@
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.2"
9
+ end
10
+ end
11
+
12
+ require "rbkb/http/common.rb"
13
+ require "rbkb/http/base.rb"
14
+ require "rbkb/http/request.rb"
15
+ require "rbkb/http/response.rb"
16
+ require "rbkb/http/headers.rb"
17
+ require "rbkb/http/body.rb"
18
+ require "rbkb/http/parameters.rb"
19
+
20
+
@@ -0,0 +1,172 @@
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").first =~ /^(\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
+
@@ -0,0 +1,214 @@
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
+