segregate 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +127 -2
- data/lib/segregate/version.rb +1 -1
- data/lib/segregate.rb +41 -10
- data/spec/segregate_spec.rb +103 -3
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00c26832bb6ab7be4cb90fcdc05c33619b9e1a46
|
4
|
+
data.tar.gz: c26a45764b6b70156f3dc1abf8ab05bd2661807a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbd93e8b75a52fa3a59cc50e2745cc30f1ff2d7a494a37c87e8b306ead379f98341645f2cc64c9f46c5b3de35cfa42a24f4e528ace9b9043f0a294440256fd3e
|
7
|
+
data.tar.gz: 69d129517e6220d11686e0a1447f6c678a1056d0a60c5923964c465b48050ea64382b2aad00d0af00f5ba03060e9e754dd733164510189cc5351bb624a98deed
|
data/README.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
=========
|
1
|
+
## "Data is a precious thing and will last longer than the systems themselves."
|
3
2
|
|
4
3
|
[![Code Climate](https://codeclimate.com/github/benSlaughter/segregate.png)](https://codeclimate.com/github/benSlaughter/segregate)
|
5
4
|
[![Build Status](https://travis-ci.org/benSlaughter/segregate.png?branch=master)](https://travis-ci.org/benSlaughter/segregate)
|
@@ -8,3 +7,129 @@ Segregate
|
|
8
7
|
[![Gem Version](https://badge.fury.io/rb/segregate.png)](http://badge.fury.io/rb/segregate)
|
9
8
|
|
10
9
|
An http parser that also includes URI parsing and retaining and rebuilding the original data
|
10
|
+
|
11
|
+
---------
|
12
|
+
|
13
|
+
Segregate is an easy to use http parser, including object callback and the ability to rebuild the http message.
|
14
|
+
|
15
|
+
Segregate is designed so that it is not only incredibly easy to parse incoming data in any state, and uses URI to parse the request line path. There is also the ability to be able to manipulate and change the data and reform the message into data that can then be reused or forwarded.
|
16
|
+
|
17
|
+
### Limitations
|
18
|
+
Currently the parser is unable to handle multiple headers with the same key.
|
19
|
+
|
20
|
+
## Setup
|
21
|
+
Segregate has been tested with Ruby 1.9.2 and later.
|
22
|
+
To install:
|
23
|
+
|
24
|
+
```bash
|
25
|
+
gem install segregate
|
26
|
+
```
|
27
|
+
|
28
|
+
## Using Segregate
|
29
|
+
|
30
|
+
Require Segregate at the start of your code
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
require 'segregate'
|
34
|
+
```
|
35
|
+
|
36
|
+
### Basic useage
|
37
|
+
#### Parsing data:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
parser = Segregate.new
|
41
|
+
parser.parse data
|
42
|
+
```
|
43
|
+
|
44
|
+
#### Accessing data:
|
45
|
+
|
46
|
+
```Ruby
|
47
|
+
parser.request_line
|
48
|
+
parser.request_method
|
49
|
+
parser.request_url
|
50
|
+
|
51
|
+
parser.status_line
|
52
|
+
parser.status_code
|
53
|
+
parser.status_phrase
|
54
|
+
|
55
|
+
parser.http_version
|
56
|
+
parser.major_http_version
|
57
|
+
parser.minor_http_version
|
58
|
+
|
59
|
+
parser.headers
|
60
|
+
parser.body
|
61
|
+
|
62
|
+
parser.raw_data
|
63
|
+
```
|
64
|
+
|
65
|
+
#### Modifying data:
|
66
|
+
|
67
|
+
```Ruby
|
68
|
+
parser.request_method = "POST"
|
69
|
+
parser.path = "/new/endpoint"
|
70
|
+
|
71
|
+
parser.status_code = 404
|
72
|
+
parser.status_phrase = "Not Found"
|
73
|
+
|
74
|
+
parser.http_version = [0.2]
|
75
|
+
parser.major_http_version = 3
|
76
|
+
parser.minor_http_version = 4
|
77
|
+
|
78
|
+
parser.headers.host = "www.example.com"
|
79
|
+
parser.headers['accept'] = "application/json"
|
80
|
+
|
81
|
+
parser.body.sub! "data", "information"
|
82
|
+
```
|
83
|
+
|
84
|
+
### Callback useage
|
85
|
+
|
86
|
+
```Ruby
|
87
|
+
class Callback_object
|
88
|
+
def on_message_begin parser
|
89
|
+
end
|
90
|
+
|
91
|
+
def on_request_line parser
|
92
|
+
end
|
93
|
+
|
94
|
+
def on_status_line parser
|
95
|
+
end
|
96
|
+
|
97
|
+
def on_headers_complete parser
|
98
|
+
end
|
99
|
+
|
100
|
+
def on_body chunk
|
101
|
+
end
|
102
|
+
|
103
|
+
def on_body_complete parser
|
104
|
+
end
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
```Ruby
|
109
|
+
parser = Segregate.new(Callback_object.new)
|
110
|
+
```
|
111
|
+
|
112
|
+
### Segregate with event machine
|
113
|
+
|
114
|
+
```Ruby
|
115
|
+
module MyHttpConnection
|
116
|
+
def connection_completed
|
117
|
+
@parser = Segregate.new(self)
|
118
|
+
end
|
119
|
+
|
120
|
+
def receive_data(data)
|
121
|
+
@parser << data
|
122
|
+
end
|
123
|
+
|
124
|
+
def on_body_complete parser
|
125
|
+
puts parser.body
|
126
|
+
end
|
127
|
+
end
|
128
|
+
```
|
129
|
+
|
130
|
+
## Rebuilding message
|
131
|
+
### Forwarding raw data
|
132
|
+
|
133
|
+
```Ruby
|
134
|
+
socket.write parser.raw_data
|
135
|
+
```
|
data/lib/segregate/version.rb
CHANGED
data/lib/segregate.rb
CHANGED
@@ -4,7 +4,8 @@ require 'segregate/http_methods'
|
|
4
4
|
require 'segregate/http_regular_expressions'
|
5
5
|
|
6
6
|
class Segregate
|
7
|
-
attr_reader :uri
|
7
|
+
attr_reader :uri
|
8
|
+
attr_accessor :request_method, :status_code, :status_phrase, :http_version, :headers, :body
|
8
9
|
|
9
10
|
def method_missing meth, *args, &block
|
10
11
|
if @uri.respond_to? meth
|
@@ -18,13 +19,8 @@ class Segregate
|
|
18
19
|
@uri.respond_to?(meth, include_private) || super
|
19
20
|
end
|
20
21
|
|
21
|
-
def initialize callback = nil
|
22
|
+
def initialize callback = nil
|
22
23
|
@callback = callback
|
23
|
-
|
24
|
-
@uri = nil
|
25
|
-
@request_method = nil
|
26
|
-
@status_code = nil
|
27
|
-
@status_phrase = nil
|
28
24
|
@http_version = [nil, nil]
|
29
25
|
|
30
26
|
@headers = Hashie::Mash.new
|
@@ -36,7 +32,7 @@ class Segregate
|
|
36
32
|
@first_line_complete = false
|
37
33
|
@headers_complete = false
|
38
34
|
@body_complete = false
|
39
|
-
@
|
35
|
+
@header_orders = []
|
40
36
|
end
|
41
37
|
|
42
38
|
def request?
|
@@ -71,10 +67,42 @@ class Segregate
|
|
71
67
|
http_version[0]
|
72
68
|
end
|
73
69
|
|
70
|
+
def major_http_version= val
|
71
|
+
http_version[0] = val
|
72
|
+
end
|
73
|
+
|
74
74
|
def minor_http_version
|
75
75
|
http_version[1]
|
76
76
|
end
|
77
77
|
|
78
|
+
def minor_http_version= val
|
79
|
+
http_version[1] = val
|
80
|
+
end
|
81
|
+
|
82
|
+
def update_content_length
|
83
|
+
if @body_complete
|
84
|
+
@headers['content-length'] = @body.length.to_s
|
85
|
+
@header_orders.push 'content-length' unless @header_orders.include? 'content-length'
|
86
|
+
@headers.delete 'content-encoding'
|
87
|
+
@header_orders.delete 'content-encoding'
|
88
|
+
else
|
89
|
+
raise "ERROR: parsing message body not complete"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def raw_data
|
94
|
+
raw_message = ""
|
95
|
+
update_content_length
|
96
|
+
|
97
|
+
request? ? raw_message << request_line + "\r\n" : raw_message << status_line + "\r\n"
|
98
|
+
|
99
|
+
@header_orders.each do |header|
|
100
|
+
raw_message << "%s: %s\r\n" % [header, headers[header]]
|
101
|
+
end
|
102
|
+
|
103
|
+
raw_message << "\r\n" + @body + "\r\n\r\n"
|
104
|
+
end
|
105
|
+
|
78
106
|
def parse data
|
79
107
|
raise "ERROR: parsing completed" if @body_complete
|
80
108
|
|
@@ -138,7 +166,7 @@ class Segregate
|
|
138
166
|
else
|
139
167
|
key, value = line.split(":")
|
140
168
|
@headers[key.downcase] = value.strip
|
141
|
-
@
|
169
|
+
@header_orders << key.downcase
|
142
170
|
end
|
143
171
|
end
|
144
172
|
|
@@ -157,6 +185,7 @@ class Segregate
|
|
157
185
|
|
158
186
|
def parse_body data
|
159
187
|
@body = read data, headers['content-length'].to_i
|
188
|
+
@callback.on_body @body if @callback.respond_to?(:on_body)
|
160
189
|
@body_complete = true
|
161
190
|
end
|
162
191
|
|
@@ -166,7 +195,9 @@ class Segregate
|
|
166
195
|
if chunk_size == 0
|
167
196
|
@body_complete = true
|
168
197
|
else
|
169
|
-
|
198
|
+
chunk = read(data, chunk_size)
|
199
|
+
@body << chunk
|
200
|
+
@callback.on_body chunk if @callback.respond_to?(:on_body)
|
170
201
|
end
|
171
202
|
end
|
172
203
|
end
|
data/spec/segregate_spec.rb
CHANGED
@@ -61,6 +61,24 @@ describe Segregate do
|
|
61
61
|
it 'returns a request line' do
|
62
62
|
expect(@parser.request_line).to match Segregate::REQUEST_LINE
|
63
63
|
end
|
64
|
+
|
65
|
+
it 'returns a modified method request line' do
|
66
|
+
@parser.request_method = 'POST'
|
67
|
+
expect(@parser.request_line).to match Segregate::REQUEST_LINE
|
68
|
+
expect(@parser.request_line).to eq "POST /endpoint HTTP/1.1"
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'returns a modified path request line' do
|
72
|
+
@parser.path = "/new/endpoint"
|
73
|
+
expect(@parser.request_line).to match Segregate::REQUEST_LINE
|
74
|
+
expect(@parser.request_line).to eq "GET /new/endpoint HTTP/1.1"
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'returns a modified http version request line' do
|
78
|
+
@parser.http_version = [2,3]
|
79
|
+
expect(@parser.request_line).to match Segregate::REQUEST_LINE
|
80
|
+
expect(@parser.request_line).to eq "GET /endpoint HTTP/2.3"
|
81
|
+
end
|
64
82
|
end
|
65
83
|
|
66
84
|
describe '#status_line' do
|
@@ -186,6 +204,24 @@ describe Segregate do
|
|
186
204
|
it 'returns a status line' do
|
187
205
|
expect(@parser.status_line).to match Segregate::STATUS_LINE
|
188
206
|
end
|
207
|
+
|
208
|
+
it 'returns a modified http version status line' do
|
209
|
+
@parser.http_version = [2,3]
|
210
|
+
expect(@parser.status_line).to match Segregate::STATUS_LINE
|
211
|
+
expect(@parser.status_line).to eq "HTTP/2.3 200 OK"
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'returns a modified status code status line' do
|
215
|
+
@parser.status_code = 404
|
216
|
+
expect(@parser.status_line).to match Segregate::STATUS_LINE
|
217
|
+
expect(@parser.status_line).to eq "HTTP/1.1 404 OK"
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'returns a modified status phrase status line' do
|
221
|
+
@parser.status_phrase = 'NOT_OK'
|
222
|
+
expect(@parser.status_line).to match Segregate::STATUS_LINE
|
223
|
+
expect(@parser.status_line).to eq "HTTP/1.1 200 NOT_OK"
|
224
|
+
end
|
189
225
|
end
|
190
226
|
|
191
227
|
describe '#request?' do
|
@@ -312,6 +348,12 @@ describe Segregate do
|
|
312
348
|
expect(@parser.headers.accept).to eq 'application/json'
|
313
349
|
expect(@parser.headers.host).to eq 'www.google.com'
|
314
350
|
end
|
351
|
+
|
352
|
+
it 'contains modified headers' do
|
353
|
+
@parser.headers.host = 'www.yahoo.com'
|
354
|
+
expect(@parser.headers).to respond_to(:host)
|
355
|
+
expect(@parser.headers.host).to eq 'www.yahoo.com'
|
356
|
+
end
|
315
357
|
end
|
316
358
|
|
317
359
|
describe '#headers_complete?' do
|
@@ -336,7 +378,7 @@ describe Segregate do
|
|
336
378
|
end
|
337
379
|
|
338
380
|
it 'contains the body text' do
|
339
|
-
expect(@parser.body).to eq
|
381
|
+
expect(@parser.body).to eq 'This is the content!'
|
340
382
|
end
|
341
383
|
end
|
342
384
|
|
@@ -345,6 +387,21 @@ describe Segregate do
|
|
345
387
|
expect(@parser.body_complete?).to be_an_instance_of TrueClass
|
346
388
|
end
|
347
389
|
end
|
390
|
+
|
391
|
+
describe '#update_content_length' do
|
392
|
+
it 'updates the content lenght header' do
|
393
|
+
expect(@parser.headers['content-length']).to eq '20'
|
394
|
+
@parser.body = 'new content'
|
395
|
+
@parser.update_content_length
|
396
|
+
expect(@parser.headers['content-length']).to eq '11'
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
describe '#raw_data' do
|
401
|
+
it 'returns the raw message' do
|
402
|
+
expect(@parser.raw_data).to eq "GET /endpoint HTTP/1.1\r\nhost: www.google.com\r\ncontent-length: 20\r\n\r\nThis is the content!\r\n\r\n"
|
403
|
+
end
|
404
|
+
end
|
348
405
|
end
|
349
406
|
|
350
407
|
context 'a partial chunked body has been parsed' do
|
@@ -362,7 +419,7 @@ describe Segregate do
|
|
362
419
|
end
|
363
420
|
|
364
421
|
it 'contains the body text' do
|
365
|
-
expect(@parser.body).to eq
|
422
|
+
expect(@parser.body).to eq 'This is the first content!'
|
366
423
|
end
|
367
424
|
end
|
368
425
|
|
@@ -372,6 +429,18 @@ describe Segregate do
|
|
372
429
|
end
|
373
430
|
end
|
374
431
|
|
432
|
+
describe '#update_content_length' do
|
433
|
+
it 'raises an error if the body is not complete' do
|
434
|
+
expect{ @parser.update_content_length }.to raise_error RuntimeError, 'ERROR: parsing message body not complete'
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
describe '#raw_data' do
|
439
|
+
it 'raises an error if the body is not complete' do
|
440
|
+
expect{ @parser.raw_data }.to raise_error RuntimeError, 'ERROR: parsing message body not complete'
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
375
444
|
context 'the body parsing is completed' do
|
376
445
|
before(:each) do
|
377
446
|
@parser.parse "27\r\nThis is the second content!\r\n"
|
@@ -384,7 +453,12 @@ describe Segregate do
|
|
384
453
|
end
|
385
454
|
|
386
455
|
it 'contains the body text' do
|
387
|
-
expect(@parser.body).to eq
|
456
|
+
expect(@parser.body).to eq 'This is the first content!This is the second content!'
|
457
|
+
end
|
458
|
+
|
459
|
+
it 'contains the modified body' do
|
460
|
+
@parser.body.sub!('first', 'third')
|
461
|
+
expect(@parser.body).to eq 'This is the third content!This is the second content!'
|
388
462
|
end
|
389
463
|
end
|
390
464
|
|
@@ -393,6 +467,23 @@ describe Segregate do
|
|
393
467
|
expect(@parser.body_complete?).to be_an_instance_of TrueClass
|
394
468
|
end
|
395
469
|
end
|
470
|
+
|
471
|
+
describe '#update_content_length' do
|
472
|
+
it 'updates the content lenght header' do
|
473
|
+
expect(@parser.headers['content-length']).to be_nil
|
474
|
+
expect(@parser.headers['content-encoding']).to eq 'chunked'
|
475
|
+
@parser.body = 'new content'
|
476
|
+
@parser.update_content_length
|
477
|
+
expect(@parser.headers['content-length']).to eq '11'
|
478
|
+
expect(@parser.headers['content-encoding']).to be_nil
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
describe '#raw_data' do
|
483
|
+
it 'returns the raw message' do
|
484
|
+
expect(@parser.raw_data).to eq "GET /endpoint HTTP/1.1\r\nhost: www.google.com\r\ncontent-length: 53\r\n\r\nThis is the first content!This is the second content!\r\n\r\n"
|
485
|
+
end
|
486
|
+
end
|
396
487
|
end
|
397
488
|
end
|
398
489
|
end
|
@@ -432,6 +523,15 @@ describe Segregate do
|
|
432
523
|
end
|
433
524
|
end
|
434
525
|
|
526
|
+
describe 'on_body' do
|
527
|
+
it 'calls the callback object' do
|
528
|
+
@callback_object.should_receive(:on_body).with("TestData")
|
529
|
+
@parser.parse "GET /endpoint HTTP/1.1\r\n"
|
530
|
+
@parser.parse "Content-Length: 8\r\n\r\n"
|
531
|
+
@parser.parse "TestData\r\n\r\n"
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
435
535
|
describe 'on_body_complete' do
|
436
536
|
it 'calls the callback object' do
|
437
537
|
@callback_object.should_receive(:on_body_complete).with(@parser)
|