rbkb 0.6.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. data/History.txt +74 -0
  2. data/README.rdoc +149 -0
  3. data/Rakefile +47 -0
  4. data/bin/b64 +5 -0
  5. data/bin/bgrep +5 -0
  6. data/bin/blit +5 -0
  7. data/bin/c +5 -0
  8. data/bin/crc32 +5 -0
  9. data/bin/d64 +5 -0
  10. data/bin/dedump +5 -0
  11. data/bin/feed +5 -0
  12. data/bin/hexify +5 -0
  13. data/bin/len +5 -0
  14. data/bin/plugsrv +271 -0
  15. data/bin/rex +10 -0
  16. data/bin/rstrings +5 -0
  17. data/bin/slice +5 -0
  18. data/bin/telson +5 -0
  19. data/bin/unhexify +5 -0
  20. data/bin/urldec +5 -0
  21. data/bin/urlenc +5 -0
  22. data/bin/xor +5 -0
  23. data/cli_usage.rdoc +285 -0
  24. data/doctor-bag.jpg +0 -0
  25. data/lib/rbkb.rb +51 -0
  26. data/lib/rbkb/cli.rb +219 -0
  27. data/lib/rbkb/cli/b64.rb +35 -0
  28. data/lib/rbkb/cli/bgrep.rb +86 -0
  29. data/lib/rbkb/cli/blit.rb +89 -0
  30. data/lib/rbkb/cli/chars.rb +24 -0
  31. data/lib/rbkb/cli/crc32.rb +35 -0
  32. data/lib/rbkb/cli/d64.rb +28 -0
  33. data/lib/rbkb/cli/dedump.rb +52 -0
  34. data/lib/rbkb/cli/feed.rb +229 -0
  35. data/lib/rbkb/cli/hexify.rb +65 -0
  36. data/lib/rbkb/cli/len.rb +76 -0
  37. data/lib/rbkb/cli/rstrings.rb +108 -0
  38. data/lib/rbkb/cli/slice.rb +47 -0
  39. data/lib/rbkb/cli/telson.rb +87 -0
  40. data/lib/rbkb/cli/unhexify.rb +50 -0
  41. data/lib/rbkb/cli/urldec.rb +35 -0
  42. data/lib/rbkb/cli/urlenc.rb +35 -0
  43. data/lib/rbkb/cli/xor.rb +43 -0
  44. data/lib/rbkb/extends.rb +725 -0
  45. data/lib/rbkb/http.rb +21 -0
  46. data/lib/rbkb/http/base.rb +172 -0
  47. data/lib/rbkb/http/body.rb +214 -0
  48. data/lib/rbkb/http/common.rb +74 -0
  49. data/lib/rbkb/http/headers.rb +370 -0
  50. data/lib/rbkb/http/parameters.rb +104 -0
  51. data/lib/rbkb/http/request.rb +58 -0
  52. data/lib/rbkb/http/response.rb +86 -0
  53. data/lib/rbkb/plug.rb +9 -0
  54. data/lib/rbkb/plug/blit.rb +222 -0
  55. data/lib/rbkb/plug/cli.rb +83 -0
  56. data/lib/rbkb/plug/feed_import.rb +74 -0
  57. data/lib/rbkb/plug/peer.rb +67 -0
  58. data/lib/rbkb/plug/plug.rb +215 -0
  59. data/lib/rbkb/plug/proxy.rb +26 -0
  60. data/lib/rbkb/plug/unix_domain.rb +75 -0
  61. data/lib_usage.rdoc +176 -0
  62. data/rbkb.gemspec +38 -0
  63. data/spec/rbkb_spec.rb +7 -0
  64. data/spec/spec_helper.rb +16 -0
  65. data/tasks/ann.rake +80 -0
  66. data/tasks/bones.rake +20 -0
  67. data/tasks/gem.rake +201 -0
  68. data/tasks/git.rake +40 -0
  69. data/tasks/notes.rake +27 -0
  70. data/tasks/post_load.rake +34 -0
  71. data/tasks/rdoc.rake +51 -0
  72. data/tasks/rubyforge.rake +55 -0
  73. data/tasks/setup.rb +292 -0
  74. data/tasks/spec.rake +54 -0
  75. data/tasks/svn.rake +47 -0
  76. data/tasks/test.rake +40 -0
  77. data/test/test_cli_b64.rb +35 -0
  78. data/test/test_cli_bgrep.rb +137 -0
  79. data/test/test_cli_blit.rb +11 -0
  80. data/test/test_cli_chars.rb +21 -0
  81. data/test/test_cli_crc32.rb +108 -0
  82. data/test/test_cli_d64.rb +22 -0
  83. data/test/test_cli_dedump.rb +118 -0
  84. data/test/test_cli_feed.rb +11 -0
  85. data/test/test_cli_helper.rb +96 -0
  86. data/test/test_cli_hexify.rb +63 -0
  87. data/test/test_cli_len.rb +96 -0
  88. data/test/test_cli_rstrings.rb +15 -0
  89. data/test/test_cli_slice.rb +73 -0
  90. data/test/test_cli_telson.rb +11 -0
  91. data/test/test_cli_unhexify.rb +43 -0
  92. data/test/test_cli_urldec.rb +50 -0
  93. data/test/test_cli_urlenc.rb +44 -0
  94. data/test/test_cli_xor.rb +71 -0
  95. data/test/test_helper.rb +5 -0
  96. data/test/test_http.rb +27 -0
  97. data/test/test_http_helper.rb +60 -0
  98. data/test/test_http_request.rb +136 -0
  99. data/test/test_http_response.rb +222 -0
  100. data/test/test_rbkb.rb +19 -0
  101. metadata +238 -0
