Add tinyhttp as a subdirectory and not a submodule for ease of switching from master to development

This commit is contained in:
Mike Brady
2021-07-06 19:41:24 +01:00
parent d4a625cf9c
commit e3131c5394
9 changed files with 821 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
Copyright 2012 Matthew Endsley
All rights reserved
Redistribution and use in source and binary forms, with or without
modification, are permitted providing that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
+28
View File
@@ -0,0 +1,28 @@
tinyhttp
========
Tiny (as in minimal) implementation of an HTTP response parser. The parser
itself is only dependent on:
* `<ctype.h> - tolower`
* `<string.h> - memcpy`
For more information please see my blog post at: <http://mendsley.github.com/2012/12/19/tinyhttp.html>
[![Build Status](https://api.travis-ci.org/mendsley/tinyhttp.png)](http://travis-ci.org/mendsley/tinyhttp)
Contact
-------
[@MatthewEndsley](https://twitter.com/#!/MatthewEndsley)
<https://github.com/mendsley/tinyhttp>
License
-------
Copyright 2012 Matthew Endsley
This project is governed by the BSD 2-clause license. For details see the file
titled LICENSE in the project root folder.
Compiling
---------
`gcc -c *.c && g++ -std=c++0x example.cpp *.o -o example`
`./example` will fetch the root of <http://nothings.org>
+79
View File
@@ -0,0 +1,79 @@
/*-
* Copyright 2012 Matthew Endsley
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
static const unsigned char http_chunk_state[] = {
/* * LF CR HEX */
0xC1, 0xC1, 0xC1, 1, /* s0: initial hex char */
0xC1, 0xC1, 2, 0x81, /* s1: additional hex chars, followed by CR */
0xC1, 0x83, 0xC1, 0xC1, /* s2: trailing LF */
0xC1, 0xC1, 4, 0xC1, /* s3: CR after chunk block */
0xC1, 0xC0, 0xC1, 0xC1, /* s4: LF after chunk block */
};
int http_parse_chunked(int* state, int *size, char ch)
{
int newstate, code = 0;
switch (ch) {
case '\n': code = 1; break;
case '\r': code = 2; break;
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 'A': case 'B': case 'C': case 'D':
case 'E': case 'F': code = 3; break;
}
newstate = http_chunk_state[*state * 4 + code];
*state = (newstate & 0xF);
switch (newstate) {
case 0xC0:
return *size != 0;
case 0xC1: /* error */
*size = -1;
return 0;
case 0x01: /* initial char */
*size = 0;
/* fallthrough */
case 0x81: /* size char */
if (ch >= 'a')
*size = *size * 16 + (ch - 'a' + 10);
else if (ch >= 'A')
*size = *size * 16 + (ch - 'A' + 10);
else
*size = *size * 16 + (ch - '0');
break;
case 0x83:
return *size == 0;
}
return 1;
}
+48
View File
@@ -0,0 +1,48 @@
/*-
* Copyright 2012 Matthew Endsley
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HTTP_CHUNK_H
#define HTTP_CHUNK_H
#if defined(__cplusplus)
extern "C" {
#endif
/**
* Parses the size out of a chunk-encoded HTTP response. Returns non-zero if it
* needs more data. Returns zero success or error. When error: size == -1 On
* success, size = size of following chunk data excluding trailing \r\n. User is
* expected to process or otherwise seek past chunk data up to the trailing
* \r\n. The state parameter is used for internal state and should be
* initialized to zero the first call.
*/
int http_parse_chunked(int* state, int *size, char ch);
#if defined(__cplusplus)
}
#endif
#endif
+190
View File
@@ -0,0 +1,190 @@
/*-
* Copyright 2012 Matthew Endsley
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
Compiling example:
$ g++ -o example example.cpp
*/
#include <string>
#include <vector>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include "http.h"
// directly embed the source here
extern "C" {
#include "http.c"
#include "header.c"
#include "chunk.c"
}
// return a socket connected to a hostname, or -1
int connectsocket(const char* host, int port)
{
addrinfo* result = NULL;
sockaddr_in addr = {0};
int s;
if (getaddrinfo(host, NULL, NULL, &result))
goto error;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
for (addrinfo* ai = result; ai != NULL; ai = ai->ai_next) {
if (ai->ai_family != AF_INET)
continue;
const sockaddr_in *ai_in = (const sockaddr_in*)ai->ai_addr;
addr.sin_addr = ai_in->sin_addr;
break;
}
freeaddrinfo(result);
if (addr.sin_addr.s_addr == INADDR_ANY)
goto error;
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == -1)
goto error;
if (connect(s, (const sockaddr*)&addr, sizeof(addr)))
goto error;
return s;
error:
if (s != -1)
close(s);
if (result)
freeaddrinfo(result);
return -1;
}
// Response data/funcs
struct HttpResponse {
std::vector<char> body;
int code;
};
static void* response_realloc(void* opaque, void* ptr, int size)
{
return realloc(ptr, size);
}
static void response_body(void* opaque, const char* data, int size)
{
HttpResponse* response = (HttpResponse*)opaque;
response->body.insert(response->body.end(), data, data + size);
}
static void response_header(void* opaque, const char* ckey, int nkey, const char* cvalue, int nvalue)
{ /* example doesn't care about headers */ }
static void response_code(void* opaque, int code)
{
HttpResponse* response = (HttpResponse*)opaque;
response->code = code;
}
static const http_funcs responseFuncs = {
response_realloc,
response_body,
response_header,
response_code,
};
int main()
{
int conn = connectsocket("nothings.org", 80);
if (conn < 0) {
fprintf(stderr, "Failed to connect socket\n");
return -1;
}
const char request[] = "GET / HTTP/1.0\r\nContent-Length: 0\r\n\r\n";
int len = send(conn, request, sizeof(request) - 1, 0);
if (len != sizeof(request) - 1) {
fprintf(stderr, "Failed to send request\n");
close(conn);
return -1;
}
HttpResponse response;
response.code = 0;
http_roundtripper rt;
http_init(&rt, responseFuncs, &response);
bool needmore = true;
char buffer[1024];
while (needmore) {
const char* data = buffer;
int ndata = recv(conn, buffer, sizeof(buffer), 0);
if (ndata <= 0) {
fprintf(stderr, "Error receiving data\n");
http_free(&rt);
close(conn);
return -1;
}
while (needmore && ndata) {
int read;
needmore = http_data(&rt, data, ndata, &read);
ndata -= read;
data += read;
}
}
if (http_iserror(&rt)) {
fprintf(stderr, "Error parsing data\n");
http_free(&rt);
close(conn);
return -1;
}
http_free(&rt);
close(conn);
printf("Response: %d\n", response.code);
if (!response.body.empty()) {
printf("%s\n", &response.body[0]);
}
return 0;
}
+72
View File
@@ -0,0 +1,72 @@
/*-
* Copyright 2012 Matthew Endsley
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "header.h"
static unsigned char http_header_state[] = {
/* * \t \n \r ' ' , : PAD */
0x80, 1, 0xC1, 0xC1, 1, 0x80, 0x80, 0xC1, /* state 0: HTTP version */
0x81, 2, 0xC1, 0xC1, 2, 1, 1, 0xC1, /* state 1: Response code */
0x82, 0x82, 4, 3, 0x82, 0x82, 0x82, 0xC1, /* state 2: Response reason */
0xC1, 0xC1, 4, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, /* state 3: HTTP version newline */
0x84, 0xC1, 0xC0, 5, 0xC1, 0xC1, 6, 0xC1, /* state 4: Start of header field */
0xC1, 0xC1, 0xC0, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, /* state 5: Last CR before end of header */
0x87, 6, 0xC1, 0xC1, 6, 0x87, 0x87, 0xC1, /* state 6: leading whitespace before header value */
0x87, 0x87, 0xC4, 10, 0x87, 0x88, 0x87, 0xC1, /* state 7: header field value */
0x87, 0x88, 6, 9, 0x88, 0x88, 0x87, 0xC1, /* state 8: Split value field value */
0xC1, 0xC1, 6, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, /* state 9: CR after split value field */
0xC1, 0xC1, 0xC4, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, /* state 10:CR after header value */
};
int http_parse_header_char(int* state, char ch)
{
int newstate, code = 0;
switch (ch) {
case '\t': code = 1; break;
case '\n': code = 2; break;
case '\r': code = 3; break;
case ' ': code = 4; break;
case ',': code = 5; break;
case ':': code = 6; break;
}
newstate = http_header_state[*state * 8 + code];
*state = (newstate & 0xF);
switch (newstate) {
case 0xC0: return http_header_status_done;
case 0xC1: return http_header_status_done;
case 0xC4: return http_header_status_store_keyvalue;
case 0x80: return http_header_status_version_character;
case 0x81: return http_header_status_code_character;
case 0x82: return http_header_status_status_character;
case 0x84: return http_header_status_key_character;
case 0x87: return http_header_status_value_character;
case 0x88: return http_header_status_value_character;
}
return http_header_status_continue;
}
+61
View File
@@ -0,0 +1,61 @@
/*-
* Copyright 2012 Matthew Endsley
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HTTP_HEADER_H
#define HTTP_HEADER_H
#if defined(__cplusplus)
extern "C" {
#endif
enum http_header_status
{
http_header_status_done,
http_header_status_continue,
http_header_status_version_character,
http_header_status_code_character,
http_header_status_status_character,
http_header_status_key_character,
http_header_status_value_character,
http_header_status_store_keyvalue
};
/**
* Parses a single character of an HTTP header stream. The state parameter is
* used as internal state and should be initialized to zero for the first call.
* Return value is a value from the http_header_status enuemeration specifying
* the semantics of the character. If an error is encountered,
* http_header_status_done will be returned with a non-zero state parameter. On
* success http_header_status_done is returned with the state parameter set to
* zero.
*/
int http_parse_header_char(int* state, char ch);
#if defined(__cplusplus)
}
#endif
#endif
+227
View File
@@ -0,0 +1,227 @@
/*-
* Copyright 2012 Matthew Endsley
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "http.h"
#include <ctype.h>
#include <string.h>
#include "header.h"
#include "chunk.h"
static void append_body(struct http_roundtripper* rt, const char* data, int ndata)
{
rt->funcs.body(rt->opaque, data, ndata);
}
static void grow_scratch(struct http_roundtripper* rt, int size)
{
if (rt->nscratch >= size)
return;
if (size < 64)
size = 64;
int nsize = (rt->nscratch * 3) / 2;
if (nsize < size)
nsize = size;
rt->scratch = (char*)rt->funcs.realloc_scratch(rt->opaque, rt->scratch, nsize);
rt->nscratch = nsize;
}
static int min(int a, int b)
{
return a > b ? b : a;
}
enum http_roundtripper_state {
http_roundtripper_header,
http_roundtripper_chunk_header,
http_roundtripper_chunk_data,
http_roundtripper_raw_data,
http_roundtripper_unknown_data,
http_roundtripper_close,
http_roundtripper_error,
};
void http_init(struct http_roundtripper* rt, struct http_funcs funcs, void* opaque)
{
rt->funcs = funcs;
rt->scratch = 0;
rt->opaque = opaque;
rt->code = 0;
rt->parsestate = 0;
rt->contentlength = -1;
rt->state = http_roundtripper_header;
rt->nscratch = 0;
rt->nkey = 0;
rt->nvalue = 0;
rt->chunked = 0;
}
void http_free(struct http_roundtripper* rt)
{
if (rt->scratch) {
rt->funcs.realloc_scratch(rt->opaque, rt->scratch, 0);
rt->scratch = 0;
}
}
int http_data(struct http_roundtripper* rt, const char* data, int size, int* read)
{
const int initial_size = size;
while (size) {
switch (rt->state) {
case http_roundtripper_header:
switch (http_parse_header_char(&rt->parsestate, *data)) {
case http_header_status_done:
rt->funcs.code(rt->opaque, rt->code);
if (rt->parsestate != 0)
rt->state = http_roundtripper_error;
else if (rt->chunked) {
rt->contentlength = 0;
rt->state = http_roundtripper_chunk_header;
} else if (rt->contentlength == 0)
rt->state = http_roundtripper_close;
else if (rt->contentlength > 0)
rt->state = http_roundtripper_raw_data;
else if (rt->contentlength == -1)
rt->state = http_roundtripper_unknown_data;
else
rt->state = http_roundtripper_error;
break;
case http_header_status_code_character:
rt->code = rt->code * 10 + *data - '0';
break;
case http_header_status_key_character:
grow_scratch(rt, rt->nkey + 1);
rt->scratch[rt->nkey] = tolower(*data);
++rt->nkey;
break;
case http_header_status_value_character:
grow_scratch(rt, rt->nkey + rt->nvalue + 1);
rt->scratch[rt->nkey+rt->nvalue] = *data;
++rt->nvalue;
break;
case http_header_status_store_keyvalue:
if (rt->nkey == 17 && 0 == strncmp(rt->scratch, "transfer-encoding", rt->nkey))
rt->chunked = (rt->nvalue == 7 && 0 == strncmp(rt->scratch + rt->nkey, "chunked", rt->nvalue));
else if (rt->nkey == 14 && 0 == strncmp(rt->scratch, "content-length", rt->nkey)) {
int ii, end;
rt->contentlength = 0;
for (ii = rt->nkey, end = rt->nkey + rt->nvalue; ii != end; ++ii)
rt->contentlength = rt->contentlength * 10 + rt->scratch[ii] - '0';
}
rt->funcs.header(rt->opaque, rt->scratch, rt->nkey, rt->scratch + rt->nkey, rt->nvalue);
rt->nkey = 0;
rt->nvalue = 0;
break;
}
--size;
++data;
break;
case http_roundtripper_chunk_header:
if (!http_parse_chunked(&rt->parsestate, &rt->contentlength, *data)) {
if (rt->contentlength == -1)
rt->state = http_roundtripper_error;
else if (rt->contentlength == 0)
rt->state = http_roundtripper_close;
else
rt->state = http_roundtripper_chunk_data;
}
--size;
++data;
break;
case http_roundtripper_chunk_data: {
const int chunksize = min(size, rt->contentlength);
append_body(rt, data, chunksize);
rt->contentlength -= chunksize;
size -= chunksize;
data += chunksize;
if (rt->contentlength == 0) {
rt->contentlength = 1;
rt->state = http_roundtripper_chunk_header;
}
}
break;
case http_roundtripper_raw_data: {
const int chunksize = min(size, rt->contentlength);
append_body(rt, data, chunksize);
rt->contentlength -= chunksize;
size -= chunksize;
data += chunksize;
if (rt->contentlength == 0)
rt->state = http_roundtripper_close;
}
break;
case http_roundtripper_unknown_data: {
if (size == 0)
rt->state = http_roundtripper_close;
else {
append_body(rt, data, size);
size -= size;
data += size;
}
}
break;
case http_roundtripper_close:
case http_roundtripper_error:
break;
}
if (rt->state == http_roundtripper_error || rt->state == http_roundtripper_close) {
if (rt->scratch) {
rt->funcs.realloc_scratch(rt->opaque, rt->scratch, 0);
rt->scratch = 0;
}
*read = initial_size - size;
return 0;
}
}
*read = initial_size - size;
return 1;
}
int http_iserror(struct http_roundtripper* rt)
{
return rt->state == http_roundtripper_error;
}
+93
View File
@@ -0,0 +1,93 @@
/*-
* Copyright 2012 Matthew Endsley
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HTTP_HTTP_H
#define HTTP_HTTP_H
#if defined(__cplusplus)
extern "C" {
#endif
/**
* Callbacks for handling response data.
* realloc_scratch - reallocate memory, cannot fail. There will only
* be one scratch buffer. Implemnentation may take
* advantage of this fact.
* body - handle HTTP response body data
* header - handle an HTTP header key/value pair
* code - handle the HTTP status code for the response
*/
struct http_funcs {
void* (*realloc_scratch)(void* opaque, void* ptr, int size);
void (*body)(void* opaque, const char* data, int size);
void (*header)(void* opaque, const char* key, int nkey, const char* value, int nvalue);
void (*code)(void* opqaue, int code);
};
struct http_roundtripper {
struct http_funcs funcs;
void *opaque;
char *scratch;
int code;
int parsestate;
int contentlength;
int state;
int nscratch;
int nkey;
int nvalue;
int chunked;
};
/**
* Initializes a rountripper with the specified response functions. This must
* be called before the rt object is used.
*/
void http_init(struct http_roundtripper* rt, struct http_funcs, void* opaque);
/**
* Frees any scratch memory allocated during parsing.
*/
void http_free(struct http_roundtripper* rt);
/**
* Parses a block of HTTP response data. Returns zero if the parser reached the
* end of the response, or an error was encountered. Use http_iserror to check
* for the presence of an error. Returns non-zero if more data is required for
* the response.
*/
int http_data(struct http_roundtripper* rt, const char* data, int size, int* read);
/**
* Returns non-zero if a completed parser encounted an error. If http_data did
* not return non-zero, the results of this function are undefined.
*/
int http_iserror(struct http_roundtripper* rt);
#if defined(__cplusplus)
}
#endif
#endif