1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
|
# fast-http
[](https://travis-ci.org/fukamachi/fast-http)
This is a fast HTTP request/response protocol parser for Common Lisp.
## Features
* Parses both HTTP requests and responses.
* Handles persistent streams (keep-alive).
* Decodes chunked encoding.
* Exports low-level APIs which don't any memory allocations during parsing.
## API differences from http-parse
The API is quite similar to [http-parse](https://github.com/orthecreedence/http-parse), although there's some differences.
* `http`, `http-request` and `http-response` are structure classes, not standard classes.
* `http` doesn't have `:force-stream` option. (always streaming)
* `http` doesn't have `:store-body` option because it can consume much memory.
* `body-callback` for `make-parser` doesn't take a flag `body-complete-p`.
* Use `finish-callback` to know if the parsing is finished.
* `body-callback` for `make-parser` takes pointers `start` and `end`.
* `multipart-callback` for `make-parser` has been deleted.
* Use `make-multipart-parser` and `body-callback` by yourself.
* `:callback` of `make-multipart-parser` takes a stream, not a body octet vector at the 4th argument.
* Raises errors aggressively while parsing.
* Handle `fast-http-error` as you needed.
* Doesn't use a property list as a representation of HTTP headers. (See [issue #1](https://github.com/fukamachi/fast-http/issues/1))
## APIs
### \[Structure] http
Base structure class extended by `http-request` and `http-response`.
**NOTE**: Don't use this class directly unless you're intended to use low-level APIs of fast-http.
```common-lisp
(make-http)
;=> #S(FAST-HTTP.HTTP:HTTP
; :METHOD NIL
; :MAJOR-VERSION 0
; :MINOR-VERSION 9
; :STATUS 0
; :CONTENT-LENGTH NIL
; :CHUNKED-P NIL
; :UPGRADE-P NIL
; :HEADERS NIL
; :HEADER-READ 0
; :MARK -1
; :STATE 0)
```
#### Methods
- `http-method`: Returns a HTTP request method in a keyword (such like `:GET`, `:POST` or `:HEAD`).
- `http-major-version`: Returns a HTTP protocol major version in an integer (such like `1` or `0`).
- `http-minor-version`: Returns a HTTP protocol minor version in an integer (such like `1` or `0`).
- `http-version`: Returns a HTTP protocol version in a float (such like `1.0` or `1.1`).
- `http-status`: Returns a HTTP response status code in an integer (such like `200` or `302`).
- `http-content-length`: Returns a value of `Content-Length` header in an integer. If the header doesn't exist, it returns `NIL`.
- `http-chunked-p`: Returns `T` if the value of `Transfer-Encoding` header is `chunked`. If the header doesn't exist, it returns `NIL`.
- `http-upgrade-p`: Returns `T` if `Upgrade` header exists.
- `http-headers`: Returns a hash-table which represents HTTP headers. Note all hash keys are lower-cased and all values are string except `Set-Cookie` header, whose value is a list of strings. (`Content-Length` -> `"content-length"`).
### \[Structure] http-request (extends http)
Structure class holds values specific to an HTTP request.
```common-lisp
(make-http-request)
;=> #S(FAST-HTTP.HTTP:HTTP-REQUEST
; :METHOD NIL
; :MAJOR-VERSION 0
; :MINOR-VERSION 9
; :STATUS 0
; :CONTENT-LENGTH NIL
; :CHUNKED-P NIL
; :UPGRADE-P NIL
; :HEADERS NIL
; :HEADER-READ 0
; :MARK -1
; :STATE 0
; :RESOURCE NIL)
```
#### Methods
- `http-resource`: Returns an URI string.
### \[Structure] http-response (extends http)
Structure class holds values specific to an HTTP response.
```common-lisp
(make-http-response)
;=> #S(FAST-HTTP.HTTP:HTTP-RESPONSE
; :METHOD NIL
; :MAJOR-VERSION 0
; :MINOR-VERSION 9
; :STATUS 0
; :CONTENT-LENGTH NIL
; :CHUNKED-P NIL
; :UPGRADE-P NIL
; :HEADERS NIL
; :HEADER-READ 0
; :MARK -1
; :STATE 0
; :STATUS-TEXT NIL)
```
#### Methods
- `http-status-text`: Returns an response status text (such like `Continue`, `OK` or `Bad Request`).
### \[Function] make-parser (http &key first-line-callback header-callback body-callback finish-callback)
Makes a parser closure and returns it.
```common-lisp
(let ((http (make-http-request)))
(make-parser http
:body-callback
(lambda (data start end)
(write-to-buffer data start end))
:finish-callback
(lambda ()
(handle-response http))))
;=> #<CLOSURE (LAMBDA (DATA &KEY (START 0) END)
; :IN
; FAST-HTTP.PARSER:MAKE-PARSER) {10090BDD0B}>
```
The closure takes one required argument `data`, that is a simple byte vector and two keyword arguments `start` and `end`.
#### Callbacks
- `first-line-callback` (): This callback function will be called when the first line is parsed.
- `header-callback` (headers-hash-table): This callback function will be called when the header lines are parsed. This function is the same object to the `http` object holds.
- `body-callback` (data-byte-vector): This callback function will be called whenever it gets a chunk of HTTP body. Which means this can be called multiple times.
- `finish-callback` (): This callback function will be called when the HTTP message ends.
NOTE: If the HTTP request/response has multiple messages (like HTTP/1.1 pipelining), all these functions can be called multiple times.
### \[Function] make-multipart-parser (content-type callback)
Makes a multipart/form-data parser closure and returns it.
This takes 2 arguments, `content-type` (such like `"multipart/form-data; boundary=--AsB03x"`) and `callback`. The `callback` is a function which takes exact 4 arguments -- a field name, field headers, field meta data and body bytes.
## Low-level APIs
The following functions are intended to be used for internally. These APIs are likely to change in the future.
Most of functions are declared as `(optimize (speed 3) (safety 0))` which means it won't check the type of arguments.
### \[Structure] callbacks
Structure class holds callback functions. The callbacks are similar to `make-parser`'s, but don't correspond to them directly.
#### Slots
- `message-begin` (http): This will be called when a new HTTP message begins.
- `url` (http data start end): This will be called when an URL part of the HTTP request parsed.
- `first-line` (http): This will be called when the first line of the HTTP request/response parsed.
- `status` (http data start end): This will be called when the status text (not code) of the HTTP response parsed.
- `header-field` (http data start end): This will be called when a header field parsed.
- `header-value` (http data start end): This will be called when a header value parsed. This function can be called multiple times when the header value is folded onto multiple lines.
- `headers-complete` (http): This will be called when all headers parsed.
- `body` (http data start end): This will be called whenever the parser gets a chunk of HTTP body.
- `message-complete` (http): This will be called when the HTTP message ends.
### \[Function] parse-request (http callbacks data &key (start 0) end)
Parses `data` as an HTTP request, sets values to `http` and invokes callbacks in `callbacks`.
This takes a `http` object, a `callbacks` object, and a simple byte vector `data` and two pointers -- `start` and `end`. If `end` is `nil`, the length of `data` will be used.
### \[Function] parse-response (http callbacks data &key (start 0) end)
Parses `data` as an HTTP response, sets values to `http` and invokes callbacks in `callbacks`.
Takes a `http` object, a `callbacks` object, and a simple byte vector `data` and two pointers -- `start` and `end`. If `end` is `nil`, the length of `data` will be used.
### \[Condition] eof
Will be raised when the `data` ends in the middle of parsing.
## Installation
```common-lisp
(ql:quickload :fast-http)
```
## Running tests
```common-lisp
(asdf:test-system :fast-http)
```
## Benchmark
- Parsing an HTTP request header 100000 times.
In this benchmark, fast-http is **1.25 times faster** than [http-parser](https://github.com/joyent/http-parser), a C equivalent.
| http-parser (C) | fast-http |
| ---------------:| ---------:|
| 0.108s | 0.086s |
### Environment
* Travis CI
* SBCL 1.2.6
You can see the latest result at [Travis CI](https://travis-ci.org/fukamachi/fast-http).
### fast-http (Common Lisp)
```common-lisp
(ql:quickload :fast-http-test)
(fast-http-test.benchmark:run-ll-benchmark)
```
```
Evaluation took:
0.086 seconds of real time
0.085897 seconds of total run time (0.084763 user, 0.001134 system)
100.00% CPU
257,140,751 processor cycles
0 bytes consed
```
### http-parser (C)
```c
#include "http_parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>
static http_parser *parser;
static http_parser_settings settings_null =
{.on_message_begin = 0
,.on_header_field = 0
,.on_header_value = 0
,.on_url = 0
,.on_status = 0
,.on_body = 0
,.on_headers_complete = 0
,.on_message_complete = 0
};
int
main (void)
{
const char *buf;
int i;
float start, end;
size_t parsed;
parser = malloc(sizeof(http_parser));
buf = "GET /cookies HTTP/1.1\r\nHost: 127.0.0.1:8090\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17\r\nAccept-Encoding: gzip,deflate,sdch\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\nCookie: name=wookie\r\n\r\n";
start = (float)clock()/CLOCKS_PER_SEC;
for (i = 0; i < 100000; i++) {
http_parser_init(parser, HTTP_REQUEST);
parsed = http_parser_execute(parser, &settings_null, buf, strlen(buf));
}
end = (float)clock()/CLOCKS_PER_SEC;
free(parser);
parser = NULL;
printf("Elapsed %f seconds.\n", (end - start));
return 0;
}
```
```
$ make http_parser.o
$ gcc -Wall -Wextra -Werror -Wno-error=unused-but-set-variable -O3 http_parser.o mybench.c -o mybench
$ mybench
Elapsed 0.108815 seconds.
```
## Author
* Eitaro Fukamachi (e.arrows@gmail.com)
## Copyright
Copyright (c) 2014 Eitaro Fukamachi
## License
Licensed under the MIT License.
|