safrano 0.3.2 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/odata/attribute.rb +1 -1
- data/lib/odata/batch.rb +24 -10
- data/lib/odata/collection.rb +242 -96
- data/lib/odata/collection_filter.rb +40 -9
- data/lib/odata/collection_media.rb +279 -0
- data/lib/odata/collection_order.rb +46 -36
- data/lib/odata/common_logger.rb +59 -0
- data/lib/odata/entity.rb +268 -54
- data/lib/odata/error.rb +58 -17
- data/lib/odata/expand.rb +123 -0
- data/lib/odata/filter/error.rb +6 -0
- data/lib/odata/filter/parse.rb +4 -12
- data/lib/odata/filter/sequel.rb +11 -13
- data/lib/odata/filter/tree.rb +11 -15
- data/lib/odata/navigation_attribute.rb +150 -0
- data/lib/odata/relations.rb +2 -2
- data/lib/odata/select.rb +42 -0
- data/lib/odata/url_parameters.rb +51 -36
- data/lib/odata/walker.rb +12 -4
- data/lib/safrano.rb +23 -12
- data/lib/{safrano_core.rb → safrano/core.rb} +14 -3
- data/lib/{multipart.rb → safrano/multipart.rb} +51 -29
- data/lib/{odata_rack_builder.rb → safrano/odata_rack_builder.rb} +1 -1
- data/lib/{rack_app.rb → safrano/rack_app.rb} +15 -10
- data/lib/{request.rb → safrano/request.rb} +21 -8
- data/lib/{response.rb → safrano/response.rb} +1 -2
- data/lib/{sequel_join_by_paths.rb → safrano/sequel_join_by_paths.rb} +1 -1
- data/lib/{service.rb → safrano/service.rb} +93 -97
- data/lib/safrano/version.rb +3 -0
- data/lib/sequel/plugins/join_by_paths.rb +11 -10
- metadata +34 -15
data/lib/odata/relations.rb
CHANGED
@@ -85,7 +85,7 @@ module OData
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
-
def get_metadata_xml_attribs(from, to, assoc_type, xnamespace)
|
88
|
+
def get_metadata_xml_attribs(from, to, assoc_type, xnamespace, attrname)
|
89
89
|
rel = get([from, to])
|
90
90
|
# use Sequel reflection to get multiplicity (will be used later
|
91
91
|
# in 2. Associations below)
|
@@ -107,7 +107,7 @@ module OData
|
|
107
107
|
# <NavigationProperty Name="Supplier"
|
108
108
|
# Relationship="ODataDemo.Product_Supplier_Supplier_Products"
|
109
109
|
# FromRole="Product_Supplier" ToRole="Supplier_Products"/>
|
110
|
-
{ 'Name' =>
|
110
|
+
{ 'Name' => attrname, 'Relationship' => "#{xnamespace}.#{rel.name}",
|
111
111
|
'FromRole' => from, 'ToRole' => to }
|
112
112
|
end
|
113
113
|
end
|
data/lib/odata/select.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'odata/error.rb'
|
2
|
+
|
3
|
+
# all dataset selecting related classes in our OData module
|
4
|
+
# ie do eager loading
|
5
|
+
module OData
|
6
|
+
# base class for selecting. We have to distinguish between
|
7
|
+
# fields of the current entity, and the navigation properties
|
8
|
+
# we can have one special case
|
9
|
+
# empty, ie no $select specified --> return all fields and all nav props
|
10
|
+
# ==> SelectAll
|
11
|
+
|
12
|
+
class SelectBase
|
13
|
+
ALL = new # re-useable selecting-all handler
|
14
|
+
|
15
|
+
def self.factory(selectstr)
|
16
|
+
case selectstr&.strip
|
17
|
+
when nil, '', '*'
|
18
|
+
ALL
|
19
|
+
else
|
20
|
+
Select.new(selectstr)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def all_props?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def ALL.all_props?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# single select
|
34
|
+
class Select < SelectBase
|
35
|
+
COMASPLIT = /\s*,\s*/.freeze
|
36
|
+
attr_reader :props
|
37
|
+
def initialize(selstr)
|
38
|
+
@selectp = selstr.strip
|
39
|
+
@props = @selectp.split(COMASPLIT)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/odata/url_parameters.rb
CHANGED
@@ -3,57 +3,72 @@ require 'odata/error.rb'
|
|
3
3
|
# url parameters processing . Mostly delegates to specialised classes
|
4
4
|
# (filter, order...) to convert into Sequel exprs.
|
5
5
|
module OData
|
6
|
-
class
|
6
|
+
class UrlParametersBase
|
7
|
+
attr_reader :expand
|
8
|
+
attr_reader :select
|
9
|
+
|
10
|
+
def check_expand
|
11
|
+
return BadRequestExpandParseError if @expand.parse_error?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# url parameters for a single entity expand/select
|
16
|
+
class UrlParameters4Single < UrlParametersBase
|
17
|
+
def initialize(params)
|
18
|
+
@params = params
|
19
|
+
@expand = ExpandBase.factory(@params['$expand'])
|
20
|
+
@select = SelectBase.factory(@params['$select'])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# url parameters for a collection expand/select + filter/order
|
25
|
+
class UrlParameters4Coll < UrlParametersBase
|
7
26
|
attr_reader :filt
|
8
27
|
attr_reader :ordby
|
9
|
-
|
10
|
-
|
28
|
+
|
29
|
+
def initialize(model, params)
|
30
|
+
# join helper is only needed for odering or filtering
|
31
|
+
@jh = model.join_by_paths_helper if params['$orderby'] || params['$filter']
|
11
32
|
@params = params
|
33
|
+
@ordby = OrderBase.factory(@params['$orderby'], @jh)
|
34
|
+
@filt = FilterBase.factory(@params['$filter'])
|
35
|
+
@expand = ExpandBase.factory(@params['$expand'])
|
36
|
+
@select = SelectBase.factory(@params['$select'])
|
12
37
|
end
|
13
38
|
|
14
39
|
def check_filter
|
15
|
-
return unless @params['$filter']
|
16
|
-
|
17
|
-
@filt = FilterByParse.new(@params['$filter'], @jh)
|
18
40
|
return BadRequestFilterParseError if @filt.parse_error?
|
19
|
-
|
20
|
-
# nil is the expected return for no errors
|
21
|
-
nil
|
22
41
|
end
|
23
42
|
|
24
43
|
def check_order
|
25
|
-
return
|
44
|
+
return BadRequestOrderParseError if @ordby.parse_error?
|
45
|
+
end
|
26
46
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
qualfn.strip!
|
32
|
-
dir.strip! if dir
|
33
|
-
return BadRequestError unless @jh.start_model.attrib_path_valid? qualfn
|
34
|
-
return BadRequestError unless [nil, 'asc', 'desc'].include? dir
|
35
|
-
end
|
47
|
+
def apply_to_dataset(dtcx)
|
48
|
+
dtcx = apply_expand_to_dataset(dtcx)
|
49
|
+
apply_filter_order_to_dataset(dtcx)
|
50
|
+
end
|
36
51
|
|
37
|
-
|
52
|
+
def apply_expand_to_dataset(dtcx)
|
53
|
+
return dtcx if @expand.empty?
|
38
54
|
|
39
|
-
|
40
|
-
nil
|
55
|
+
@expand.apply_to_dataset(dtcx)
|
41
56
|
end
|
42
57
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
58
|
+
# Warning, the @ordby and @filt objects are coupled by way of the join helper
|
59
|
+
def apply_filter_order_to_dataset(dtcx)
|
60
|
+
return dtcx if @filt.empty? && @ordby.empty?
|
61
|
+
|
62
|
+
# filter object and join-helper need to be finalized after filter has been parsed and checked
|
63
|
+
@filt.finalize(@jh)
|
64
|
+
|
65
|
+
# start with the join
|
66
|
+
dtcx = @jh.dataset(dtcx)
|
67
|
+
|
68
|
+
dtcx = @filt.apply_to_dataset(dtcx)
|
69
|
+
dtcx = @ordby.apply_to_dataset(dtcx)
|
70
|
+
|
71
|
+
dtcx.select_all(@jh.start_model.table_name)
|
57
72
|
end
|
58
73
|
end
|
59
74
|
end
|
data/lib/odata/walker.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
require 'json'
|
4
2
|
require 'rexml/document'
|
5
3
|
require 'safrano.rb'
|
@@ -21,9 +19,12 @@ module OData
|
|
21
19
|
# is $count requested?
|
22
20
|
attr_accessor :do_count
|
23
21
|
|
24
|
-
# is $value requested?
|
22
|
+
# is $value (of attribute) requested?
|
25
23
|
attr_reader :raw_value
|
26
24
|
|
25
|
+
# is $value (of media entity) requested?
|
26
|
+
attr_reader :media_value
|
27
|
+
|
27
28
|
# are $links requested ?
|
28
29
|
attr_reader :do_links
|
29
30
|
|
@@ -35,9 +36,11 @@ module OData
|
|
35
36
|
@content_id_refs = content_id_refs
|
36
37
|
|
37
38
|
@contexts = [@context]
|
39
|
+
|
38
40
|
@path_start = @path_remain = if service
|
39
41
|
unprefixed(service.xpath_prefix, path)
|
40
42
|
else # This is for batch function
|
43
|
+
|
41
44
|
path
|
42
45
|
end
|
43
46
|
@path_done = ''
|
@@ -51,7 +54,9 @@ module OData
|
|
51
54
|
if (prefix == '') || (prefix == '/')
|
52
55
|
path
|
53
56
|
else
|
54
|
-
path.sub!(/\A#{prefix}/, '')
|
57
|
+
# path.sub!(/\A#{prefix}/, '')
|
58
|
+
# TODO check
|
59
|
+
path.sub(/\A#{prefix}/, '')
|
55
60
|
end
|
56
61
|
end
|
57
62
|
|
@@ -107,6 +112,9 @@ module OData
|
|
107
112
|
when :end_with_value
|
108
113
|
@raw_value = true
|
109
114
|
@status = :end
|
115
|
+
when :end_with_media_value
|
116
|
+
@media_value = true
|
117
|
+
@status = :end
|
110
118
|
when :run_with_links
|
111
119
|
@do_links = true
|
112
120
|
@status = :run
|
data/lib/safrano.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
require 'json'
|
4
2
|
require 'rexml/document'
|
5
|
-
require_relative '
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
require_relative 'safrano/multipart.rb'
|
4
|
+
require_relative 'safrano/core.rb'
|
5
|
+
require_relative 'odata/entity.rb'
|
6
|
+
require_relative 'odata/attribute.rb'
|
7
|
+
require_relative 'odata/navigation_attribute.rb'
|
8
|
+
require_relative 'odata/collection.rb'
|
9
|
+
require_relative 'safrano/service.rb'
|
10
|
+
require_relative 'odata/walker.rb'
|
12
11
|
require 'sequel'
|
13
|
-
require_relative '
|
14
|
-
|
15
|
-
|
12
|
+
require_relative 'safrano/sequel_join_by_paths.rb'
|
13
|
+
require_relative 'safrano/rack_app'
|
14
|
+
require_relative 'safrano/odata_rack_builder'
|
15
|
+
require_relative 'safrano/version'
|
16
16
|
|
17
17
|
# picked from activsupport; needed for ruby < 2.5
|
18
18
|
# Destructively converts all keys using the +block+ operations.
|
@@ -29,3 +29,14 @@ class Hash
|
|
29
29
|
transform_keys! { |key| key.to_sym rescue key }
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
# needed for ruby < 2.5
|
34
|
+
class Dir
|
35
|
+
def self.each_child(dir)
|
36
|
+
Dir.foreach(dir) do |x|
|
37
|
+
next if (x == '.') || (x == '..')
|
38
|
+
|
39
|
+
yield x
|
40
|
+
end
|
41
|
+
end unless respond_to? :each_child
|
42
|
+
end
|
@@ -2,13 +2,23 @@
|
|
2
2
|
|
3
3
|
# our main namespace
|
4
4
|
module OData
|
5
|
+
# frozen empty Array/Hash to reduce unncecessary object creation
|
6
|
+
EMPTY_ARRAY = [].freeze
|
7
|
+
EMPTY_HASH = {}.freeze
|
8
|
+
EMPTY_HASH_IN_ARY = [EMPTY_HASH].freeze
|
9
|
+
EMPTY_STRING = ''.freeze
|
10
|
+
ARY_204_EMPTY_HASH_ARY = [204, EMPTY_HASH, EMPTY_ARRAY].freeze
|
11
|
+
SPACE = ' '.freeze
|
12
|
+
COMMA = ','.freeze
|
13
|
+
|
5
14
|
# some prominent constants... probably already defined elsewhere eg in Rack
|
6
15
|
# but lets KISS
|
7
16
|
CONTENT_TYPE = 'Content-Type'.freeze
|
17
|
+
CTT_TYPE_LC = 'content-type'.freeze
|
8
18
|
TEXTPLAIN_UTF8 = 'text/plain;charset=utf-8'.freeze
|
9
19
|
APPJSON = 'application/json'.freeze
|
10
20
|
APPXML = 'application/xml'.freeze
|
11
|
-
|
21
|
+
MP_MIXED = 'multipart/mixed'.freeze
|
12
22
|
APPXML_UTF8 = 'application/xml;charset=utf-8'.freeze
|
13
23
|
APPATOMXML_UTF8 = 'application/atomsvc+xml;charset=utf-8'.freeze
|
14
24
|
APPJSON_UTF8 = 'application/json;charset=utf-8'.freeze
|
@@ -31,14 +41,15 @@ module OData
|
|
31
41
|
# database-specific types.
|
32
42
|
DB_TYPE_STRING_RGX = /\ACHAR\s*\(\d+\)\z/.freeze
|
33
43
|
|
44
|
+
# TODO... complete; used in $metadata
|
34
45
|
def self.get_edm_type(db_type:)
|
35
|
-
case db_type
|
46
|
+
case db_type.upcase
|
36
47
|
when 'INTEGER'
|
37
48
|
'Edm.Int32'
|
38
49
|
when 'TEXT', 'STRING'
|
39
50
|
'Edm.String'
|
40
51
|
else
|
41
|
-
'Edm.String' if DB_TYPE_STRING_RGX =~ db_type
|
52
|
+
'Edm.String' if DB_TYPE_STRING_RGX =~ db_type.upcase
|
42
53
|
end
|
43
54
|
end
|
44
55
|
end
|
@@ -6,10 +6,17 @@ require 'webrick/httpstatus'
|
|
6
6
|
|
7
7
|
# Simple multipart support for OData $batch purpose
|
8
8
|
module MIME
|
9
|
+
CTT_TYPE_LC = 'content-type'.freeze
|
10
|
+
TEXT_PLAIN = 'text/plain'.freeze
|
11
|
+
|
9
12
|
# a mime object has a header(with content-type etc) and a content(aka body)
|
10
13
|
class Media
|
11
14
|
# Parser for MIME::Media
|
12
15
|
class Parser
|
16
|
+
HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
|
17
|
+
|
18
|
+
CRLF_LINE_RGX = /^#{CRLF}$/.freeze
|
19
|
+
|
13
20
|
attr_accessor :lines
|
14
21
|
attr_accessor :target
|
15
22
|
def initialize
|
@@ -47,11 +54,12 @@ module MIME
|
|
47
54
|
end
|
48
55
|
|
49
56
|
def parse_head(line)
|
50
|
-
if (hmd =
|
57
|
+
if (hmd = HMD_RGX.match(line))
|
51
58
|
@target_hd[hmd[1].downcase] = hmd[2].strip
|
52
59
|
|
53
|
-
elsif
|
54
|
-
|
60
|
+
# elsif CRLF_LINE_RGX =~ line
|
61
|
+
elsif CRLF == line
|
62
|
+
@target_ct = @target_hd[CTT_TYPE_LC] || TEXT_PLAIN
|
55
63
|
@state = new_content
|
56
64
|
|
57
65
|
end
|
@@ -95,21 +103,24 @@ module MIME
|
|
95
103
|
end
|
96
104
|
|
97
105
|
def hook_multipart(content_type, boundary)
|
98
|
-
@target_hd[
|
99
|
-
@target_ct = @target_hd[
|
106
|
+
@target_hd[CTT_TYPE_LC] = content_type
|
107
|
+
@target_ct = @target_hd[CTT_TYPE_LC]
|
100
108
|
@target = multipart_content(boundary)
|
101
109
|
@target.hd = @target_hd
|
102
110
|
@target.ct = @target_ct
|
103
111
|
@state = :bmp
|
104
112
|
end
|
105
|
-
|
106
|
-
|
113
|
+
MPS = 'multipart/'.freeze
|
114
|
+
MP_RGX1 = %r{^(digest|mixed);\s*boundary=\"(.*)\"}.freeze
|
115
|
+
MP_RGX2 = %r{^(digest|mixed);\s*boundary=(.*)}.freeze
|
116
|
+
# APP_HTTP_RGX = %r{^application/http}.freeze
|
117
|
+
APP_HTTP = 'application/http'.freeze
|
107
118
|
def new_content
|
108
119
|
@target =
|
109
|
-
if
|
110
|
-
(md = MP_RGX2.match(@target_ct))
|
120
|
+
if @target_ct.start_with?(MPS) &&
|
121
|
+
(md = (MP_RGX1.match(@target_ct[10..-1]) || MP_RGX2.match(@target_ct[10..-1])))
|
111
122
|
multipart_content(md[2].strip)
|
112
|
-
elsif
|
123
|
+
elsif @target_ct.start_with?(APP_HTTP)
|
113
124
|
MIME::Content::Application::Http.new
|
114
125
|
else
|
115
126
|
MIME::Content::Text::Plain.new
|
@@ -187,6 +198,8 @@ module MIME
|
|
187
198
|
class Plain < Media
|
188
199
|
# Parser for Text::Plain
|
189
200
|
class Parser
|
201
|
+
HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
|
202
|
+
CRLF_LINE_RGX = /^#{CRLF}$/.freeze
|
190
203
|
def initialize(target)
|
191
204
|
@state = :h
|
192
205
|
@lines = []
|
@@ -198,9 +211,10 @@ module MIME
|
|
198
211
|
end
|
199
212
|
|
200
213
|
def parse_head(line)
|
201
|
-
if (hmd =
|
214
|
+
if (hmd = HMD_RGX.match(line))
|
202
215
|
@target.hd[hmd[1].downcase] = hmd[2].strip
|
203
|
-
elsif
|
216
|
+
# elsif CRLF_LINE_RGX =~ line
|
217
|
+
elsif CRLF == line
|
204
218
|
@state = :b
|
205
219
|
else
|
206
220
|
@target.content << line
|
@@ -230,8 +244,8 @@ module MIME
|
|
230
244
|
@hd = {}
|
231
245
|
@content = ''
|
232
246
|
# set default values. Can be overwritten by parser
|
233
|
-
@hd[
|
234
|
-
@ct =
|
247
|
+
@hd[CTT_TYPE_LC] = TEXT_PLAIN
|
248
|
+
@ct = TEXT_PLAIN
|
235
249
|
@parser = Parser.new(self)
|
236
250
|
end
|
237
251
|
|
@@ -251,6 +265,7 @@ module MIME
|
|
251
265
|
class Base < Media
|
252
266
|
# Parser for Multipart Base class
|
253
267
|
class Parser
|
268
|
+
CRLF_ENDING_RGX = /#{CRLF}$/.freeze
|
254
269
|
def initialize(target)
|
255
270
|
@body_lines = []
|
256
271
|
@target = target
|
@@ -295,7 +310,8 @@ module MIME
|
|
295
310
|
# to remove it from the end of the last body line
|
296
311
|
return unless @body_lines
|
297
312
|
|
298
|
-
@body_lines.last.sub!(
|
313
|
+
# @body_lines.last.sub!(CRLF_ENDING_RGX, '')
|
314
|
+
@body_lines.last.chomp!(CRLF)
|
299
315
|
@parts << @body_lines
|
300
316
|
end
|
301
317
|
|
@@ -349,7 +365,7 @@ module MIME
|
|
349
365
|
end
|
350
366
|
|
351
367
|
def set_multipart_header
|
352
|
-
@hd[
|
368
|
+
@hd[CTT_TYPE_LC] = "#{OData::MP_MIXED}; boundary=#{@boundary}"
|
353
369
|
end
|
354
370
|
|
355
371
|
def get_http_resp(batcha)
|
@@ -367,9 +383,7 @@ module MIME
|
|
367
383
|
# of the changes
|
368
384
|
batcha.db.transaction do
|
369
385
|
begin
|
370
|
-
@response.content = @content.map { |part|
|
371
|
-
part.get_response(batcha)
|
372
|
-
}
|
386
|
+
@response.content = @content.map { |part| part.get_response(batcha) }
|
373
387
|
rescue Sequel::Rollback => e
|
374
388
|
# one of the changes of the changeset has failed
|
375
389
|
# --> provide a dummy empty response for the change-parts
|
@@ -390,17 +404,19 @@ module MIME
|
|
390
404
|
@response.content = [{ 'odata.error' =>
|
391
405
|
{ 'message' =>
|
392
406
|
'Bad Request: Failed changeset ' } }.to_json]
|
393
|
-
@response.hd =
|
407
|
+
@response.hd = OData::CT_JSON
|
394
408
|
@response
|
395
409
|
end
|
396
410
|
|
397
411
|
def unparse(bodyonly = false)
|
398
412
|
b = ''
|
399
413
|
unless bodyonly
|
400
|
-
b <<
|
414
|
+
# b << OData::CONTENT_TYPE << ': ' << @hd[OData::CTT_TYPE_LC] << CRLF
|
415
|
+
b << "#{OData::CONTENT_TYPE}: #{@hd[CTT_TYPE_LC]}#{CRLF}"
|
401
416
|
end
|
402
|
-
|
403
|
-
b <<
|
417
|
+
|
418
|
+
b << crbdcr = "#{CRLF}--#{@boundary}#{CRLF}"
|
419
|
+
b << @content.map(&:unparse).join(crbdcr)
|
404
420
|
b << "#{CRLF}--#{@boundary}--#{CRLF}"
|
405
421
|
b
|
406
422
|
end
|
@@ -425,7 +441,8 @@ module MIME
|
|
425
441
|
|
426
442
|
def unparse
|
427
443
|
b = "#{@http_method} #{@uri} HTTP/1.1#{CRLF}"
|
428
|
-
@hd.each { |k, v| b << k
|
444
|
+
@hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
|
445
|
+
# @hd.each { |k, v| b << k.to_s << ': ' << v.to_s << CRLF }
|
429
446
|
b << CRLF
|
430
447
|
b << @content if @content != ''
|
431
448
|
b
|
@@ -455,7 +472,8 @@ module MIME
|
|
455
472
|
def unparse
|
456
473
|
b = String.new(APPLICATION_HTTP_11)
|
457
474
|
b << "#{@status} #{StatusMessage[@status]} #{CRLF}"
|
458
|
-
@hd.each { |k, v| b << k
|
475
|
+
@hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
|
476
|
+
# @hd.each { |k, v| b << k.to_s << ': ' << v.to_s << CRLF }
|
459
477
|
b << CRLF
|
460
478
|
b << @content.join if @content
|
461
479
|
b
|
@@ -464,13 +482,16 @@ module MIME
|
|
464
482
|
|
465
483
|
# For application/http . Content is either a Request or a Response
|
466
484
|
class Http < Media
|
467
|
-
HTTP_MTHS_RGX = 'POST|GET|PUT|MERGE|PATCH'.freeze
|
485
|
+
HTTP_MTHS_RGX = 'POST|GET|PUT|MERGE|PATCH|DELETE'.freeze
|
468
486
|
HTTP_R_RGX = %r{^(#{HTTP_MTHS_RGX})\s+(\S*)\s?(HTTP/1\.1)\s*$}.freeze
|
469
487
|
HEADER_RGX = %r{^([a-zA-Z\-]+):\s*([0-9a-zA-Z\-\/,\s]+;?\S*)\s*$}.freeze
|
470
488
|
HTTP_RESP_RGX = %r{^HTTP/1\.1\s(\d+)\s}.freeze
|
471
489
|
|
472
490
|
# Parser for Http Media
|
473
491
|
class Parser
|
492
|
+
HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
|
493
|
+
CRLF_LINE_RGX = /^#{CRLF}$/.freeze
|
494
|
+
|
474
495
|
def initialize(target)
|
475
496
|
@state = :http
|
476
497
|
@lines = []
|
@@ -483,7 +504,7 @@ module MIME
|
|
483
504
|
end
|
484
505
|
|
485
506
|
def parse_http(line)
|
486
|
-
if (hmd =
|
507
|
+
if (hmd = HMD_RGX.match(line))
|
487
508
|
@target.hd[hmd[1].downcase] = hmd[2].strip
|
488
509
|
elsif (mdht = HTTP_R_RGX.match(line))
|
489
510
|
@state = :hd
|
@@ -499,9 +520,10 @@ module MIME
|
|
499
520
|
end
|
500
521
|
|
501
522
|
def parse_head(line)
|
502
|
-
if (hmd =
|
523
|
+
if (hmd = HMD_RGX.match(line))
|
503
524
|
@target.content.hd[hmd[1].downcase] = hmd[2].strip
|
504
|
-
elsif
|
525
|
+
elsif CRLF == line
|
526
|
+
# elsif CRLF_LINE_RGX =~ line
|
505
527
|
@state = :b
|
506
528
|
else
|
507
529
|
@body_lines << line
|