diff options
| author | Přemysl Janouch <p.janouch@gmail.com> | 2014-09-21 00:58:19 +0200 | 
|---|---|---|
| committer | Přemysl Janouch <p.janouch@gmail.com> | 2014-09-21 01:02:16 +0200 | 
| commit | 47aaedd26a5ebabb3f009ca0c24c130c46558352 (patch) | |
| tree | 88cfb53cd79b1538759c81c9e6e83207de1f6c1b | |
| parent | 2b74a188332f152e8952725812e086968a41e925 (diff) | |
| download | ponymap-47aaedd26a5ebabb3f009ca0c24c130c46558352.tar.gz ponymap-47aaedd26a5ebabb3f009ca0c24c130c46558352.tar.xz ponymap-47aaedd26a5ebabb3f009ca0c24c130c46558352.zip | |
Implement the HTTP plugin
Ended up including Joyent's http-parser library.
| -rw-r--r-- | .gitmodules | 3 | ||||
| -rw-r--r-- | Makefile | 7 | ||||
| -rw-r--r-- | README | 2 | ||||
| m--------- | http-parser | 0 | ||||
| -rw-r--r-- | plugin-api.h | 4 | ||||
| -rw-r--r-- | plugins/http.c | 104 | 
6 files changed, 112 insertions, 8 deletions
| diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d56ec12 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "http-parser"] +	path = http-parser +	url = git://github.com/joyent/http-parser.git @@ -5,6 +5,7 @@ CFLAGS = -std=c99 -Wall -Wextra -Wno-unused-function -ggdb  # -lpthread is only there for debugging (gdb & errno)  # -lrt is only for glibc < 2.17  LDFLAGS = `pkg-config --libs libssl jansson` -lpthread -lrt -ldl -lcurses +LDFLAGS_PLUGIN = $(LDFLAGS) -shared -fPIC  .PHONY: all clean  .SUFFIXES: @@ -23,4 +24,8 @@ ponymap.1: ponymap  	help2man -No $@ ./$<  plugins/%.so: plugins/%.c utils.c plugin-api.h -	$(CC) $< -o $@ $(CFLAGS) $(LDFLAGS) -shared -fPIC +	$(CC) $< -o $@ $(CFLAGS) $(LDFLAGS_PLUGIN) + +plugins/http.so: plugins/http.c utils.c plugin-api.h \ +	http-parser/http_parser.c http-parser/http_parser.h +	$(CC) $< http-parser/http_parser.c -o $@ $(CFLAGS) $(LDFLAGS_PLUGIN) @@ -18,6 +18,8 @@ If you don't have Clang, you can edit the Makefile to use GCC or TCC, they work  just as good.  But there's no CMake support yet, so I force it in the Makefile.   $ git clone https://github.com/pjanouch/ponymap.git + $ git submodule init + $ git submodule update   $ make  That is all, no installation is required, or supported for that matter. diff --git a/http-parser b/http-parser new file mode 160000 +Subproject 0b433671316ef7e6b73cae4ac23b8149fa0b9b2 diff --git a/plugin-api.h b/plugin-api.h index 3406308..7a73c59 100644 --- a/plugin-api.h +++ b/plugin-api.h @@ -50,11 +50,11 @@ struct service  	// FIXME: the dependency on `struct str' is not very good  	void (*on_data) (void *handle, struct unit *u, struct str *data); -	// XXX: do we need these at all?  Is there any use for them? -  	/// Server has closed the connection  	void (*on_eof) (void *handle, struct unit *u); +	// XXX: do we need these at all?  Is there any use for them? +  	/// Network or other error has occured  	void (*on_error) (void *handle, struct unit *u); diff --git a/plugins/http.c b/plugins/http.c index b350fee..ff48b5f 100644 --- a/plugins/http.c +++ b/plugins/http.c @@ -20,6 +20,7 @@  #include "../utils.c"  #include "../plugin-api.h" +#include "../http-parser/http_parser.h"  // --- Service detection ------------------------------------------------------- @@ -30,11 +31,70 @@ static struct plugin_data  }  g_data; +enum header_state +{ +	STATE_FIELD,                        ///< We've been parsing a field so far +	STATE_VALUE                         ///< We've been parsing a value so far +}; +  struct scan_data  { -	struct str input;                   ///< Input buffer +	struct unit *u;                     ///< Scan unit + +	http_parser parser;                 ///< HTTP parser +	http_parser_settings settings;      ///< HTTP parser settings + +	enum header_state state;            ///< What did we get last time? +	struct str field;                   ///< Field part buffer +	struct str value;                   ///< Value part buffer  }; +static void +on_header_read (struct scan_data *data) +{ +	if (!strcasecmp (data->field.str, "Server")) +	{ +		char *info = xstrdup_printf ("%s: %s", +			"server software", data->value.str); +		g_data.api->unit_add_info (data->u, info); +		free (info); +	} +} + +static int +on_header_field (http_parser *parser, const char *at, size_t len) +{ +	struct scan_data *data = parser->data; +	if (data->state == STATE_VALUE) +	{ +		on_header_read (data); +		str_reset (&data->field); +		str_reset (&data->value); +	} +	str_append_data (&data->field, at, len); +	data->state = STATE_FIELD; +	return 0; +} + +static int +on_header_value (http_parser *parser, const char *at, size_t len) +{ +	struct scan_data *data = parser->data; +	str_append_data (&data->value, at, len); +	data->state = STATE_VALUE; +	return 0; +} + +static int +on_headers_complete (http_parser *parser) +{ +	struct scan_data *data = parser->data; +	// We've got this far, this must be an HTTP server +	g_data.api->unit_set_success (data->u, true); +	g_data.api->unit_abort (data->u); +	return 1; +} +  static void *  scan_init (struct unit *u)  { @@ -46,7 +106,19 @@ scan_init (struct unit *u)  	str_free (&hello);  	struct scan_data *scan = xcalloc (1, sizeof *scan); -	str_init (&scan->input); +	http_parser_init (&scan->parser, HTTP_RESPONSE); +	scan->parser.data = scan; + +	http_parser_settings *s = &scan->settings; +	s->on_header_field      = on_header_field; +	s->on_header_value      = on_header_value; +	s->on_headers_complete  = on_headers_complete; + +	scan->state = STATE_FIELD; +	str_init (&scan->field); +	str_init (&scan->value); + +	scan->u = u;  	return scan;  } @@ -54,14 +126,36 @@ static void  scan_free (void *handle)  {  	struct scan_data *scan = handle; -	str_free (&scan->input); +	str_free (&scan->field); +	str_free (&scan->value);  	free (scan);  }  static void  on_data (void *handle, struct unit *u, struct str *data)  { -	// TODO: implement a state machine to parse the headers +	struct scan_data *scan = handle; +	http_parser *parser = &scan->parser; + +	size_t len = data ? data->len : 0; +	const char *str = data ? data->str : NULL; +	size_t n_parsed = http_parser_execute (parser, &scan->settings, str, len); + +	if (parser->upgrade) +	{ +		// We should never get here though because `on_headers_complete' +		// is called first and ends up aborting the unit. +		g_data.api->unit_add_info (u, "upgrades to a different protocol"); +		g_data.api->unit_abort (u); +	} +	else if (n_parsed != len && parser->http_errno != HPE_CB_headers_complete) +		g_data.api->unit_abort (u); +} + +static void +on_eof (void *handle, struct unit *u) +{ +	on_data (handle, u, NULL);  }  static struct service g_http_service = @@ -72,7 +166,7 @@ static struct service g_http_service =  	.scan_init   = scan_init,  	.scan_free   = scan_free,  	.on_data     = on_data, -	.on_eof      = NULL, +	.on_eof      = on_eof,  	.on_error    = NULL,  	.on_aborted  = NULL  }; | 
