netkit/http/parser

This module implements an incremental HTTP parser. This parser supports parsing both request messages and response messages.

This parser is incremental, meaning that the message can be parsed continuously regardless of whether the message is delivered at once or divided into multiple parts. This makes the parser particularly suitable for complex IO transfer environments.

Usage

Parse message header

For example:

import netkit/http/parser
import netkit/http/header
import netkit/buffer/circular

var parser = initHttpParser()
var buffer = initMarkableCircularBuffer()
var header = HttpHeader(kind: HttpHeaderKind.Request)
var finished = false

while not finished:
  put data to buffer ...
  finished = parser.parseHttpHeader(buffer, header)

Another example:

import netkit/http/parser
import netkit/http/header
import netkit/buffer/circular

var parser = initHttpParser()
var buffer = initMarkableCircularBuffer()
var header = HttpHeader(kind: HttpHeaderKind.Request)

# parse first
let messageRequestLine = "GET / HTTP/1.1\r\n"
buffer.add(messageRequestLine.cstring, messageRequestLine.len)
assert parser.parseHttpHeader(buffer, header) == false
buffer.del(messageRequestLine.len)

# parse second
let messageHeaderFields = "Host: www.iocrate.com\r\n\r\n"
buffer.add(messageHeaderFields.cstring, messageHeaderFields.len)
assert parser.parseHttpHeader(buffer, header) == true
buffer.del(messageHeaderFields.len)

assert header.reqMethod == HttpGet
assert header.url == "/"
assert header.version.orig == "HTTP/1.1"
assert header.fields["Host"][0] == "www.iocrate.com"

Parse chunk header

For example:

import netkit/http/parser
import netkit/http/chunk
import netkit/buffer/circular

var parser = initHttpParser()
var buffer = initMarkableCircularBuffer()
var header: ChunkHeader
var finished = false

while not finished:
  put data to buffer ...
  finished = parser.parseChunkHeader(buffer, header)

Another example:

import netkit/http/parser
import netkit/http/chunk
import netkit/buffer/circular

var parser = initHttpParser()
var buffer = initMarkableCircularBuffer()
var header: ChunkHeader

let s = "9; language=en; city=London\r\n"
buffer.add(s.cstring, s.len)
assert parser.parseChunkHeader(buffer, header) == true
buffer.del(s.len)

assert header.size == 9
assert header.extensions == "; language=en; city=London"

See chunk module and metadata module for more information about chunked encoding.

Parse chunk tail

For example:

import netkit/http/parser
import netkit/http/chunk
import netkit/buffer/circular

var parser = initHttpParser()
var buffer = initMarkableCircularBuffer()
var trailers: seq[string]
var finished = false

while not finished:
  put data to buffer ...
  finished = parser.parseChunkEnd(buffer, trailers)

Another example:

import netkit/http/parser
import netkit/http/chunk
import netkit/buffer/circular

var parser = initHttpParser()
var buffer = initMarkableCircularBuffer()
var trailers: seq[string]

let s = "\0\r\nExpires": "Wed, 21 Oct 2015 07:28:00 GMT\r\n\r\n"
buffer.add(s.cstring, s.len)
assert parser.parseChunkEnd(buffer, trailers) == true
buffer.del(s.len)

assert trailers[0] == "Expires": "Wed, 21 Oct 2015 07:28:00 GMT"

See chunk module and metadata module for more information about terminating chunk and trailers.

Types

HttpParser = object
  secondaryBuffer: string
  currentLineLen: Natural
  currentFieldName: string
  currentFieldCount: Natural
  state: HttpParseState
  startLineState: StartLineState
HTTP message parser.   Source Edit

Procs

proc initHttpParser(): HttpParser {...}{.raises: [], tags: [].}
Initialize a HttpParser.   Source Edit
proc clear(p: var HttpParser) {...}{.raises: [], tags: [].}

Reset this parser to clear all status.

Since HttpParser is an incremental, various state will be saved during the parsing process. This proc resets all states in order to start a new parsing process.

  Source Edit
proc parseHttpHeader(p: var HttpParser; buf: var MarkableCircularBuffer;
                    header: var HttpHeader): bool {...}{.
    raises: [HttpError, ValueError, Exception, KeyError], tags: [].}

Parses the header of a HTTP message. buf specifies a circular buffer, which stores the data to be parsed. header specifies the message header object that output when the parsing is complete. Returns true if the parsing is complete.

Depending on the value of the kind attribute of header, different resolutions are taken. When kind=Request, a message is parsed as a request. When kind=Response, a message is parsed as a request.

This process is performed incrementally, that is, the next parsing will continue from the previous position.

  Source Edit
proc parseChunkHeader(p: var HttpParser; buf: var MarkableCircularBuffer;
                     header: var ChunkHeader): bool {...}{.
    raises: [HttpError, ValueError], tags: [].}

Parse the size and extensions of a data chunk that encoded by Transfor-Encoding: chunked.

This process is performed incrementally, that is, the next parsing will continue from the previous position.

  Source Edit
proc parseChunkEnd(p: var HttpParser; buf: var MarkableCircularBuffer;
                  trailers: var seq[string]): bool {...}{.raises: [HttpError], tags: [].}

Parse the tail of a message that encoded by Transfor-Encoding: chunked.

This process is performed incrementally, that is, the next parsing will continue from the previous position.

  Source Edit