emonti-rbkb 0.6.2.1 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
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
+