safrano 0.3.2 → 0.4.2
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.
- 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
|