rbkb 0.6.10 → 0.6.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,74 +0,0 @@
1
- module Rbkb::Http
2
- DEFAULT_HTTP_VERSION = "HTTP/1.1"
3
-
4
- module CommonInterface
5
- # This provides a common method for use in 'initialize' to slurp in
6
- # opts parameters and optionally capture a raw blob. This method also
7
- # accepts a block to which it yields 'self'
8
- def _common_init(raw=nil, opts=nil)
9
- self.opts = opts
10
- yield self if block_given?
11
- capture(raw) if raw
12
- return self
13
- end
14
-
15
- # Implements a common interface for an opts hash which is stored internally
16
- # as the class variable @opts.
17
- #
18
- # The opts hash is designed to contain various named values for
19
- # configuration, etc. The values and names are determined entirely
20
- # by the class that uses it.
21
- def opts
22
- @opts
23
- end
24
-
25
- # Implements a common interface for setting a new opts hash containing
26
- # various named values for configuration, etc. This also performs a
27
- # minimal sanity check to ensure the object is a Hash.
28
- def opts=(o=nil)
29
- raise "opts must be a hash" unless (o ||= {}).is_a? Hash
30
- @opts = o
31
- end
32
- end
33
-
34
-
35
- # A generic cheat for an Array of named value pairs to pretend to
36
- # be like Hash when using [] and []=
37
- class NamedValueArray < Array
38
-
39
- # Act like a hash with named values. Return the named value if a string
40
- # or Symbol is supplied as the index argument.
41
- #
42
- # Note, this doesn't do any magic with String / Symbol conversion.
43
- def [](*args)
44
- if args.size == 1 and (String === args[0] or Symbol === args[0])
45
- if h=find {|x| x[0] == args[0]}
46
- return h[1]
47
- end
48
- else
49
- super(*args)
50
- end
51
- end
52
-
53
- # Act like a hash with named values. Set the named value if a String
54
- # or Symbol is supplied as the index argument.
55
- #
56
- # Note, this doesn't do any magic with String / Symbol conversion.
57
- def []=(*args)
58
- if args.size > 1 and (String === args[0] or Symbol === args[0])
59
- if h=find {|x| x[0] == args[0]}
60
- h[1] = args[1]
61
- else
62
- self << args[0,2]
63
- end
64
- else
65
- super(*args)
66
- end
67
- end
68
-
69
- def delete_key(key)
70
- delete_if {|x| x[0] == key }
71
- end
72
- end
73
-
74
- end
@@ -1,370 +0,0 @@
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
-
@@ -1,104 +0,0 @@
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
-