Added htparse library

htparse is written by Mark Ellzey and part of libevhtp.
https://github.com/ellzey/libevhtp

The included code are modified by me for bugfixes.
See my fork:
https://github.com/tatsuhiro-t/libevhtp/tree/master/htparse
This commit is contained in:
Tatsuhiro Tsujikawa 2012-06-07 01:25:43 +09:00
parent e37ec7b765
commit b189e291a9
5 changed files with 2071 additions and 0 deletions

34
examples/htparse/LICENSE Normal file
View File

@ -0,0 +1,34 @@
Libevhtp is available for use under the following license, commonly known
as the 3-clause (or "modified") BSD license:
==============================
Copyright (c) 2010-2011 Mark Ellzey
Redistribution and use in source and binary forms, with or without
modification, are permitted provided 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.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
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.
==============================
Portions of Libevhtp are based on works by others, also made available by them
under the three-clause BSD license above. The functions include:
evhtp.c: _evhtp_glob_match():
Copyright (c) 2006-2009, Salvatore Sanfilippo

23
examples/htparse/Makefile Normal file
View File

@ -0,0 +1,23 @@
SRC = htparse.c
OUT = libhtparse.a
OBJ = $(SRC:.c=.o)
INCLUDES = -I.
CFLAGS += -ggdb -Wall -Wextra
LDFLAGS +=
CC = gcc
.SUFFIXES: .c
default: $(OUT)
.c.o:
$(CC) $(INCLUDES) $(CFLAGS) -c $< -o $@
$(OUT): $(OBJ)
ar rcs $(OUT) $(OBJ)
test: $(OUT) test.c
$(CC) $(INCLUDES) $(CFLAGS) test.c -o test $(OUT)
clean:
rm -f $(OBJ) $(OUT) test

1656
examples/htparse/htparse.c Normal file

File diff suppressed because it is too large Load Diff

108
examples/htparse/htparse.h Normal file
View File

@ -0,0 +1,108 @@
#ifndef __HTPARSE_H__
#define __HTPARSE_H__
struct htparser;
enum htp_type {
htp_type_request = 0,
htp_type_response
};
enum htp_scheme {
htp_scheme_none = 0,
htp_scheme_ftp,
htp_scheme_http,
htp_scheme_https,
htp_scheme_nfs,
htp_scheme_unknown
};
enum htp_method {
htp_method_GET = 0,
htp_method_HEAD,
htp_method_POST,
htp_method_PUT,
htp_method_DELETE,
htp_method_MKCOL,
htp_method_COPY,
htp_method_MOVE,
htp_method_OPTIONS,
htp_method_PROPFIND,
htp_method_PROPPATCH,
htp_method_LOCK,
htp_method_UNLOCK,
htp_method_TRACE,
htp_method_UNKNOWN
};
enum htpparse_error {
htparse_error_none = 0,
htparse_error_too_big,
htparse_error_inval_method,
htparse_error_inval_reqline,
htparse_error_inval_schema,
htparse_error_inval_proto,
htparse_error_inval_ver,
htparse_error_inval_hdr,
htparse_error_inval_chunk_sz,
htparse_error_inval_chunk,
htparse_error_inval_state,
htparse_error_user,
htparse_error_status,
htparse_error_generic
};
typedef struct htparser htparser;
typedef struct htparse_hooks htparse_hooks;
typedef enum htp_scheme htp_scheme;
typedef enum htp_method htp_method;
typedef enum htp_type htp_type;
typedef enum htpparse_error htpparse_error;
typedef int (*htparse_hook)(htparser *);
typedef int (*htparse_data_hook)(htparser *, const char *, size_t);
struct htparse_hooks {
htparse_hook on_msg_begin;
htparse_data_hook method;
htparse_data_hook scheme; /* called if scheme is found */
htparse_data_hook host; /* called if a host was in the request scheme */
htparse_data_hook port; /* called if a port was in the request scheme */
htparse_data_hook path; /* only the path of the uri */
htparse_data_hook args; /* only the arguments of the uri */
htparse_data_hook uri; /* the entire uri including path/args */
htparse_hook on_hdrs_begin;
htparse_data_hook hdr_key;
htparse_data_hook hdr_val;
htparse_hook on_hdrs_complete;
htparse_hook on_new_chunk; /* called after parsed chunk octet */
htparse_hook on_chunk_complete; /* called after single parsed chunk */
htparse_hook on_chunks_complete; /* called after all parsed chunks processed */
htparse_data_hook body;
htparse_hook on_msg_complete;
};
size_t htparser_run(htparser *, htparse_hooks *, const char *, size_t);
int htparser_should_keep_alive(htparser * p);
htp_scheme htparser_get_scheme(htparser *);
htp_method htparser_get_method(htparser *);
const char * htparser_get_methodstr(htparser *);
void htparser_set_major(htparser *, unsigned char);
void htparser_set_minor(htparser *, unsigned char);
unsigned char htparser_get_major(htparser *);
unsigned char htparser_get_minor(htparser *);
unsigned int htparser_get_status(htparser *);
uint64_t htparser_get_content_length(htparser *);
uint64_t htparser_get_total_bytes_read(htparser *);
htpparse_error htparser_get_error(htparser *);
const char * htparser_get_strerror(htparser *);
void * htparser_get_userdata(htparser *);
void htparser_set_userdata(htparser *, void *);
void htparser_init(htparser *, htp_type);
htparser * htparser_new(void);
#endif

