Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Commit 66c46ab

Browse files
author
Adam Kliment
committed
Whole http parser
1 parent 2b1e06c commit 66c46ab

File tree

13 files changed

+555
-11
lines changed

13 files changed

+555
-11
lines changed

README.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,23 @@ console.log(message['request']);
1818
console.log(message['response']);
1919
```
2020

21+
See more about [Request][request] and [Response][response] data model.
22+
23+
[request]: https://www.relishapp.com/apiary/gavel/docs/data-model#http-request
24+
[response]: https://www.relishapp.com/apiary/gavel/docs/data-model#http-response
25+
26+
## API Reference
27+
28+
`parseRequest(requestString)`
29+
30+
`parseRequestLine(requestLine)`
31+
32+
`parseResponse(responseString)`
33+
34+
`parseStatusLine(statusLine)`
35+
36+
`praseHeaders(headersLinesArray)`
37+
2138
- - -
2239

23-
NOTE: Proof of concept, naive HTTP parsing. In future may be replaced with better parser from Node.JS core's C bindings of NGINX Http parser
40+
NOTE: Proof of concept, naive HTTP parsing, wheel re-inventation. In future it may be replaced with better parser from [Node.JS core's C bindings of NGINX HTTP parser](https://github.com/joyent/http-parser)

src/parser.coffee

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,74 @@
1-
module.exports.parse = () ->
1+
parseRequest = (requestString) ->
2+
request = {}
3+
lines = requestString.split('\r\n')
4+
5+
parsedRequestLine = parseRequestLine lines.shift()
6+
request['method'] = parsedRequestLine['method']
7+
request['uri'] = parsedRequestLine['uri']
8+
9+
#TODO refactor this part to be tested
10+
headerLines = []
11+
while lines.length > 0
12+
line = lines.shift()
13+
break if line == ""
14+
headerLines.push line
15+
16+
request['headers'] = parseHeaders headerLines
17+
request['body'] = lines.join '\r\n'
18+
19+
request
20+
21+
parseResponse = (responseString) ->
22+
response = {}
23+
lines = responseString.split('\r\n')
24+
25+
parsedStatusLine = parseStatusLine lines.shift()
26+
response['statusCode'] = parsedStatusLine['statusCode']
27+
response['statusMessage'] = parsedStatusLine['statusMessage']
28+
29+
#TODO refactor this part to be tested
30+
headerLines = []
31+
while lines.length > 0
32+
line = lines.shift()
33+
break if line == ""
34+
headerLines.push line
35+
36+
response['headers'] = parseHeaders headerLines
37+
response['body'] = lines.join '\r\n'
38+
39+
response
40+
41+
parseHeaders = (headerLines) ->
42+
headers = {}
43+
for line in headerLines
44+
parts = line.split(":")
45+
key = parts.shift()
46+
headers[key] = parts.join(":").trim()
47+
48+
headers
49+
50+
parseStatusLine = (statusLine) ->
51+
parts = statusLine.split ' '
52+
parsed = {}
53+
54+
parsed['protocol'] = parts[0]
55+
parsed['statusCode'] = parts[1]
56+
parsed['statusMessage'] = parts[2]
57+
58+
parsed
59+
60+
parseRequestLine = (requestLineString) ->
61+
parts = requestLineString.split(' ')
62+
parsed = {}
63+
64+
parsed['method'] = parts[0]
65+
parsed['uri'] = parts[1]
66+
parsed['protocol'] = parts[2]
67+
68+
parsed
69+
70+
module.exports.parseRequest = parseRequest
71+
module.exports.parseResponse = parseResponse
72+
module.exports.parseRequestLine = parseRequestLine
73+
module.exports.parseStatusLine = parseStatusLine
74+
module.exports.parseHeaders = parseHeaders

test/fixtures/get/expected-output

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
> GET /shopping-cart HTTP/1.1
2+
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
3+
> Host: curltraceparser.apiary.io
4+
> Accept: */*
5+
>
6+
>
7+
8+
< HTTP/1.1 200 OK
9+
< Content-Type: application/json
10+
< Date: Sun, 21 Jul 2013 13:23:55 GMT
11+
< X-Apiary-Ratelimit-Limit: 120
12+
< X-Apiary-Ratelimit-Remaining: 119
13+
< Content-Length: 119
14+
< Connection: keep-alive
15+
<
16+
< { "items": [
17+
{ "url": "/shopping-cart/1", "product":"2ZY48XPZ", "quantity": 1, "name": "New socks", "price": 1.25 }
18+
] }

test/fixtures/get/request-string

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
GET /shopping-cart HTTP/1.1
2+
User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
3+
Host: curltraceparser.apiary.io
4+
Accept: */*
5+

test/fixtures/get/response-string

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
HTTP/1.1 200 OK
2+
Content-Type: application/json
3+
Date: Sun, 21 Jul 2013 13:23:55 GMT
4+
X-Apiary-Ratelimit-Limit: 120
5+
X-Apiary-Ratelimit-Remaining: 119
6+
Content-Length: 119
7+
Connection: keep-alive
8+
9+
{ "items": [
10+
{ "url": "/shopping-cart/1", "product":"2ZY48XPZ", "quantity": 1, "name": "New socks", "price": 1.25 }
11+
] }

test/fixtures/get/tracefile

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
== Info: About to connect() to curltraceparser.apiary.io port 80 (#0)
2+
== Info: Trying 54.235.100.115...
3+
== Info: connected
4+
== Info: Connected to curltraceparser.apiary.io (54.235.100.115) port 80 (#0)
5+
=> Send header, 169 bytes (0xa9)
6+
0000: 47 45 54 20 2f 73 68 6f 70 70 69 6e 67 2d 63 61 GET /shopping-ca
7+
0010: 72 74 20 48 54 54 50 2f 31 2e 31 0d 0a 55 73 65 rt HTTP/1.1..Use
8+
0020: 72 2d 41 67 65 6e 74 3a 20 63 75 72 6c 2f 37 2e r-Agent: curl/7.
9+
0030: 32 34 2e 30 20 28 78 38 36 5f 36 34 2d 61 70 70 24.0 (x86_64-app
10+
0040: 6c 65 2d 64 61 72 77 69 6e 31 32 2e 30 29 20 6c le-darwin12.0) l
11+
0050: 69 62 63 75 72 6c 2f 37 2e 32 34 2e 30 20 4f 70 ibcurl/7.24.0 Op
12+
0060: 65 6e 53 53 4c 2f 30 2e 39 2e 38 78 20 7a 6c 69 enSSL/0.9.8x zli
13+
0070: 62 2f 31 2e 32 2e 35 0d 0a 48 6f 73 74 3a 20 63 b/1.2.5..Host: c
14+
0080: 75 72 6c 74 72 61 63 65 70 61 72 73 65 72 2e 61 urltraceparser.a
15+
0090: 70 69 61 72 79 2e 69 6f 0d 0a 41 63 63 65 70 74 piary.io..Accept
16+
00a0: 3a 20 2a 2f 2a 0d 0a 0d 0a : */*....
17+
<= Recv header, 17 bytes (0x11)
18+
0000: 48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d HTTP/1.1 200 OK.
19+
0010: 0a .
20+
<= Recv header, 32 bytes (0x20)
21+
0000: 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 61 70 Content-Type: ap
22+
0010: 70 6c 69 63 61 74 69 6f 6e 2f 6a 73 6f 6e 0d 0a plication/json..
23+
<= Recv header, 37 bytes (0x25)
24+
0000: 44 61 74 65 3a 20 53 75 6e 2c 20 32 31 20 4a 75 Date: Sun, 21 Ju
25+
0010: 6c 20 32 30 31 33 20 31 33 3a 32 33 3a 35 35 20 l 2013 13:23:55
26+
0020: 47 4d 54 0d 0a GMT..
27+
<= Recv header, 31 bytes (0x1f)
28+
0000: 58 2d 41 70 69 61 72 79 2d 52 61 74 65 6c 69 6d X-Apiary-Ratelim
29+
0010: 69 74 2d 4c 69 6d 69 74 3a 20 31 32 30 0d 0a it-Limit: 120..
30+
<= Recv header, 35 bytes (0x23)
31+
0000: 58 2d 41 70 69 61 72 79 2d 52 61 74 65 6c 69 6d X-Apiary-Ratelim
32+
0010: 69 74 2d 52 65 6d 61 69 6e 69 6e 67 3a 20 31 31 it-Remaining: 11
33+
0020: 39 0d 0a 9..
34+
<= Recv header, 21 bytes (0x15)
35+
0000: 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 Content-Length:
36+
0010: 31 31 39 0d 0a 119..
37+
<= Recv header, 24 bytes (0x18)
38+
0000: 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 70 Connection: keep
39+
0010: 2d 61 6c 69 76 65 0d 0a -alive..
40+
<= Recv header, 2 bytes (0x2)
41+
0000: 0d 0a ..
42+
<= Recv data, 119 bytes (0x77)
43+
0000: 7b 20 22 69 74 65 6d 73 22 3a 20 5b 0a 7b 20 22 { "items": [.{ "
44+
0010: 75 72 6c 22 3a 20 22 2f 73 68 6f 70 70 69 6e 67 url": "/shopping
45+
0020: 2d 63 61 72 74 2f 31 22 2c 20 22 70 72 6f 64 75 -cart/1", "produ
46+
0030: 63 74 22 3a 22 32 5a 59 34 38 58 50 5a 22 2c 20 ct":"2ZY48XPZ",
47+
0040: 22 71 75 61 6e 74 69 74 79 22 3a 20 31 2c 20 22 "quantity": 1, "
48+
0050: 6e 61 6d 65 22 3a 20 22 4e 65 77 20 73 6f 63 6b name": "New sock
49+
0060: 73 22 2c 20 22 70 72 69 63 65 22 3a 20 31 2e 32 s", "price": 1.2
50+
0070: 35 20 7d 0a 5d 20 7d 5 }.] }
51+
== Info: Connection #0 to host curltraceparser.apiary.io left intact
52+
== Info: Closing connection #0

test/fixtures/post/expected-output

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
> POST /shopping-cart HTTP/1.1
2+
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
3+
> Host: curltraceparser.apiary.io
4+
> Accept: */*
5+
> Content-Type: application/json
6+
> Content-Length: 39
7+
>
8+
> { "product":"1AB23ORM", "quantity": 2 }
9+
10+
< HTTP/1.1 201 Created
11+
< Content-Type: application/json
12+
< Date: Sun, 21 Jul 2013 14:51:09 GMT
13+
< X-Apiary-Ratelimit-Limit: 120
14+
< X-Apiary-Ratelimit-Remaining: 119
15+
< Content-Length: 50
16+
< Connection: keep-alive
17+
<
18+
< { "status": "created", "url": "/shopping-cart/2" }

test/fixtures/post/request-string

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
POST /shopping-cart HTTP/1.1
2+
User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
3+
Host: curltraceparser.apiary.io
4+
Accept: */*
5+
Content-Type: application/json
6+
Content-Length: 39
7+
8+
{ "product":"1AB23ORM", "quantity": 2 }

test/fixtures/post/response-string

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
HTTP/1.1 201 Created
2+
Content-Type: application/json
3+
Date: Sun, 21 Jul 2013 14:51:09 GMT
4+
X-Apiary-Ratelimit-Limit: 120
5+
X-Apiary-Ratelimit-Remaining: 119
6+
Content-Length: 50
7+
Connection: keep-alive
8+
9+
{ "status": "created", "url": "/shopping-cart/2" }

test/fixtures/post/tracefile

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
== Info: About to connect() to curltraceparser.apiary.io port 80 (#0)
2+
== Info: Trying 54.235.90.239...
3+
== Info: connected
4+
== Info: Connected to curltraceparser.apiary.io (54.235.90.239) port 80 (#0)
5+
=> Send header, 222 bytes (0xde)
6+
0000: 50 4f 53 54 20 2f 73 68 6f 70 70 69 6e 67 2d 63 POST /shopping-c
7+
0010: 61 72 74 20 48 54 54 50 2f 31 2e 31 0d 0a 55 73 art HTTP/1.1..Us
8+
0020: 65 72 2d 41 67 65 6e 74 3a 20 63 75 72 6c 2f 37 er-Agent: curl/7
9+
0030: 2e 32 34 2e 30 20 28 78 38 36 5f 36 34 2d 61 70 .24.0 (x86_64-ap
10+
0040: 70 6c 65 2d 64 61 72 77 69 6e 31 32 2e 30 29 20 ple-darwin12.0)
11+
0050: 6c 69 62 63 75 72 6c 2f 37 2e 32 34 2e 30 20 4f libcurl/7.24.0 O
12+
0060: 70 65 6e 53 53 4c 2f 30 2e 39 2e 38 78 20 7a 6c penSSL/0.9.8x zl
13+
0070: 69 62 2f 31 2e 32 2e 35 0d 0a 48 6f 73 74 3a 20 ib/1.2.5..Host:
14+
0080: 63 75 72 6c 74 72 61 63 65 70 61 72 73 65 72 2e curltraceparser.
15+
0090: 61 70 69 61 72 79 2e 69 6f 0d 0a 41 63 63 65 70 apiary.io..Accep
16+
00a0: 74 3a 20 2a 2f 2a 0d 0a 43 6f 6e 74 65 6e 74 2d t: */*..Content-
17+
00b0: 54 79 70 65 3a 20 61 70 70 6c 69 63 61 74 69 6f Type: applicatio
18+
00c0: 6e 2f 6a 73 6f 6e 0d 0a 43 6f 6e 74 65 6e 74 2d n/json..Content-
19+
00d0: 4c 65 6e 67 74 68 3a 20 33 39 0d 0a 0d 0a Length: 39....
20+
=> Send data, 39 bytes (0x27)
21+
0000: 7b 20 22 70 72 6f 64 75 63 74 22 3a 22 31 41 42 { "product":"1AB
22+
0010: 32 33 4f 52 4d 22 2c 20 22 71 75 61 6e 74 69 74 23ORM", "quantit
23+
0020: 79 22 3a 20 32 20 7d y": 2 }
24+
== Info: upload completely sent off: 39 out of 39 bytes
25+
<= Recv header, 22 bytes (0x16)
26+
0000: 48 54 54 50 2f 31 2e 31 20 32 30 31 20 43 72 65 HTTP/1.1 201 Cre
27+
0010: 61 74 65 64 0d 0a ated..
28+
<= Recv header, 32 bytes (0x20)
29+
0000: 43 6f 6e 74 65 6e 74 2d 54 79 70 65 3a 20 61 70 Content-Type: ap
30+
0010: 70 6c 69 63 61 74 69 6f 6e 2f 6a 73 6f 6e 0d 0a plication/json..
31+
<= Recv header, 37 bytes (0x25)
32+
0000: 44 61 74 65 3a 20 53 75 6e 2c 20 32 31 20 4a 75 Date: Sun, 21 Ju
33+
0010: 6c 20 32 30 31 33 20 31 34 3a 35 31 3a 30 39 20 l 2013 14:51:09
34+
0020: 47 4d 54 0d 0a GMT..
35+
<= Recv header, 31 bytes (0x1f)
36+
0000: 58 2d 41 70 69 61 72 79 2d 52 61 74 65 6c 69 6d X-Apiary-Ratelim
37+
0010: 69 74 2d 4c 69 6d 69 74 3a 20 31 32 30 0d 0a it-Limit: 120..
38+
<= Recv header, 35 bytes (0x23)
39+
0000: 58 2d 41 70 69 61 72 79 2d 52 61 74 65 6c 69 6d X-Apiary-Ratelim
40+
0010: 69 74 2d 52 65 6d 61 69 6e 69 6e 67 3a 20 31 31 it-Remaining: 11
41+
0020: 39 0d 0a 9..
42+
<= Recv header, 20 bytes (0x14)
43+
0000: 43 6f 6e 74 65 6e 74 2d 4c 65 6e 67 74 68 3a 20 Content-Length:
44+
0010: 35 30 0d 0a 50..
45+
<= Recv header, 24 bytes (0x18)
46+
0000: 43 6f 6e 6e 65 63 74 69 6f 6e 3a 20 6b 65 65 70 Connection: keep
47+
0010: 2d 61 6c 69 76 65 0d 0a -alive..
48+
<= Recv header, 2 bytes (0x2)
49+
0000: 0d 0a ..
50+
<= Recv data, 50 bytes (0x32)
51+
0000: 7b 20 22 73 74 61 74 75 73 22 3a 20 22 63 72 65 { "status": "cre
52+
0010: 61 74 65 64 22 2c 20 22 75 72 6c 22 3a 20 22 2f ated", "url": "/
53+
0020: 73 68 6f 70 70 69 6e 67 2d 63 61 72 74 2f 32 22 shopping-cart/2"
54+
0030: 20 7d }
55+
== Info: Connection #0 to host curltraceparser.apiary.io left intact
56+
== Info: Closing connection #0

0 commit comments

Comments
 (0)