Skip to content

Commit

Permalink
Merge pull request #2 from termermc/master
Browse files Browse the repository at this point in the history
Reformat for modern nimble project structure; include picohttpparser as submodule
  • Loading branch information
philip-wernersbach committed Apr 16, 2023
2 parents 5248bd3 + ffc8323 commit 2e1e6d9
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "vendor/picohttpparser"]
path = vendor/picohttpparser
url = https://github.com/h2o/picohttpparser.git
url = https://github.com/h2o/picohttpparser
26 changes: 6 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,22 @@
# nim-picohttpparser
Nim binding for [picohttpparser](https://github.com/h2o/picohttpparser).

##Setup
In order to use these bindings in your program, you must install the
[picohttpparser.h](https://github.com/h2o/picohttpparser/blob/master/picohttpparser.h)
header on your machine, and link your Nim program with
[picohttpparser.c](https://github.com/h2o/picohttpparser/blob/master/picohttpparser.c).
## Setup
Install `picohttpparser` with Nimble and you're ready to go.

The easiest way to do this is to:

1. Copy the [picohttpparser](https://github.com/h2o/picohttpparser) sources into your
project sources,
2. Use the [cincludes](http://nim-lang.org/docs/nimc.html) Nim compiler flag
to add the picohttpparser sources to the C compiler include search path,
3. And create a Nim file that uses the
[compile pragma](http://nim-lang.org/docs/manual.html#implementation-specific-pragmas-compile-pragma)
to compile
[picohttpparser.c](https://github.com/h2o/picohttpparser/blob/master/picohttpparser.c).

##Usage
To use the low level picohttpparser API binding, `import picohttpparser_api`
## Usage
To use the low level picohttpparser API binding, `import picohttpparser/api`
in your program, and use `parseRequest` or `tryParseRequest`.

Converter `proc`s `toStringTableRef` and `toHttpHeaders` are provided to
convert picohttpparser's native `seq[phr_header]` header type to types that
are often used in Nim libraries.

##Todo
## TODO
A high level picohttpparser API named `picohttpparser` would be nice. Patches
welcome!

##License
## License
This project is licensed under the same license as
[picohttpparser](https://github.com/h2o/picohttpparser), which is
dual-licensed under the Perl License or the MIT License.
2 changes: 2 additions & 0 deletions picohttpparser.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import picohttpparser/api
export api
12 changes: 9 additions & 3 deletions picohttpparser.nimble
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
[Package]
# Package

version = "0.10.0"
version = "0.11.0"
author = "Philip Wernersbach"
description = "Bindings for picohttpparser."
description = "Bindings for picohttpparser"
license = "MIT"

installDirs = @["vendor"]

# Dependencies

requires "nim >= 1.6.12"
64 changes: 34 additions & 30 deletions picohttpparser_api.nim → picohttpparser/api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
# IN THE SOFTWARE.
#

import strtabs
import httpcore
import std/[strtabs, httpcore, strutils, os]

#when defined(_MSC_VER):
# const
Expand All @@ -35,39 +34,45 @@ import httpcore
# contains name and value of a header (name == NULL if is a continuing line
# of a multiline header

const picoRoot = currentSourcePath().rsplit(DirSep, 1)[0].parentDir & "/vendor/picohttpparser"

{.passC: "-I" & currentSourcePath().rsplit(DirSep, 1)[0] & "/vendor/picohttpparser/include".}
{.compile: picoRoot & "/picohttpparser.c".}

type
PicoHttpParserError* = object of CatchableError
## Error raised when a picohttpparser error occurs

ssize_t* {.importc, header: "<sys/types.h>".} = BiggestInt

phr_header* {.importc: "struct phr_header", header: "picohttpparser.h".} = object
phr_header* {.importc: "struct phr_header", header: picoRoot & "/picohttpparser.h".} = object
name: cstring
name_len: csize
name_len: csize_t
value: cstring
value_len: csize

value_len: csize_t

# returns number of bytes consumed if successful, -2 if request is partial,
# -1 if failed

proc phr_parse_request*(buf: cstring; len: csize; `method`: ptr cstring;
method_len: ptr csize; path: ptr cstring;
path_len: ptr csize; minor_version: ptr cint;
headers: ptr phr_header; num_headers: ptr csize;
last_len: csize): cint {.importc, header: "picohttpparser.h".}
proc phr_parse_request*(buf: cstring; len: csize_t; `method`: ptr cstring;
method_len: ptr csize_t; path: ptr cstring;
path_len: ptr csize_t; minor_version: ptr cint;
headers: ptr phr_header; num_headers: ptr csize_t;
last_len: csize_t): cint {.importc, header: picoRoot & "/picohttpparser.h".}
## returns number of bytes consumed if successful, -2 if request is partial, -1 if failed
# ditto

proc phr_parse_response*(buf: cstring; len: csize; minor_version: ptr cint;
proc phr_parse_response*(buf: cstring; len: csize_t; minor_version: ptr cint;
status: ptr cint; msg: cstringArray;
msg_len: ptr csize; headers: ptr phr_header;
num_headers: ptr csize; last_len: csize): cint {.importc, header: "picohttpparser.h".}
msg_len: ptr csize_t; headers: ptr phr_header;
num_headers: ptr csize_t; last_len: csize_t): cint {.importc, header: picoRoot & "/picohttpparser.h".}
# ditto

proc phr_parse_headers*(buf: cstring; len: csize; headers: ptr phr_header;
num_headers: ptr csize; last_len: csize): cint {.importc, header: "picohttpparser.h".}
proc phr_parse_headers*(buf: cstring; len: csize_t; headers: ptr phr_header;
num_headers: ptr csize_t; last_len: csize_t): cint {.importc, header: picoRoot & "/picohttpparser.h".}
# should be zero-filled before start

type
phr_chunked_decoder* = tuple
bytes_left_in_chunk: csize # number of bytes left in current chunk
bytes_left_in_chunk: csize_t # number of bytes left in current chunk
consume_trailer: char # if trailing headers should be consumed
hex_count: char
state: char
Expand All @@ -84,22 +89,21 @@ type
#

proc phr_decode_chunked*(decoder: ptr phr_chunked_decoder; buf: cstring;
bufsz: ptr csize): ssize_t {.importc, header: "picohttpparser.h".}
bufsz: ptr csize_t): ssize_t {.importc, header: picoRoot & "/picohttpparser.h".}

proc tryParseRequest*(request: string, httpMethod: var string, path: var string, minor_version: var cint,
headers: var seq[phr_header]): cint =

var methodPointer: cstring
var methodLen: csize
var methodLen: csize_t
var pathPointer: cstring
var pathLen: csize
var pathLen: csize_t
var minorVersion: cint
var previousHeaderBufferLen: csize
var numberOfHeaders = csize(headers.len)
var previousHeaderBufferLen: csize_t
var numberOfHeaders = csize_t(headers.len)

let requestLen = request.len
let requestCstring = cstring(request)
let requestLenCsize = csize(requestLen)
let methodPointerAddr = addr(methodPointer)
let methodLenAddr = addr(methodLen)
let pathPointerAddr = addr(pathPointer)
Expand All @@ -113,7 +117,7 @@ proc tryParseRequest*(request: string, httpMethod: var string, path: var string,

let numberOfHeadersAddr = addr(numberOfHeaders)

#var result = phr_parse_request(cstring(request), csize(requestLen), addr(methodPointer), addr(methodLen), addr(pathPointer), addr(pathLen),
#var result = phr_parse_request(cstring(request), csize_t(requestLen), addr(methodPointer), addr(methodLen), addr(pathPointer), addr(pathLen),
# addr(minorVersion), addr(headers[0]), addr(numberOfHeaders), previousHeaderBufferLen)
{.emit: "`result` = phr_parse_request(`requestCstring`, `requestLen`, (const char **)`methodPointerAddr`, `methodLenAddr`, (const char **)`pathPointerAddr`, `pathLenAddr`, `minorVersionAddr`, `headersAddr`, `numberOfHeadersAddr`, `previousHeaderBufferLen`);" .}

Expand All @@ -138,13 +142,13 @@ proc parseRequest*(request: string, httpMethod: var string, path: var string, mi
if (result >= 0):
return
elif (result == -1):
raise newException(Exception, "picohttpparser: Parse error!")
raise newException(PicoHttpParserError, "picohttpparser: Parse error!")
elif (result == -2):
raise newException(Exception, "picohttpparser: Incomplete request!")
raise newException(PicoHttpParserError, "picohttpparser: Incomplete request!")
elif (result == -255):
raise newException(Exception, "picohttpparser: Request only partially consumed!")
raise newException(PicoHttpParserError, "picohttpparser: Request only partially consumed!")
else:
raise newException(Exception, "picohttpparser: Unknown error! (Error code: " & $result & ")")
raise newException(PicoHttpParserError, "picohttpparser: Unknown error! (Error code: " & $result & ")")

converter toStringTableRef*(x: seq[phr_header]): StringTableRef =
result = newStringTable(modeCaseSensitive)
Expand Down
13 changes: 13 additions & 0 deletions tests/t_simple.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import std/unittest

import ../picohttpparser/api

suite "Can parse simple request":
var httpMethod: string
var path: string
var minorVersion: cint
var headers: seq[phr_header]

parseRequest("GET /test HTTP/1.1\r\n\r\n", httpMethod, path, minorVersion, headers)

assert(path == "/test")
1 change: 1 addition & 0 deletions vendor/picohttpparser
Submodule picohttpparser added at 066d2b

0 comments on commit 2e1e6d9

Please sign in to comment.