250
examples/htparse/test.c Normal file
View File

@ -0,0 +1,250 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include "htparse.h"
static int
_on_msg_start(htparser * p) {
printf("START {\n");
return 0;
}
static int
_on_msg_end(htparser * p) {
printf("}\n");
return 0;
}
static int
_path(htparser * p, const char * data, size_t len) {
printf("\tpath = '%.*s'\n", (int)len, data);
return 0;
}
static int
_method(htparser * p, const char * data, size_t len) {
printf("\tmethod = '%.*s'\n", (int)len, data);
return 0;
}
static int
_uri(htparser * p, const char * data, size_t len) {
printf("\turi = '%.*s'\n", (int)len, data);
return 0;
}
static int
_args(htparser * p, const char * data, size_t len) {
printf("\targs = '%.*s'\n", (int)len, data);
return 0;
}
static int
_hdrs_end(htparser * p) {
printf("\t}\n");
return 0;
}
static int
_hdrs_start(htparser * p) {
printf("\thdrs {\n");
return 0;
}
static int
_hdr_key(htparser * p, const char * data, size_t len) {
printf("\t\thdr_key = '%.*s'\n", (int)len, data);
return 0;
}
static int
_hdr_val(htparser * p, const char * data, size_t len) {
printf("\t\thdr_val = '%.*s'\n", (int)len, data);
return 0;
}
static int
_read_body(htparser * p, const char * data, size_t len) {
printf("\t'%.*s'\n", (int)len, data);
return 0;
}
static int
_on_new_chunk(htparser * p) {
printf("\t--chunk payload (%zu)--\n", htparser_get_content_length(p));
/* printf("..chunk..\n"); */
return 0;
}
static void
_test(htparser * p, htparse_hooks * hooks, const char * l, htp_type type) {
printf("---- test ----\n");
printf("%zu, %s\n", strlen(l), l);
htparser_init(p, type);
printf("%zu == %zu\n", htparser_run(p, hooks, l, strlen(l)), strlen(l));
if (htparser_get_error(p)) {
printf("ERROR: %s\n", htparser_get_strerror(p));
}
printf("\n");
}
static void
_test_fragments(htparser * p, htparse_hooks * hooks, const char ** fragments,
htp_type type) {
int i = 0;
printf("---- test fragment ----\n");
htparser_init(p, type);
while (1) {
const char * l = fragments[i++];
if (l == NULL) {
break;
}
htparser_run(p, hooks, l, strlen(l));
if (htparser_get_error(p)) {
printf("ERROR: %s\n", htparser_get_strerror(p));
}
}
printf("\n");
}
static const char * test_fragment_1[] = {
"GET \0",
" /fjdksf\0",
"jfkdslfds H\0",
"TTP/1.\0",
"1\r\0",
"\n\0",
"\r\0",
"\n\0",
NULL
};
static const char * test_fragment_2[] = {
"POST /\0",
"h?a=b HTTP/1.0\r\n\0",
"Content-Len\0",
"gth\0",
": 1\0",
"0\r\n\0",
"\r\n\0",
"12345\0",
"67890\0",
NULL
};
static const char * test_chunk_fragment_1[] = {
"POST /stupid HTTP/1.1\r\n",
"Transfer-Encoding: chunked\r\n",
"\r\n",
"25\r\n",
"This is the data in the first chunk\r\n",
"\r\n",
"1C\r\n",
"and this is the second one\r\n",
"\r\n",
"3\r\n",
"con\r\n",
"8\r\n",
"sequence\r\n",
"0\r\n",
"\r\n",
NULL
};
static const char * test_chunk_fragment_2[] = {
"POST /stupid HTTP/1.1\r\n",
"Transfer-Encoding: chunked\r\n",
"\r\n",
"25\r\n",
"This is the data in the first chunk\r\n",
"\r\n",
"1C\r\n",
"and this is the second one\r\n",
"\r\n",
"3\r\n",
"c",
"on\r\n",
"8\r\n",
"sequence\r\n",
"0\r\n",
"\r\n",
"GET /foo?bar/baz? HTTP/1.0\r\n",
"Host: stupid.com\r\n",
"\r\n",
NULL
};
int
main(int argc, char ** argv) {
htparser * p = htparser_new();
htparse_hooks hooks = {
.on_msg_begin = _on_msg_start,
.method = _method,
.scheme = NULL,
.host = NULL,
.port = NULL,
.path = _path,
.args = _args,
.uri = _uri,
.on_hdrs_begin = _hdrs_start,
.hdr_key = _hdr_key,
.hdr_val = _hdr_val,
.on_hdrs_complete = _hdrs_end,
.on_new_chunk = _on_new_chunk,
.on_chunk_complete = NULL,
.on_chunks_complete = NULL,
.body = _read_body,
.on_msg_complete = _on_msg_end
};
const char * test_1 = "GET / HTTP/1.0\r\n\r\n";
const char * test_2 = "GET /hi?a=b&c=d HTTP/1.1\r\n\r\n";
const char * test_3 = "GET /hi/die/?a=b&c=d HTTP/1.1\r\n\r\n";
const char * test_4 = "POST /fjdls HTTP/1.0\r\n"
"Content-Length: 4\r\n"
"\r\n"
"abcd";
const char * test_7 = "POST /derp HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n\r\n"
"1e\r\nall your base are belong to us\r\n"
"0\r\n"
"\r\n\0";
const char * test_8 = "GET /DIE HTTP/1.1\r\n"
"HERP: DE\r\n"
"\tRP\r\nthings:stuff\r\n\r\n";
const char * test_9 = "GET /big_content_len HTTP/1.1\r\n"
"Content-Length: 18446744073709551615\r\n\r\n";
const char * test_fail = "GET /JF HfD]\r\n\r\n";
const char * test_resp_1 = "HTTP/1.0 200 OK\r\n"
"Stuff: junk\r\n\r\n";
_test(p, &hooks, test_resp_1, htp_type_response);
_test(p, &hooks, test_1, htp_type_request);
_test(p, &hooks, test_2, htp_type_request);
_test(p, &hooks, test_3, htp_type_request);
_test(p, &hooks, test_4, htp_type_request);
_test(p, &hooks, test_7, htp_type_request);
_test(p, &hooks, test_8, htp_type_request);
_test(p, &hooks, test_9, htp_type_request);
_test(p, &hooks, test_fail, htp_type_request);
_test_fragments(p, &hooks, test_fragment_1, htp_type_request);
_test_fragments(p, &hooks, test_fragment_2, htp_type_request);
_test_fragments(p, &hooks, test_chunk_fragment_1, htp_type_request);
_test_fragments(p, &hooks, test_chunk_fragment_2, htp_type_request);
return 0;
} /* main */