segregate 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e0d78f2e3626e434f62af51a9bd25dc6f2f7608
4
- data.tar.gz: cb81f3d82418ea3d07cebf692418a901ca305969
3
+ metadata.gz: 13ff1b7cc5c95ae45abf27d4edaa5fd80365fa19
4
+ data.tar.gz: 59dd6532dd4a5be947c24c7ac0bb9378950f9b88
5
5
  SHA512:
6
- metadata.gz: 2586ce6b36f3f33d41923bea19d07b7227b1de6c5fa00e9283e7b5514285ad896457ff36bd57a8d81c590fce74cea1fd0f458e02dcaf0638abf0e47d1661df6e
7
- data.tar.gz: dc606c644b5d5221c870412a5afee531a1882782d56298a273fb1c8aa5f48124359a818e5a35f5b5707dcf6a97a478d887d5825e31f80c03081411d431108b35
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
@@ -1,4 +1,4 @@
1
1
  class Segregate
2
- VERSION = "0.4.0".freeze
2
+ VERSION = "0.5.0".freeze
3
3
  DATE = "2014-02-04".freeze
4
4
  end
data/lib/segregate.rb CHANGED
@@ -1,11 +1,14 @@
1
- require 'uri'
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, :http_version, :headers, :body
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
- @request = false
30
- @response = false
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
- @headers_complete
49
+ @state > :headers
48
50
  end
49
51
 
50
- def body_complete?
51
- @body_complete
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= val
71
- http_version[0] = val
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= val
79
- http_version[1] = val
80
+ def minor_http_version= value
81
+ http_version[1] = value
80
82
  end
81
83
 
82
84
  def update_content_length
83
- if @body_complete
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
- @header_orders.each do |header|
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" if @body_complete
100
+ raw_message << "\r\n" + @body + "\r\n" unless @body.empty?
101
101
  raw_message << "\r\n"
102
102
  end
103
103
 
104
- def parse data
105
- data = StringIO.new data
104
+ def parse_data data
105
+ data = StringIO.new(@stashed_data + data)
106
+ @stashed_data = ""
106
107
 
107
- read_first_line data unless @first_line_complete
108
- read_headers data unless @headers_complete
109
- read_body data unless data.eof?
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
- private
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
- def message_complete?
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
- def read data, size = nil
121
- if size
122
- data.read(size + 2).chomp("\r\n")
123
- else
124
- data.readline.chomp("\r\n")
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 read_first_line data
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
- @first_line_complete = true
147
+ @state.next
143
148
  end
144
149
 
145
150
  def parse_request_line line
146
- @request = true
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 = true
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 read_headers data
164
- while !data.eof? && !@headers_complete
165
- line = read data
166
- if line.empty?
167
- @headers_complete = true
168
- else
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
- @callback.on_headers_complete self if @callback.respond_to?(:on_headers_complete) && @headers_complete
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 read_body data
179
- if headers.key? 'content-length'
180
- parse_body data
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 data
188
+ parse_chunked_data line
183
189
  end
184
190
  end
185
191
 
186
- def parse_body data
187
- @body = read data, headers['content-length'].to_i
188
- @callback.on_body @body if @callback.respond_to?(:on_body)
189
- @body_complete = true
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 data
193
- while !data.eof? && !@body_complete
194
- chunk_size = read(data).to_i(16)
195
- if chunk_size == 0
196
- @body_complete = true
197
- else
198
- chunk = read(data, chunk_size)
199
- @body << chunk
200
- @callback.on_body chunk if @callback.respond_to?(:on_body)
201
- end
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