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:
parent
e37ec7b765
commit
b189e291a9
|
@ -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
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue