{"id":18077663,"url":"https://github.com/fukamachi/fast-http","last_synced_at":"2025-08-02T01:09:17.185Z","repository":{"id":21706478,"uuid":"25027926","full_name":"fukamachi/fast-http","owner":"fukamachi","description":"A fast HTTP request/response parser for Common Lisp.","archived":false,"fork":false,"pushed_at":"2023-11-28T06:18:18.000Z","size":676,"stargazers_count":346,"open_issues_count":4,"forks_count":37,"subscribers_count":31,"default_branch":"master","last_synced_at":"2025-02-11T19:13:13.817Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fukamachi.png","metadata":{"files":{"readme":"README.markdown","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2014-10-10T08:55:01.000Z","updated_at":"2025-01-21T06:50:39.000Z","dependencies_parsed_at":"2023-11-27T03:23:56.314Z","dependency_job_id":null,"html_url":"https://github.com/fukamachi/fast-http","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fukamachi%2Ffast-http","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fukamachi%2Ffast-http/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fukamachi%2Ffast-http/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fukamachi%2Ffast-http/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fukamachi","download_url":"https://codeload.github.com/fukamachi/fast-http/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247394676,"owners_count":20931991,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-31T11:47:01.704Z","updated_at":"2025-04-05T20:20:25.755Z","avatar_url":"https://github.com/fukamachi.png","language":"Common Lisp","readme":"# fast-http\n\n[![Build Status](https://travis-ci.org/fukamachi/fast-http.svg?branch=master)](https://travis-ci.org/fukamachi/fast-http)\n\nThis is a fast HTTP request/response protocol parser for Common Lisp.\n\n## Features\n\n* Parses both HTTP requests and responses.\n* Handles persistent streams (keep-alive).\n* Decodes chunked encoding.\n* Exports low-level APIs which don't any memory allocations during parsing.\n\n## API differences from http-parse\n\nThe API is quite similar to [http-parse](https://github.com/orthecreedence/http-parse), although there's some differences.\n\n* `http`, `http-request` and `http-response` are structure classes, not standard classes.\n* `http` doesn't have `:force-stream` option. (always streaming)\n* `http` doesn't have `:store-body` option because it can consume much memory.\n* `body-callback` for `make-parser` doesn't take a flag `body-complete-p`.\n  * Use `finish-callback` to know if the parsing is finished.\n* `body-callback` for `make-parser` takes pointers `start` and `end`.\n* `multipart-callback` for `make-parser` has been deleted.\n  * Use `make-multipart-parser` and `body-callback` by yourself.\n* `:callback` of `make-multipart-parser` takes a stream, not a body octet vector at the 4th argument.\n* Raises errors aggressively while parsing.\n  * Handle `fast-http-error` as you needed.\n* Doesn't use a property list as a representation of HTTP headers. (See [issue #1](https://github.com/fukamachi/fast-http/issues/1))\n\n## APIs\n\n### \\[Structure] http\n\nBase structure class extended by `http-request` and `http-response`.\n\n**NOTE**: Don't use this class directly unless you're intended to use low-level APIs of fast-http.\n\n```common-lisp\n(make-http)\n;=\u003e #S(FAST-HTTP.HTTP:HTTP\n;     :METHOD NIL\n;     :MAJOR-VERSION 0\n;     :MINOR-VERSION 9\n;     :STATUS 0\n;     :CONTENT-LENGTH NIL\n;     :CHUNKED-P NIL\n;     :UPGRADE-P NIL\n;     :HEADERS NIL\n;     :HEADER-READ 0\n;     :MARK -1\n;     :STATE 0)\n```\n\n#### Methods\n\n- `http-method`: Returns a HTTP request method in a keyword (such like `:GET`, `:POST` or `:HEAD`).\n- `http-major-version`: Returns a HTTP protocol major version in an integer (such like `1` or `0`).\n- `http-minor-version`: Returns a HTTP protocol minor version in an integer (such like `1` or `0`).\n- `http-version`: Returns a HTTP protocol version in a float (such like `1.0` or `1.1`).\n- `http-status`: Returns a HTTP response status code in an integer (such like `200` or `302`).\n- `http-content-length`: Returns a value of `Content-Length` header in an integer. If the header doesn't exist, it returns `NIL`.\n- `http-chunked-p`: Returns `T` if the value of `Transfer-Encoding` header is `chunked`. If the header doesn't exist, it returns `NIL`.\n- `http-upgrade-p`: Returns `T` if `Upgrade` header exists.\n- `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` -\u003e `\"content-length\"`).\n\n### \\[Structure] http-request (extends http)\n\nStructure class holds values specific to an HTTP request.\n\n```common-lisp\n(make-http-request)\n;=\u003e #S(FAST-HTTP.HTTP:HTTP-REQUEST\n;     :METHOD NIL\n;     :MAJOR-VERSION 0\n;     :MINOR-VERSION 9\n;     :STATUS 0\n;     :CONTENT-LENGTH NIL\n;     :CHUNKED-P NIL\n;     :UPGRADE-P NIL\n;     :HEADERS NIL\n;     :HEADER-READ 0\n;     :MARK -1\n;     :STATE 0\n;     :RESOURCE NIL)\n```\n\n#### Methods\n\n- `http-resource`: Returns an URI string.\n\n### \\[Structure] http-response (extends http)\n\nStructure class holds values specific to an HTTP response.\n\n```common-lisp\n(make-http-response)\n;=\u003e #S(FAST-HTTP.HTTP:HTTP-RESPONSE\n;     :METHOD NIL\n;     :MAJOR-VERSION 0\n;     :MINOR-VERSION 9\n;     :STATUS 0\n;     :CONTENT-LENGTH NIL\n;     :CHUNKED-P NIL\n;     :UPGRADE-P NIL\n;     :HEADERS NIL\n;     :HEADER-READ 0\n;     :MARK -1\n;     :STATE 0\n;     :STATUS-TEXT NIL)\n```\n\n#### Methods\n\n- `http-status-text`: Returns an response status text (such like `Continue`, `OK` or `Bad Request`).\n\n### \\[Function] make-parser (http \u0026key first-line-callback header-callback body-callback finish-callback)\n\nMakes a parser closure and returns it.\n\n```common-lisp\n(let ((http (make-http-request)))\n  (make-parser http\n               :body-callback\n               (lambda (data start end)\n                 (write-to-buffer data start end))\n               :finish-callback\n               (lambda ()\n                 (handle-response http))))\n;=\u003e #\u003cCLOSURE (LAMBDA (DATA \u0026KEY (START 0) END)\n;              :IN\n;              FAST-HTTP.PARSER:MAKE-PARSER) {10090BDD0B}\u003e\n```\n\nThe closure takes one required argument `data`, that is a simple byte vector and two keyword arguments `start` and `end`.\n\n#### Callbacks\n\n- `first-line-callback` (): This callback function will be called when the first line is parsed.\n- `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.\n- `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.\n- `finish-callback` (): This callback function will be called when the HTTP message ends.\n\nNOTE: If the HTTP request/response has multiple messages (like HTTP/1.1 pipelining), all these functions can be called multiple times.\n\n### \\[Function] make-multipart-parser (content-type callback)\n\nMakes a multipart/form-data parser closure and returns it.\n\nThis 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.\n\n## Low-level APIs\n\nThe following functions are intended to be used for internally. These APIs are likely to change in the future.\n\nMost of functions are declared as `(optimize (speed 3) (safety 0))` which means it won't check the type of arguments.\n\n### \\[Structure] callbacks\n\nStructure class holds callback functions. The callbacks are similar to `make-parser`'s, but don't correspond to them directly.\n\n#### Slots\n\n- `message-begin` (http): This will be called when a new HTTP message begins.\n- `url` (http data start end): This will be called when an URL part of the HTTP request parsed.\n- `first-line` (http): This will be called when the first line of the HTTP request/response parsed.\n- `status` (http data start end): This will be called when the status text (not code) of the HTTP response parsed.\n- `header-field` (http data start end): This will be called when a header field parsed.\n- `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.\n- `headers-complete` (http): This will be called when all headers parsed.\n- `body` (http data start end): This will be called whenever the parser gets a chunk of HTTP body.\n- `message-complete` (http): This will be called when the HTTP message ends.\n\n### \\[Function] parse-request (http callbacks data \u0026key (start 0) end)\n\nParses `data` as an HTTP request, sets values to `http` and invokes callbacks in `callbacks`.\n\nThis 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.\n\n### \\[Function] parse-response (http callbacks data \u0026key (start 0) end)\n\nParses `data` as an HTTP response, sets values to `http` and invokes callbacks in `callbacks`.\n\nTakes 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.\n\n### \\[Condition] eof\n\nWill be raised when the `data` ends in the middle of parsing.\n\n## Installation\n\n```common-lisp\n(ql:quickload :fast-http)\n```\n\n## Running tests\n\n```common-lisp\n(asdf:test-system :fast-http)\n```\n\n## Benchmark\n\n- Parsing an HTTP request header 100000 times.\n\nIn this benchmark, fast-http is **1.25 times faster** than [http-parser](https://github.com/joyent/http-parser), a C equivalent.\n\n| http-parser (C) | fast-http |\n| ---------------:| ---------:|\n|      0.108s     |   0.086s  |\n\n### Environment\n\n* Travis CI\n* SBCL 1.2.6\n\nYou can see the latest result at [Travis CI](https://travis-ci.org/fukamachi/fast-http).\n\n### fast-http (Common Lisp)\n\n```common-lisp\n(ql:quickload :fast-http-test)\n(fast-http-test.benchmark:run-ll-benchmark)\n```\n\n```\nEvaluation took:\n  0.086 seconds of real time\n  0.085897 seconds of total run time (0.084763 user, 0.001134 system)\n  100.00% CPU\n  257,140,751 processor cycles\n  0 bytes consed\n```\n\n### http-parser (C)\n\n\n```c\n#include \"http_parser.h\"\n#include \u003cstdio.h\u003e\n#include \u003cstdlib.h\u003e\n#include \u003cstring.h\u003e\n#include \u003cassert.h\u003e\n#include \u003ctime.h\u003e\n\nstatic http_parser *parser;\n\nstatic http_parser_settings settings_null =\n  {.on_message_begin = 0\n  ,.on_header_field = 0\n  ,.on_header_value = 0\n  ,.on_url = 0\n  ,.on_status = 0\n  ,.on_body = 0\n  ,.on_headers_complete = 0\n  ,.on_message_complete = 0\n  };\n\nint\nmain (void)\n{\n  const char *buf;\n  int i;\n  float start, end;\n  size_t parsed;\n\n  parser = malloc(sizeof(http_parser));\n\n  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\";\n\n  start = (float)clock()/CLOCKS_PER_SEC;\n  for (i = 0; i \u003c 100000; i++) {\n    http_parser_init(parser, HTTP_REQUEST);\n    parsed = http_parser_execute(parser, \u0026settings_null, buf, strlen(buf));\n  }\n  end = (float)clock()/CLOCKS_PER_SEC;\n\n  free(parser);\n  parser = NULL;\n\n  printf(\"Elapsed %f seconds.\\n\", (end - start));\n\n  return 0;\n}\n```\n\n```\n$ make http_parser.o\n$ gcc -Wall -Wextra -Werror -Wno-error=unused-but-set-variable -O3 http_parser.o mybench.c -o mybench\n$ mybench\nElapsed 0.108815 seconds.\n```\n\n## Author\n\n* Eitaro Fukamachi (e.arrows@gmail.com)\n\n## Copyright\n\nCopyright (c) 2014 Eitaro Fukamachi\n\n## License\n\nLicensed under the MIT License.\n","funding_links":[],"categories":["Interfaces to other package managers"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffukamachi%2Ffast-http","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffukamachi%2Ffast-http","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffukamachi%2Ffast-http/lists"}