diff options
author | Paul Duncan <pabs@pablotron.org> | 2016-08-26 18:51:11 -0400 |
---|---|---|
committer | Paul Duncan <pabs@pablotron.org> | 2016-08-26 18:51:11 -0400 |
commit | 8a3c72fdaabdc8c565a8c91361a2f8d38c892e1e (patch) | |
tree | 44d11e53a0345844b2d44319e5ad648454e249d4 /fhp.c | |
download | libfhp-8a3c72fdaabdc8c565a8c91361a2f8d38c892e1e.tar.bz2 libfhp-8a3c72fdaabdc8c565a8c91361a2f8d38c892e1e.zip |
initial commit
Diffstat (limited to 'fhp.c')
-rw-r--r-- | fhp.c | 591 |
1 files changed, 591 insertions, 0 deletions
@@ -0,0 +1,591 @@ +#include <string.h> +#include "fhp/fhp.h" + +#define UNUSED(a) ((void) (a)) + +// +// rfc7230, Appendix B +// https://tools.ietf.org/html/rfc7230 +// +// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +// token = 1*tchar +// +#define CASE_TOKEN_CHARS \ + case '!': \ + case '#': \ + case '$': \ + case '%': \ + case '&': \ + case '\'': \ + case '*': \ + case '+': \ + case '-': \ + case '.': \ + case '^': \ + case '_': \ + case '|': \ + case '~': \ + case '0': \ + case '1': \ + case '2': \ + case '3': \ + case '4': \ + case '5': \ + case '6': \ + case '7': \ + case '8': \ + case '9': \ + case 'a': \ + case 'b': \ + case 'c': \ + case 'd': \ + case 'e': \ + case 'f': \ + case 'g': \ + case 'h': \ + case 'i': \ + case 'j': \ + case 'k': \ + case 'l': \ + case 'm': \ + case 'n': \ + case 'o': \ + case 'p': \ + case 'q': \ + case 'r': \ + case 's': \ + case 't': \ + case 'u': \ + case 'v': \ + case 'w': \ + case 'x': \ + case 'y': \ + case 'z': \ + case 'A': \ + case 'B': \ + case 'C': \ + case 'D': \ + case 'E': \ + case 'F': \ + case 'G': \ + case 'H': \ + case 'I': \ + case 'J': \ + case 'K': \ + case 'L': \ + case 'M': \ + case 'N': \ + case 'O': \ + case 'P': \ + case 'Q': \ + case 'R': \ + case 'S': \ + case 'T': \ + case 'U': \ + case 'V': \ + case 'W': \ + case 'X': \ + case 'Y': \ + case 'Z': + +// +// rfc7230, Appendix B +// https://tools.ietf.org/html/rfc7230 +// +// OWS = *( SP / HTAB ) +// +#define CASE_OWS_CHARS \ + case ' ': \ + case '\t': + +static const char * +fhp_errors[] = { + "OK", + "callback error", + "bad state", + "invalid character", + "invalid error code", + "buffer too small", +}; + +fhp_err_t +fhp_strerror( + fhp_err_t err, + char * const buf, + size_t len +) { + // check error code + if (err >= FHP_ERR_LAST) + return FHP_ERR_INVALID_ERROR; + + // check buffer size + size_t err_len = strlen(fhp_errors[err]) + 1; + if (len < err_len) + return FHP_ERR_BUFFER_TOO_SMALL; + + // copy string + memcpy(buf, fhp_errors[err], err_len); + + // return success + return FHP_OK; +} + +static fhp_t DEFAULT_CONTEXT = { + .state = FHP_STATE_INIT, + .user_data = NULL, + .cb = NULL, + .err = FHP_OK, + .ofs = 0, + .buf_len = 0, +}; + +fhp_err_t +fhp_init( + fhp_t * const fhp, + fhp_cb_t cb, + void * const user_data +) { + *fhp = DEFAULT_CONTEXT; + fhp->user_data = user_data; + fhp->cb = cb; + + /* return success */ + return FHP_OK; +} + +static void +fhp_buf_clear(fhp_t * const fhp) { + // clear buffer + fhp->buf_len = 0; +} + +static bool +fhp_buf_flush( + fhp_t * const fhp, + fhp_token_t token +) { + if (fhp->buf_len > 0) { + // push data + if (!fhp->cb(fhp, token, fhp->buf, fhp->buf_len)) + return false; + + // clear buffer + fhp_buf_clear(fhp); + } + + // return success + return true; +} + +static bool +fhp_buf_push( + fhp_t * const fhp, + fhp_token_t token, + uint8_t byte +) { + // flush buffer + if (fhp->buf_len + 1 >= FHP_BUF_SIZE) { + if (!fhp_buf_flush(fhp, token)) + return false; + } + + // append to buffer + fhp->buf[fhp->buf_len] = byte; + fhp->buf_len++; + + // return success + return true; +} + +static fhp_err_t +fhp_push_byte( + fhp_t * const fhp, + uint8_t byte +) { +retry: + switch (fhp->state) { + case FHP_STATE_INIT: + switch (byte) { + CASE_TOKEN_CHARS + // send start token + if (!fhp->cb(fhp, FHP_TOKEN_METHOD_START, 0, 0)) + return FHP_ERR_CB; + + // set state + fhp->state = FHP_STATE_METHOD; + goto retry; + + break; + default: + // FIXME: invalid character + return FHP_ERR_INVALID_CHAR; + } + + break; + case FHP_STATE_METHOD: + switch (byte) { + CASE_TOKEN_CHARS + // add to buffer + if (!fhp_buf_push(fhp, FHP_TOKEN_METHOD_FRAGMENT, byte)) + return FHP_ERR_CB; + + break; + case ' ': + // flush buffer + if (!fhp_buf_flush(fhp, FHP_TOKEN_METHOD_FRAGMENT)) + return FHP_ERR_CB; + + // send end token + if (!fhp->cb(fhp, FHP_TOKEN_METHOD_END, 0, 0)) + return FHP_ERR_CB; + + // set state + fhp->state = FHP_STATE_METHOD_END; + goto retry; + + break; + default: + // FIXME: invalid character + return FHP_ERR_INVALID_CHAR; + } + + break; + case FHP_STATE_METHOD_END: + switch (byte) { + case ' ': + // FIXME: do we want to allow more than one whitespace? + // ignore + break; + default: + if (!fhp->cb(fhp, FHP_TOKEN_URL_START, 0, 0)) + return FHP_ERR_CB; + + fhp->state = FHP_STATE_URL; + goto retry; + } + + break; + case FHP_STATE_URL: + switch (byte) { + case ' ': + // flush buffer + if (!fhp_buf_flush(fhp, FHP_TOKEN_URL_FRAGMENT)) + return FHP_ERR_CB; + + // send end token + if (!fhp->cb(fhp, FHP_TOKEN_URL_END, 0, 0)) + return FHP_ERR_CB; + + // set state + fhp->state = FHP_STATE_URL_END; + goto retry; + + break; + default: + // add to buffer + if (!fhp_buf_push(fhp, FHP_TOKEN_URL_FRAGMENT, byte)) + return FHP_ERR_CB; + } + + break; + case FHP_STATE_URL_END: + switch (byte) { + case ' ': + // ignore + break; + default: + if (!fhp->cb(fhp, FHP_TOKEN_VERSION_START, 0, 0)) + return FHP_ERR_CB; + + fhp->state = FHP_STATE_VERSION; + goto retry; + } + + break; + case FHP_STATE_VERSION: + switch (byte) { + case '\r': + case '\n': + // flush buffer + if (!fhp_buf_flush(fhp, FHP_TOKEN_VERSION_FRAGMENT)) + return FHP_ERR_CB; + + // send end token + if (!fhp->cb(fhp, FHP_TOKEN_VERSION_END, 0, 0)) + return FHP_ERR_CB; + + // set state + fhp->state = FHP_STATE_VERSION_END; + goto retry; + + break; + case ' ': + // FIXME: invalid character + return FHP_ERR_INVALID_CHAR; + + break; + default: + // add to buffer + if (!fhp_buf_push(fhp, FHP_TOKEN_VERSION_FRAGMENT, byte)) + return FHP_ERR_CB; + } + + break; + case FHP_STATE_VERSION_END: + switch (byte) { + case '\r': + fhp->state = FHP_STATE_VERSION_END_CR; + + break; + case '\n': + fhp->state = FHP_STATE_STATUS_END; + + break; + default: + // FIXME: invalid character + return FHP_ERR_INVALID_CHAR; + }; + + break; + case FHP_STATE_VERSION_END_CR: + switch (byte) { + case '\n': + fhp->state = FHP_STATE_STATUS_END; + + break; + default: + // FIXME: invalid character + return FHP_ERR_INVALID_CHAR; + }; + + break; + case FHP_STATE_STATUS_END: + // TODO + switch (byte) { + CASE_TOKEN_CHARS + // send start token + if (!fhp->cb(fhp, FHP_TOKEN_HEADER_NAME_START, 0, 0)) + return FHP_ERR_CB; + + // set state + fhp->state = FHP_STATE_HEADER_NAME; + goto retry; + + break; + case '\r': + // set state + fhp->state = FHP_STATE_STATUS_END_CR; + + break; + case '\n': + // set state + fhp->state = FHP_STATE_HEADERS_END; + + break; + } + + break; + case FHP_STATE_STATUS_END_CR: + switch (byte) { + case '\n': + // set state + fhp->state = FHP_STATE_HEADERS_END; + + break; + default: + // FIXME: invalid character + return FHP_ERR_INVALID_CHAR; + } + + break; + case FHP_STATE_HEADER_NAME: + switch (byte) { + CASE_TOKEN_CHARS + // add to buffer + if (!fhp_buf_push(fhp, FHP_TOKEN_HEADER_NAME_FRAGMENT, byte)) + return FHP_ERR_CB; + + break; + case ':': + // flush buffer + if (!fhp_buf_flush(fhp, FHP_TOKEN_HEADER_NAME_FRAGMENT)) + return FHP_ERR_CB; + + // send end token + if (!fhp->cb(fhp, FHP_TOKEN_HEADER_NAME_END, 0, 0)) + return FHP_ERR_CB; + + // set state + fhp->state = FHP_STATE_HEADER_NAME_END; + + break; + default: + // FIXME: invalid character + return FHP_ERR_INVALID_CHAR; + } + + break; + case FHP_STATE_HEADER_NAME_END: + switch (byte) { + CASE_OWS_CHARS + // ignore leading spaces + + break; + default: + // send start token + if (!fhp->cb(fhp, FHP_TOKEN_HEADER_VALUE_START, 0, 0)) + return FHP_ERR_CB; + + // set state + fhp->state = FHP_STATE_HEADER_VALUE; + goto retry; + + break; + } + + break; + case FHP_STATE_HEADER_VALUE: + switch (byte) { + case '\r': + fhp->state = FHP_STATE_HEADER_VALUE_END_CR; + + break; + case '\n': + fhp->state = FHP_STATE_HEADER_VALUE_END; + break; + default: + // FIXME: need more limits on valid octets + + // add to buffer + if (!fhp_buf_push(fhp, FHP_TOKEN_HEADER_VALUE_FRAGMENT, byte)) + return FHP_ERR_CB; + + break; + } + + break; + case FHP_STATE_HEADER_VALUE_END_CR: + switch (byte) { + case '\n': + fhp->state = FHP_STATE_HEADER_VALUE_END; + break; + default: + return FHP_ERR_INVALID_CHAR; + } + + break; + case FHP_STATE_HEADER_VALUE_END: + switch (byte) { + CASE_OWS_CHARS + // ows-fold + + // add to buffer + if (!fhp_buf_push(fhp, FHP_TOKEN_HEADER_VALUE_FRAGMENT, ' ')) + return FHP_ERR_CB; + + fhp->state = FHP_STATE_HEADER_VALUE; + break; + CASE_TOKEN_CHARS + // flush buffer + if (!fhp_buf_flush(fhp, FHP_TOKEN_HEADER_VALUE_FRAGMENT)) + return FHP_ERR_CB; + + // end header value + if (!fhp->cb(fhp, FHP_TOKEN_HEADER_VALUE_END, 0, 0)) + return FHP_ERR_CB; + + // send start token + if (!fhp->cb(fhp, FHP_TOKEN_HEADER_NAME_START, 0, 0)) + return FHP_ERR_CB; + + // add to buffer + if (!fhp_buf_push(fhp, FHP_TOKEN_HEADER_NAME_FRAGMENT, byte)) + return FHP_ERR_CB; + + break; + case '\r': + // flush buffer + if (!fhp_buf_flush(fhp, FHP_TOKEN_HEADER_VALUE_FRAGMENT)) + return FHP_ERR_CB; + + // end header value + if (!fhp->cb(fhp, FHP_TOKEN_HEADER_VALUE_END, 0, 0)) + return FHP_ERR_CB; + + // set state + fhp->state = FHP_STATE_HEADERS_END_CR; + + break; + case '\n': + // flush buffer + if (!fhp_buf_flush(fhp, FHP_TOKEN_HEADER_VALUE_FRAGMENT)) + return FHP_ERR_CB; + + // end header value + if (!fhp->cb(fhp, FHP_TOKEN_HEADER_VALUE_END, 0, 0)) + return FHP_ERR_CB; + + // set state + fhp->state = FHP_STATE_HEADERS_END; + + break; + default: + return FHP_ERR_INVALID_CHAR; + } + + break; + case FHP_STATE_HEADERS_END_CR: + switch (byte) { + case '\n': + fhp->state = FHP_STATE_HEADERS_END; + + break; + default: + return FHP_ERR_INVALID_CHAR; + } + + break; + case FHP_STATE_HEADERS_END: + // TODO + break; + default: + // FIXME: invalid state + return FHP_ERR_BAD_STATE; + } + + // increment byte offset + fhp->ofs++; + + /* return success */ + return FHP_OK; +} + +fhp_err_t +fhp_push( + fhp_t * const fhp, + uint8_t * const buf, + size_t len +) { + switch (fhp->state) { + case FHP_STATE_ERROR: + return fhp->err; + + break; + default: + for (size_t i = 0; i < len; i++) { + // push byte + fhp_err_t err = fhp_push_byte(fhp, buf[i]); + + // check result + if (err != FHP_OK) { + fhp->state = FHP_STATE_ERROR; + fhp->err = err; + return err; + } + } + } + + // return success + return FHP_OK; +} |