@@ -0,0 +1,370 @@
1
+ require 'uri'
2
+
3
+ module Rbkb::Http
4
+
5
+ # A base class for RequestHeaders and ResponseHeaders
6
+ #
7
+ # Includes common implementations of to_raw, to_raw_array, capture, and
8
+ # the class method parse
9
+ #
10
+ # The Headers array are stored internally as an named value pairs array.
11
+ #
12
+ # The headers are generally name/value pairs in the form of:
13
+ #
14
+ # [ ["Name1", "value1"], ["Name2", "value2"], ... ]
15
+ #
16
+ # Which will be rendered with to_raw() to (or captured with capture() from):
17
+ #
18
+ # Name1: value1
19
+ # Name2: value2
20
+ # ...
21
+ #
22
+ # This has the benefit of letting the data= accessor automatically render a
23
+ # Hash or any other Enumerable to a Headers object through the use of to_a.
24
+ # However it has the caveat that named pairs are expected on various
25
+ # operations.
26
+ class Headers < Array
27
+ include CommonInterface
28
+
29
+ # Class method to instantiate a new RequestHeaders object
30
+ def self.request_hdr(*args)
31
+ Headers.new(*args).extend(RequestHeaders)
32
+ end
33
+
34
+ # Class method to instantiate a new ResponseHeaders object
35
+ def self.response_hdr(*args)
36
+ Headers.new(*args).extend(ResponseHeaders)
37
+ end
38
+
39
+ # Instantiates a new Headers object and returns the result of capture(str)
40
+ # Note, this method does not distinguish between ResponseHeaders or
41
+ # RequestHeaders, and so the object may need to be extended with one
42
+ # or the other, if you need access to specific behviors from either.
43
+ def self.parse(str)
44
+ new().capture(str)
45
+ end
46
+
47
+ # Instantiates a new Headers object and returns the result of
48
+ # capture_full_headers(str, first_obj)
49
+ def self.parse_full_headers(str, first_obj)
50
+ new().capture_full_headers(str, first_obj)
51
+ end
52
+
53
+ # Instantiates a new Headers object.
54
+ #
55
+ # Arguments:
56
+ # raw: String or Enumerable. Strings are parsed with capture.
57
+ # Enumerables are converted with 'to_a' and stored directly.
58
+ #
59
+ # opts: Options which affect the behavior of the Headers object.
60
+ # (none currently defined)
61
+ #
62
+ def initialize(*args)
63
+ super()
64
+ if args.first.kind_of? Enumerable
65
+ raw=args.first
66
+ args[0]=nil
67
+ _common_init(*args)
68
+ self.data = raw.to_a
69
+ else
70
+ _common_init(*args)
71
+ end
72
+ end
73
+
74
+ attr_reader :base
75
+
76
+ # Conditionally sets the @base class variable if it is a kind of Base
77
+ # object.
78
+ def base=(b)
79
+ if b.nil? or b.kind_of? Base
80
+ @base = b
81
+ else
82
+ raise "base must be a kind of Base object or nil"
83
+ end
84
+ end
85
+
86
+ # The data method provides a common interface to access internal
87
+ # non-raw information stored in the object.
88
+ #
89
+ # The Headers incarnation returns the internal headers array
90
+ # (actually self).
91
+ def data
92
+ self
93
+ end
94
+
95
+ # The data= method provides a common interface to access internal
96
+ # non-raw information stored in the object.
97
+ #
98
+ # This method stores creates a shallow copy for anything but another
99
+ # Headers object which it references directly. A few rules are enforced:
100
+ # * 1-dimensional elements will be expanded to tuples with 'nil' as the
101
+ # second value.
102
+ #
103
+ # * Names which are enumerables will be 'join()'ed, but not values.
104
+ def data=(d)
105
+ if d.kind_of? Headers
106
+ self.replace d
107
+ else
108
+ self.replace []
109
+ d.to_a.each do |k, v|
110
+ k = k.to_s if k.is_a? Numeric
111
+ self << [k,v]
112
+ end
113
+ end
114
+ return self
115
+ end
116
+
117
+ # The to_raw_array method returns an interim formatted array of raw
118
+ # "Cookie: Value" strings.
119
+ def to_raw_array
120
+ self.map {|h,v| "#{h}: #{v}" }
121
+ end
122
+
123
+ def get_all(k)
124
+ self.select {|h| h[0].downcase == k.downcase }
125
+ end
126
+
127
+ def get_all_values_for(k)
128
+ self.get_all(k).collect {|h,v| v }
129
+ end
130
+ alias all_values_for get_all_values_for
131
+
132
+ def get_header(k)
133
+ self.find {|h| h[0].downcase == k.downcase }
134
+ end
135
+
136
+ def get_value_for(k)
137
+ if h=self.get_header(k)
138
+ return h[1]
139
+ end
140
+ end
141
+ alias get_header_value get_value_for
142
+ alias value_for get_value_for
143
+
144
+ def delete_header(k)
145
+ self.delete_if {|h| h[0].downcase == k.downcase }
146
+ end
147
+
148
+ def set_header(k,v)
149
+ sel = get_all(k)
150
+
151
+ if sel.empty?
152
+ self << [k,v]
153
+ return [[k,v]]
154
+ else
155
+ sel.each {|h| h[1] = v }
156
+ return sel
157
+ end
158
+ end
159
+ alias set_all_for set_header
160
+
161
+ # The to_raw method returns a raw string of headers as they appear
162
+ # on the wire.
163
+ def to_raw
164
+ to_raw_array.join("\r\n") << "\r\n"
165
+ end
166
+
167
+ # Captures a raw string of headers into this instance's internal array.
168
+ # Note: This method expects not to include the first element such as a
169
+ # RequestAction or ResponseStatus. See capture_full_headers for a version
170
+ # that can handle this.
171
+ def capture(str)
172
+
173
+ raise "arg 0 must be a string" unless str.is_a?(String)
174
+ heads = str.split(/\s*\r?\n/)
175
+
176
+ # pass interim parsed headers to a block if given
177
+ yield(self, heads) if block_given?
178
+
179
+ self.replace [] if capture_complete?
180
+ heads.each do |s|
181
+ k,v = s.split(/\s*:\s*/, 2)
182
+ self << [k,v]
183
+ end
184
+ return self
185
+ end
186
+
187
+ # See capture_full_headers. This method is used to resolve the parser
188
+ # for the first entity above the HTTP headers. This instance is designed
189
+ # to raise an exception when capturing.
190
+ def get_first_obj; raise "get_first_obj called on base stub"; end
191
+
192
+ # This method parses a full set of raw headers from the 'str' argument.
193
+ # Unlike the regular capture method, the string is expected to start
194
+ # with a line which will be parsed by first_obj using its own capture
195
+ # method. For example, first_obj would parse something like
196
+ # "GET / HTTP/1.1" for RequestAction or "HTTP/1.1 200 OK" for
197
+ # ResponseStatus. If first_obj is not defined, there will be an attempt
198
+ # to resolve it by calling get_first_obj which should return the
199
+ # appropriate type of object or raise an exception.
200
+ #
201
+ # Returns a 2 element array containing [first_entity, headers]
202
+ # where first entity is the instantiated first_obj object and headers
203
+ # is self.
204
+ def capture_full_headers(str, first_obj=nil)
205
+ first_obj ||= get_first_obj() {|x|}
206
+
207
+ first = nil
208
+ capture(str) do |this, heads|
209
+ first = first_obj.capture(heads.shift)
210
+ yield(heads) if block_given?
211
+ end
212
+ return [first, self]
213
+ end
214
+
215
+ # This method will non-destructively reset the capture state on this object.
216
+ # The existing headers are maintained when this is called.
217
+ # See also: capture_complete? reset_capture!
218
+ def reset_capture
219
+ @capture_state = nil
220
+ self
221
+ end
222
+
223
+ # This method will destructively reset the capture state on this object.
224
+ # The existing headers array is emptied when this is called.
225
+ # See also: capture_complete?, reset_capture
226
+ def reset_capture!
227
+ @capture_state = nil
228
+ self.data = []
229
+ end
230
+
231
+ # Indicates whether this object is ready to capture fresh data, or is
232
+ # waiting for additional data or a reset from a previous incomplete or
233
+ # otherwise broken capture. See also: reset_capture, reset_capture!
234
+ def capture_complete?
235
+ not @capture_state
236
+ end
237
+ end
238
+
239
+
240
+ # A mixin for HTTP Request headers to add specific request header
241
+ # behaviors and features.
242
+ #
243
+ # To instantiate a new request header, use Headers.request_hdr
244
+ module RequestHeaders
245
+ # This method is used to resolve the parser for the first entity above the
246
+ # HTTP headers. The incarnation for ResponseHeaders returns ResponseStatus
247
+ # See Headers.capture_full_headers for more information.
248
+ def get_first_obj(*args)
249
+ RequestAction.new(*args)
250
+ end
251
+ end
252
+
253
+
254
+ # A mixin for HTTP Response headers to add specific response header
255
+ # behaviors and features.
256
+ #
257
+ # To instantiate a new response header, use Headers.response_hdr
258
+ module ResponseHeaders
259
+
260
+ # This method is used to resolve the parser for the first entity above the
261
+ # HTTP headers. The incarnation for ResponseHeaders returns ResponseStatus
262
+ # See Headers.capture_full_headers for more information.
263
+ def get_first_obj(*args)
264
+ ResponseStatus.new(*args)
265
+ end
266
+ end
267
+
268
+
269
+ # A class for HTTP request actions, i.e. the first
270
+ # header sent in an HTTP request, as in "GET / HTTP/1.1"
271
+ class RequestAction
272
+ include CommonInterface
273
+
274
+ def self.parse(str)
275
+ new().capture(str)
276
+ end
277
+
278
+ attr_accessor :verb, :uri, :version
279
+
280
+ def initialize(*args)
281
+ _common_init(*args)
282
+ @verb ||= "GET"
283
+ @uri ||= URI.parse("/")
284
+ @version ||= "HTTP/1.1"
285
+ end
286
+
287
+ def to_raw
288
+ ary = [ @verb, @uri ]
289
+ ary << @version if @version
290
+ ary.join(" ")
291
+ end
292
+
293
+ # This method parses a request action String into the current instance.
294
+ def capture(str)
295
+ raise "arg 0 must be a string" unless str.is_a?(String)
296
+ unless m=/^([^\s]+)\s+([^\s]+)(?:\s+([^\s]+))?\s*$/.match(str)
297
+ raise "invalid action #{str.inspect}"
298
+ end
299
+ @verb = m[1]
300
+ @uri = URI.parse m[2]
301
+ @version = m[3]
302
+ return self
303
+ end
304
+
305
+ # Returns the URI path as a String if defined
306
+ def path
307
+ @uri.path if @uri
308
+ end
309
+
310
+ # Returns the URI query as a String if it is defined
311
+ def query
312
+ @uri.query if @uri
313
+ end
314
+
315
+ # Returns the URI query parameters as a FormUrlencodedParams object if
316
+ # the query string is defined.
317
+ # XXX note parameters cannot currently be modified in this form.
318
+ def parameters
319
+ FormUrlencodedParams.parse(query) if query
320
+ end
321
+
322
+ attr_reader :base
323
+
324
+ def base=(b)
325
+ raise "base must be a kind of Base object" if not b.is_a? Base
326
+ @base = b
327
+ end
328
+ end
329
+
330
+
331
+ # A class for HTTP response status messages, i.e. the first
332
+ # header returned by a server, as in "HTTP/1.0 200 OK"
333
+ class ResponseStatus
334
+ include CommonInterface
335
+
336
+ def self.parse(str)
337
+ new().capture(str)
338
+ end
339
+
340
+ attr_accessor :version, :code, :text
341
+
342
+ def initialize(*args)
343
+ _common_init(*args)
344
+ @version ||= DEFAULT_HTTP_VERSION
345
+ end
346
+
347
+ def to_raw
348
+ [@version, @code, @text].join(" ")
349
+ end
350
+
351
+ def capture(str)
352
+ raise "arg 0 must be a string" unless str.is_a?(String)
353
+ unless m=/^([^\s]+)\s+(\d+)(?:\s+(.*))?$/.match(str)
354
+ raise "invalid status #{str.inspect}"
355
+ end
356
+ @version = m[1]
357
+ @code = m[2] =~ /^\d+$/ ? m[2].to_i : m[2]
358
+ @text = m[3]
359
+ return self
360
+ end
361
+
362
+ attr_reader :base
363
+
364
+ def base=(b)
365
+ raise "base must be a kind of Base object" if not b.is_a? Base
366
+ @base = b
367
+ end
368
+ end
369
+ end
370
+
@@ -0,0 +1,104 @@
1
+ module Rbkb::Http
2
+
3
+ # The Parameters class is for handling named parameter values. This is a
4
+ # stub base class from which to derive specific parameter parsers such as:
5
+ #
6
+ # FormUrlencodedParams for request query string parameters and POST
7
+ # content using application/www-form-urlencoded format.
8
+ #
9
+ # MultiPartFormParams for POST content using multipart/form-data
10
+ class Parameters < Array
11
+ include CommonInterface
12
+
13
+ def self.parse(str)
14
+ new().capture(str)
15
+ end
16
+
17
+ def initialize(*args)
18
+ _common_init(*args)
19
+ end
20
+
21
+ def get_all(k)
22
+ self.select {|p| p[0] == k}
23
+ end
24
+
25
+ def get_all_values_for(k)
26
+ self.get_all(k).collect {|p,v| v }
27
+ end
28
+ alias all_values_for get_all_values_for
29
+
30
+ def get_param(k)
31
+ self.find {|p| p[0] == k}
32
+ end
33
+
34
+ def get_value_for(k)
35
+ if p=self.get_param(k)
36
+ return p[1]
37
+ end
38
+ end
39
+ alias get_param_value get_value_for
40
+ alias value_for get_value_for
41
+
42
+ def set_param(k, v)
43
+ if p=self.get_param(k)
44
+ p[1]=v
45
+ else
46
+ p << [k,v]
47
+ end
48
+ return [[k,v]]
49
+ end
50
+
51
+ def set_all_for(k, v)
52
+ sel=self.get_all(k)
53
+ if sel.empty?
54
+ self << [k,v]
55
+ return [[k,v]]
56
+ else
57
+ sel.each {|p| p[1] = v}
58
+ return sel
59
+ end
60
+ end
61
+
62
+ def delete_param(k)
63
+ self.delete_if {|p| p[0] == k }
64
+ end
65
+ end
66
+
67
+ # The FormUrlencodedParams class is for Parameters values in the
68
+ # form of 'q=foo&l=1&z=baz' as found in GET query strings and
69
+ # application/www-form-urlencoded or application/x-url-encoded POST
70
+ # contents.
71
+ class FormUrlencodedParams < Parameters
72
+ def to_raw
73
+ self.map {|k,v| "#{k}=#{v}"}.join('&')
74
+ end
75
+
76
+ def capture(str)
77
+ raise "arg 0 must be a string" unless String === str
78
+ str.split('&').each do |p|
79
+ var,val = p.split('=',2)
80
+ self << [var,val]
81
+ end
82
+ return self
83
+ end
84
+ end
85
+
86
+
87
+ # The MultipartFormParams class is for Parameters in POST data when using
88
+ # the multipart/form-data content type. This is often used for file uploads.
89
+ class MultipartFormParams < Parameters
90
+ def to_raw
91
+ self.map {|k,v| "#{k}=#{v}"}.join('&')
92
+ end
93
+
94
+ def capture(str)
95
+ raise "arg 0 must be a string" unless String === str
96
+ str.split('&').each do |p|
97
+ var,val = p.split('=',2)
98
+ self << [var,val]
99
+ end
100
+ return self
101
+ end
102
+ end
103
+ end
104
+