segregate 0.4.0 → 0.5.0
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/segregate/http_headers.rb +64 -0
- data/lib/segregate/version.rb +1 -1
- data/lib/segregate.rb +108 -75
- data/spec/segregate_spec.rb +500 -324
- metadata +16 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13ff1b7cc5c95ae45abf27d4edaa5fd80365fa19
|
4
|
+
data.tar.gz: 59dd6532dd4a5be947c24c7ac0bb9378950f9b88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0efb2a197c27af4005ee120feac38dfcec61d41628f5ffd05c9726ec4aade1fa5b9efcb7cf8690246b2a1462966b79e44616b908de2368242fbb12b459a817b
|
7
|
+
data.tar.gz: e470e38d363523ea15a042119b85d4835d98d784c15f8a19423c10f146562117f9ba8a051f10667d7345f48d9f189d636e389eeb92e4a66d06df165d0b8caddd
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class Segregate
|
2
|
+
GENERAL_HEADERS = [
|
3
|
+
"cache-control",
|
4
|
+
"connection",
|
5
|
+
"date",
|
6
|
+
"pragma",
|
7
|
+
"trailer",
|
8
|
+
"transfer-encoding",
|
9
|
+
"upgrade",
|
10
|
+
"via",
|
11
|
+
"warning"
|
12
|
+
]
|
13
|
+
|
14
|
+
REQUEST_HEADERS = [
|
15
|
+
"accept",
|
16
|
+
"accept-charset",
|
17
|
+
"accept-encoding",
|
18
|
+
"accept-language",
|
19
|
+
"authorization",
|
20
|
+
"expect",
|
21
|
+
"from",
|
22
|
+
"host",
|
23
|
+
"if-match",
|
24
|
+
"if-modified-Since",
|
25
|
+
"if-none-Match",
|
26
|
+
"if-range",
|
27
|
+
"if-unmodified-Since",
|
28
|
+
"max-forwards",
|
29
|
+
"proxy-authorization",
|
30
|
+
"range",
|
31
|
+
"referer",
|
32
|
+
"te",
|
33
|
+
"user-agent"
|
34
|
+
]
|
35
|
+
|
36
|
+
RESPONSE_HEADERS = [
|
37
|
+
"accept-ranges",
|
38
|
+
"age",
|
39
|
+
"etag",
|
40
|
+
"location",
|
41
|
+
"proxy-authenticate",
|
42
|
+
"retry-after",
|
43
|
+
"server",
|
44
|
+
"vary",
|
45
|
+
"www-authenticate"
|
46
|
+
]
|
47
|
+
|
48
|
+
ENTITY_HEADERS = [
|
49
|
+
"allow",
|
50
|
+
"content-encoding",
|
51
|
+
"content-language",
|
52
|
+
"content-length",
|
53
|
+
"content-location",
|
54
|
+
"content-md5",
|
55
|
+
"content-range",
|
56
|
+
"content-type",
|
57
|
+
"expires",
|
58
|
+
"last-modified"
|
59
|
+
]
|
60
|
+
|
61
|
+
ALL_HEADERS = GENERAL_HEADERS + REQUEST_HEADERS + RESPONSE_HEADERS + ENTITY_HEADERS
|
62
|
+
|
63
|
+
|
64
|
+
end
|
data/lib/segregate/version.rb
CHANGED
data/lib/segregate.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
-
require '
|
1
|
+
require 'juncture'
|
2
2
|
require 'hashie'
|
3
|
+
require 'uri'
|
4
|
+
require 'segregate/version'
|
3
5
|
require 'segregate/http_methods'
|
6
|
+
require 'segregate/http_headers'
|
4
7
|
require 'segregate/http_regular_expressions'
|
5
8
|
|
6
9
|
class Segregate
|
7
|
-
attr_reader :uri
|
8
|
-
attr_accessor :request_method, :status_code, :status_phrase, :
|
10
|
+
attr_reader :uri, :type, :state, :http_version, :headers
|
11
|
+
attr_accessor :request_method, :status_code, :status_phrase, :body
|
9
12
|
|
10
13
|
def method_missing meth, *args, &block
|
11
14
|
if @uri.respond_to? meth
|
@@ -23,32 +26,31 @@ class Segregate
|
|
23
26
|
@callback = callback
|
24
27
|
@http_version = [nil, nil]
|
25
28
|
|
29
|
+
# :request, :response
|
30
|
+
@type = Juncture.new :request, :response
|
31
|
+
@state = Juncture.new :waiting, :headers, :body, :done, default: :waiting
|
32
|
+
|
26
33
|
@headers = Hashie::Mash.new
|
27
34
|
@body = ""
|
28
35
|
|
29
|
-
@
|
30
|
-
@
|
31
|
-
|
32
|
-
@first_line_complete = false
|
33
|
-
@headers_complete = false
|
34
|
-
@body_complete = false
|
35
|
-
@header_orders = []
|
36
|
+
@stashed_data = ""
|
37
|
+
@stashed_body = ""
|
36
38
|
end
|
37
39
|
|
38
40
|
def request?
|
39
|
-
@request
|
41
|
+
@type == :request
|
40
42
|
end
|
41
43
|
|
42
44
|
def response?
|
43
|
-
@response
|
45
|
+
@type == :response
|
44
46
|
end
|
45
47
|
|
46
48
|
def headers_complete?
|
47
|
-
@
|
49
|
+
@state > :headers
|
48
50
|
end
|
49
51
|
|
50
|
-
def
|
51
|
-
@
|
52
|
+
def done?
|
53
|
+
@state >= :done
|
52
54
|
end
|
53
55
|
|
54
56
|
def request_line
|
@@ -67,24 +69,22 @@ class Segregate
|
|
67
69
|
http_version[0]
|
68
70
|
end
|
69
71
|
|
70
|
-
def major_http_version=
|
71
|
-
http_version[0] =
|
72
|
+
def major_http_version= value
|
73
|
+
http_version[0] = value
|
72
74
|
end
|
73
75
|
|
74
76
|
def minor_http_version
|
75
77
|
http_version[1]
|
76
78
|
end
|
77
79
|
|
78
|
-
def minor_http_version=
|
79
|
-
http_version[1] =
|
80
|
+
def minor_http_version= value
|
81
|
+
http_version[1] = value
|
80
82
|
end
|
81
83
|
|
82
84
|
def update_content_length
|
83
|
-
|
85
|
+
unless @body.empty?
|
84
86
|
@headers['content-length'] = @body.size.to_s
|
85
|
-
@header_orders.push 'content-length' unless @header_orders.include? 'content-length'
|
86
87
|
@headers.delete 'transfer-encoding'
|
87
|
-
@header_orders.delete 'transfer-encoding'
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
@@ -93,41 +93,46 @@ class Segregate
|
|
93
93
|
update_content_length
|
94
94
|
|
95
95
|
request? ? raw_message << request_line + "\r\n" : raw_message << status_line + "\r\n"
|
96
|
-
|
97
|
-
raw_message << "%s: %s\r\n" % [header, headers[header]]
|
96
|
+
ALL_HEADERS.each do |header|
|
97
|
+
raw_message << "%s: %s\r\n" % [header, headers[header]] if headers[header]
|
98
98
|
end
|
99
99
|
|
100
|
-
raw_message << "\r\n" + @body + "\r\n"
|
100
|
+
raw_message << "\r\n" + @body + "\r\n" unless @body.empty?
|
101
101
|
raw_message << "\r\n"
|
102
102
|
end
|
103
103
|
|
104
|
-
def
|
105
|
-
data = StringIO.new data
|
104
|
+
def parse_data data
|
105
|
+
data = StringIO.new(@stashed_data + data)
|
106
|
+
@stashed_data = ""
|
106
107
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
@callback.on_message_complete self if @callback.respond_to?(:on_message_complete) && message_complete?
|
108
|
+
while !data.eof? && @state < :done
|
109
|
+
line, complete_line = get_next_line data
|
110
|
+
complete_line ? parse_line(line) : @stashed_data = line
|
111
|
+
end
|
112
112
|
end
|
113
113
|
|
114
|
-
|
114
|
+
def parse_line line
|
115
|
+
case @state.state
|
116
|
+
when :waiting
|
117
|
+
read_in_first_line line
|
118
|
+
when :headers
|
119
|
+
read_in_headers line
|
120
|
+
when :body
|
121
|
+
read_in_body line
|
122
|
+
end
|
115
123
|
|
116
|
-
|
117
|
-
@body_complete || (@headers_complete && @headers['content-length'].nil? && @headers['transfer-encoding'].nil?)
|
124
|
+
@callback.on_message_complete self if @callback.respond_to?(:on_message_complete) && done?
|
118
125
|
end
|
119
126
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
end
|
127
|
+
private
|
128
|
+
|
129
|
+
def get_next_line data
|
130
|
+
line = data.readline("\r\n")
|
131
|
+
[line.chomp("\r\n"), line.end_with?("\r\n") || line.length == headers['content-length'].to_i && @state >= :body]
|
126
132
|
end
|
127
133
|
|
128
|
-
def
|
134
|
+
def read_in_first_line line
|
129
135
|
@callback.on_message_begin self if @callback.respond_to?(:on_message_begin)
|
130
|
-
line = read data
|
131
136
|
|
132
137
|
if line =~ REQUEST_LINE
|
133
138
|
parse_request_line line
|
@@ -139,11 +144,11 @@ class Segregate
|
|
139
144
|
raise "ERROR: Unknown first line: %s" % line
|
140
145
|
end
|
141
146
|
|
142
|
-
@
|
147
|
+
@state.next
|
143
148
|
end
|
144
149
|
|
145
150
|
def parse_request_line line
|
146
|
-
@request
|
151
|
+
@type.set :request
|
147
152
|
@request_method, url, @http_version[0], @http_version[1] = line.scan(REQUEST_LINE).flatten
|
148
153
|
@http_version.map! {|v| v.to_i}
|
149
154
|
@uri = URI.parse url
|
@@ -152,7 +157,7 @@ class Segregate
|
|
152
157
|
end
|
153
158
|
|
154
159
|
def parse_status_line line
|
155
|
-
@response
|
160
|
+
@type.set :response
|
156
161
|
@http_version[0], @http_version[1], code, @status_phrase = line.scan(STATUS_LINE).flatten
|
157
162
|
@http_version.map! {|v| v.to_i}
|
158
163
|
@status_code = code.to_i
|
@@ -160,45 +165,73 @@ class Segregate
|
|
160
165
|
@callback.on_status_line self if @callback.respond_to?(:on_status_line)
|
161
166
|
end
|
162
167
|
|
163
|
-
def
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
key, value = line.split(":")
|
170
|
-
@headers[key.downcase] = value.strip
|
171
|
-
@header_orders << key.downcase
|
172
|
-
end
|
168
|
+
def read_in_headers line
|
169
|
+
if line.empty?
|
170
|
+
@state.next
|
171
|
+
else
|
172
|
+
key, value = line.split(":")
|
173
|
+
@headers[key.downcase] = value.strip
|
173
174
|
end
|
174
175
|
|
175
|
-
|
176
|
+
if headers_complete?
|
177
|
+
@callback.on_headers_complete self if @callback.respond_to?(:on_headers_complete)
|
178
|
+
unless headers['content-length'] || headers['transfer-encoding']
|
179
|
+
@state.set :done
|
180
|
+
end
|
181
|
+
end
|
176
182
|
end
|
177
183
|
|
178
|
-
def
|
179
|
-
if headers
|
180
|
-
parse_body
|
184
|
+
def read_in_body line
|
185
|
+
if headers['content-length']
|
186
|
+
parse_body line
|
181
187
|
elsif headers['transfer-encoding'] == 'chunked'
|
182
|
-
parse_chunked_data
|
188
|
+
parse_chunked_data line
|
183
189
|
end
|
184
190
|
end
|
185
191
|
|
186
|
-
def parse_body
|
187
|
-
|
188
|
-
@
|
189
|
-
|
192
|
+
def parse_body line
|
193
|
+
line = @stashed_body + line
|
194
|
+
@stashed_body = ""
|
195
|
+
|
196
|
+
if line.length == headers['content-length'].to_i
|
197
|
+
@body = line
|
198
|
+
@callback.on_body @body if @callback.respond_to?(:on_body)
|
199
|
+
@state.next
|
200
|
+
else
|
201
|
+
@stashed_body = line
|
202
|
+
end
|
190
203
|
end
|
191
204
|
|
192
|
-
def parse_chunked_data
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
205
|
+
def parse_chunked_data line
|
206
|
+
@chunked_body_state ||= Juncture.new :size, :chunk, default: :size
|
207
|
+
|
208
|
+
case @chunked_body_state.state
|
209
|
+
when :size
|
210
|
+
parse_chunked_data_size line.to_i(16)
|
211
|
+
when :chunk
|
212
|
+
parse_chunked_data_chunk line
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def parse_chunked_data_size chunk_size
|
217
|
+
if chunk_size == 0
|
218
|
+
@state.next
|
219
|
+
else
|
220
|
+
@chunk_size = chunk_size
|
221
|
+
end
|
222
|
+
@chunked_body_state.next
|
223
|
+
end
|
224
|
+
|
225
|
+
def parse_chunked_data_chunk line
|
226
|
+
line = @stashed_body + line
|
227
|
+
@stashed_body = ""
|
228
|
+
|
229
|
+
if line.length == @chunk_size
|
230
|
+
@body << line
|
231
|
+
@callback.on_body line if @callback.respond_to?(:on_body)
|
232
|
+
@chunked_body_state.next
|
233
|
+
else
|
234
|
+
@stashed_body = line
|
202
235
|
end
|
203
236
|
end
|
204
237
|
end
|