├── doc ├── icon.png └── index.html ├── .gitignore ├── examples ├── ca-cert.pem ├── ca-key.pem └── hello_world.c ├── README ├── rbtree.h ├── ebb_request_parser.h ├── test_rbtree.c ├── Makefile ├── ebb.h ├── rbtree.c ├── ebb_request_parser.rl ├── ebb.c └── test_request_parser.c /doc/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taf2/libebb/HEAD/doc/icon.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | examples/hello_world 3 | test_request_parser 4 | ebb_request_parser.c 5 | tags 6 | -------------------------------------------------------------------------------- /examples/ca-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIBzDCCATegAwIBAgIESIuNVTALBgkqhkiG9w0BAQUwADAeFw0wODA3MjYyMDQ3 3 | MTlaFw0xMTA0MjIyMDQ3MjVaMAAwgZwwCwYJKoZIhvcNAQEBA4GMADCBiAKBgOm9 4 | l/FoXbTIcEusk/QlS5YrlR04+oWIbSdZIf3GJBEWEUPljDxAX96qHsTcaVnGK+EP 5 | keU4cIZvdY+hzbqa5cc1j2/9IeJNejL8gpQ/ocyMM69yq5Ib2F8K4mGWm1xr30hU 6 | bYpY5D0MrZ1b0HtYFVc8KVAr0ADGG+pye0P9c3B/AgMBAAGjWjBYMAwGA1UdEwEB 7 | /wQCMAAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MBMGA1UdJQQMMAoGCCsGAQUFBwMB 8 | MB0GA1UdDgQWBBTYgVB7kJnnm+jgX9DgrapzGfUmxjALBgkqhkiG9w0BAQUDgYEA 9 | GkadA2H8CAzU3w4oCGZu9Ry9Tj/9Agw1XMFKvoJuG7VLPk7+B25JvNFVsmpROLxO 10 | 0TJ6mIU2hz5/rLvEfTBGQ+DYtbsjIxCz1fD7R5c1kKBtA0d0u8mY8pTlPNlxFPSW 11 | 3ymx5DB2zyDa/HuX6m6/VmzMYmA0vp7Dp1cl+pA9Nhs= 12 | -----END CERTIFICATE----- 13 | -------------------------------------------------------------------------------- /examples/ca-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDpvZfxaF20yHBLrJP0JUuWK5UdOPqFiG0nWSH9xiQRFhFD5Yw8 3 | QF/eqh7E3GlZxivhD5HlOHCGb3WPoc26muXHNY9v/SHiTXoy/IKUP6HMjDOvcquS 4 | G9hfCuJhlptca99IVG2KWOQ9DK2dW9B7WBVXPClQK9AAxhvqcntD/XNwfwIDAQAB 5 | AoGAJqo3LTbfcV1KvinhG5zjwQaalwfq4RXtQHoNFmalZrIozvt01C6t7S5lApmX 6 | T8NpVMR3lNxeOM7NOqJAXuLqqVVqk81YEYuMx6E4gB/Ifl7jVZk1jstmLILhh59D 7 | pXrlpzvvm5X2hVsI7lp/YGAvtdLS1iVy37bGgmQWfCeeZiECQQDtZLfcJb4oE1yR 8 | ZfLOcPDlBCw02wGMNFpAjwbspf/du3Yn3ONWHVfhSCCcCe262h9PLblL97LoB+gF 9 | OHOlM9JvAkEA/A+U3/p9pwL4L742pxZP62/rmk6p5mZFIykk2jIUTpdilXBWBlcT 10 | OjgjnpquZpwnaClmvgFpkzdhIPF7Nq4K8QJBAITVKagOmnOUOeTF1fI78h9DkXTV 11 | 4uzP0nxzS52ZWS16Gqg9ihuCecz97flB+Prn2EMWw6tFY58/5U0ehF85OxMCQQDF 12 | 08TYdVSg+6emcPeb89sNwW18UjjuZ13j1qrhxWRCunXZK62YlEa27tCl7mjqh6w2 13 | CChm/9zIejJ1FJHLvJVBAkBj63ZbwggMYkxuj60jIBbNrEtDx9y7zM0sXkiJqcKp 14 | 5uGtJNafG+yZrLAHE6/b4aqUOtGsCGsiZpT9ms7CoaVr 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | see doc/index.html and examples/hello_world.c for explanation 2 | 3 | webpage: http://tinyclouds.org/libebb/ 4 | git repository: http://github.com/ry/libebb/tree/master 5 | 6 | (The MIT) LICENSE 7 | Copyright © 2008 Ryah Dahl (ry@tinyclouds.org) 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining 10 | a copy of this software and associated documentation files (the 11 | "Software"), to deal in the Software without restriction, including 12 | without limitation the rights to use, copy, modify, merge, publish, 13 | distribute, sublicense, and/or sell copies of the Software, and to 14 | permit persons to whom the Software is furnished to do so, subject to 15 | the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be 18 | included in all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /rbtree.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2008 Derrick Coetzee 2 | * http://en.literateprograms.org/Red-black_tree_(C)?oldid=7982 3 | * Small changes by Ryah Dahl 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a 6 | * copy of this software and associated documentation files (the 7 | * "Software"), to deal in the Software without restriction, including 8 | * without limitation the rights to use, copy, modify, merge, publish, 9 | * distribute, sublicense, and/or sell copies of the Software, and to permit 10 | * persons to whom the Software is furnished to do so, subject to the 11 | * following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included 14 | * in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 21 | * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 22 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef _RBTREE_H_ 26 | #define _RBTREE_H_ 27 | 28 | enum rbtree_node_color { RED, BLACK }; 29 | 30 | typedef int (*rbtree_compare_func)(void* left_key, void* right_key); 31 | 32 | typedef struct rbtree_node_t { 33 | struct rbtree_node_t* left; /* private */ 34 | struct rbtree_node_t* right; /* private */ 35 | struct rbtree_node_t* parent; /* private */ 36 | enum rbtree_node_color color; /* private */ 37 | void* key; /* public */ 38 | void* value; /* public */ 39 | } *rbtree_node; 40 | 41 | typedef struct rbtree_t { 42 | rbtree_node root; /* private */ 43 | rbtree_compare_func compare; /* private */ 44 | } *rbtree; 45 | 46 | 47 | void rbtree_init(rbtree t, rbtree_compare_func); 48 | void* rbtree_lookup(rbtree t, void* key); 49 | void rbtree_insert(rbtree t, rbtree_node); 50 | /* you must free the returned node */ 51 | rbtree_node rbtree_delete(rbtree t, void* key); 52 | 53 | #endif 54 | 55 | -------------------------------------------------------------------------------- /examples/hello_world.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include "ebb.h" 9 | 10 | #define MSG ("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 12\r\n\r\nhello world\n") 11 | static int c = 0; 12 | 13 | struct hello_connection { 14 | unsigned int responses_to_write; 15 | }; 16 | 17 | void on_close(ebb_connection *connection) 18 | { 19 | free(connection->data); 20 | free(connection); 21 | } 22 | 23 | static void continue_responding(ebb_connection *connection) 24 | { 25 | int r; 26 | struct hello_connection *connection_data = connection->data; 27 | //printf("response complete \n"); 28 | if(--connection_data->responses_to_write > 0) { 29 | /* write another response */ 30 | r = ebb_connection_write(connection, MSG, sizeof MSG, continue_responding); 31 | assert(r); 32 | } else { 33 | ebb_connection_schedule_close(connection); 34 | } 35 | } 36 | 37 | static void request_complete(ebb_request *request) 38 | { 39 | //printf("request complete \n"); 40 | ebb_connection *connection = request->data; 41 | struct hello_connection *connection_data = connection->data; 42 | 43 | if(ebb_request_should_keep_alive(request)) 44 | connection_data->responses_to_write++; 45 | else 46 | connection_data->responses_to_write = 1; 47 | 48 | ebb_connection_write(connection, MSG, sizeof MSG, continue_responding); 49 | free(request); 50 | } 51 | 52 | static ebb_request* new_request(ebb_connection *connection) 53 | { 54 | //printf("request %d\n", ++c); 55 | ebb_request *request = malloc(sizeof(ebb_request)); 56 | ebb_request_init(request); 57 | request->data = connection; 58 | request->on_complete = request_complete; 59 | return request; 60 | } 61 | 62 | ebb_connection* new_connection(ebb_server *server, struct sockaddr_in *addr) 63 | { 64 | struct hello_connection *connection_data = malloc(sizeof(struct hello_connection)); 65 | if(connection_data == NULL) 66 | return NULL; 67 | connection_data->responses_to_write = 0; 68 | 69 | ebb_connection *connection = malloc(sizeof(ebb_connection)); 70 | if(connection == NULL) { 71 | free(connection_data); 72 | return NULL; 73 | } 74 | 75 | ebb_connection_init(connection); 76 | connection->data = connection_data; 77 | connection->new_request = new_request; 78 | connection->on_close = on_close; 79 | 80 | //printf("connection: %d\n", c++); 81 | return connection; 82 | } 83 | 84 | int main() 85 | { 86 | struct ev_loop *loop = ev_default_loop(0); 87 | ebb_server server; 88 | 89 | ebb_server_init(&server, loop); 90 | //ebb_server_set_secure(&server, "examples/ca-cert.pem", "examples/ca-key.pem"); 91 | server.new_connection = new_connection; 92 | 93 | printf("hello_world listening on port 5000\n"); 94 | ebb_server_listen_on_port(&server, 5000); 95 | ev_loop(loop, 0); 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /ebb_request_parser.h: -------------------------------------------------------------------------------- 1 | /* HTTP/1.1 Parser 2 | * Copyright 2008 ryah dahl, ry at tiny clouds punkt org 3 | * 4 | * Based on Zed Shaw's parser for Mongrel. 5 | * Copyright (c) 2005 Zed A. Shaw 6 | * 7 | * This software may be distributed under the "MIT" license included in the 8 | * README 9 | */ 10 | #ifndef ebb_request_parser_h 11 | #define ebb_request_parser_h 12 | 13 | #include 14 | 15 | typedef struct ebb_request ebb_request; 16 | typedef struct ebb_request_parser ebb_request_parser; 17 | typedef void (*ebb_header_cb)(ebb_request*, const char *at, size_t length, int header_index); 18 | typedef void (*ebb_element_cb)(ebb_request*, const char *at, size_t length); 19 | 20 | #define EBB_RAGEL_STACK_SIZE 10 21 | #define EBB_MAX_MULTIPART_BOUNDARY_LEN 20 22 | 23 | struct ebb_request { 24 | enum { EBB_COPY 25 | , EBB_DELETE 26 | , EBB_GET 27 | , EBB_HEAD 28 | , EBB_LOCK 29 | , EBB_MKCOL 30 | , EBB_MOVE 31 | , EBB_OPTIONS 32 | , EBB_POST 33 | , EBB_PROPFIND 34 | , EBB_PROPPATCH 35 | , EBB_PUT 36 | , EBB_TRACE 37 | , EBB_UNLOCK 38 | } method; 39 | 40 | enum { EBB_IDENTITY 41 | , EBB_CHUNKED 42 | } transfer_encoding; /* ro */ 43 | 44 | size_t content_length; /* ro - 0 if unknown */ 45 | size_t body_read; /* ro */ 46 | int eating_body; /* ro */ 47 | int expect_continue; /* ro */ 48 | unsigned int version_major; /* ro */ 49 | unsigned int version_minor; /* ro */ 50 | int number_of_headers; /* ro */ 51 | int keep_alive; /* private - use ebb_request_should_keep_alive */ 52 | 53 | char multipart_boundary[EBB_MAX_MULTIPART_BOUNDARY_LEN]; /* ro */ 54 | unsigned int multipart_boundary_len; /* ro */ 55 | 56 | /* Public - ordered list of callbacks */ 57 | ebb_element_cb on_path; 58 | ebb_element_cb on_query_string; 59 | ebb_element_cb on_uri; 60 | ebb_element_cb on_fragment; 61 | ebb_header_cb on_header_field; 62 | ebb_header_cb on_header_value; 63 | void (*on_headers_complete)(ebb_request *); 64 | ebb_element_cb on_body; 65 | void (*on_complete)(ebb_request *); 66 | void *data; 67 | }; 68 | 69 | struct ebb_request_parser { 70 | int cs; /* private */ 71 | int stack[EBB_RAGEL_STACK_SIZE]; /* private */ 72 | int top; /* private */ 73 | size_t chunk_size; /* private */ 74 | unsigned eating:1; /* private */ 75 | ebb_request *current_request; /* ro */ 76 | const char *header_field_mark; 77 | const char *header_value_mark; 78 | const char *query_string_mark; 79 | const char *path_mark; 80 | const char *uri_mark; 81 | const char *fragment_mark; 82 | 83 | /* Public */ 84 | ebb_request* (*new_request)(void*); 85 | void *data; 86 | }; 87 | 88 | void ebb_request_parser_init(ebb_request_parser *parser); 89 | size_t ebb_request_parser_execute(ebb_request_parser *parser, const char *data, size_t len); 90 | int ebb_request_parser_has_error(ebb_request_parser *parser); 91 | int ebb_request_parser_is_finished(ebb_request_parser *parser); 92 | void ebb_request_init(ebb_request *); 93 | int ebb_request_should_keep_alive(ebb_request *request); 94 | #define ebb_request_has_body(request) \ 95 | (request->transfer_encoding == EBB_CHUNKED || request->content_length > 0 ) 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /test_rbtree.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2008 Derrick Coetzee 2 | * http://en.literateprograms.org/Red-black_tree_(C)?oldid=7982 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to permit 9 | * persons to whom the Software is furnished to do so, subject to the 10 | * following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 20 | * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 21 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include "rbtree.h" 25 | #include 26 | #include 27 | #include /* rand(), malloc(), free() */ 28 | 29 | static int compare_int(void* left, void* right); 30 | static void print_tree(rbtree t); 31 | static void print_tree_helper(rbtree_node n, int indent); 32 | 33 | int compare_int(void* leftp, void* rightp) { 34 | int left = (int)leftp; 35 | int right = (int)rightp; 36 | if (left < right) 37 | return -1; 38 | else if (left > right) 39 | return 1; 40 | else { 41 | assert (left == right); 42 | return 0; 43 | } 44 | } 45 | 46 | #define INDENT_STEP 4 47 | 48 | void print_tree_helper(rbtree_node n, int indent); 49 | 50 | void print_tree(rbtree t) { 51 | print_tree_helper(t->root, 0); 52 | puts(""); 53 | } 54 | 55 | void print_tree_helper(rbtree_node n, int indent) { 56 | int i; 57 | if (n == NULL) { 58 | fputs("", stdout); 59 | return; 60 | } 61 | if (n->right != NULL) { 62 | print_tree_helper(n->right, indent + INDENT_STEP); 63 | } 64 | for(i=0; icolor == BLACK) 67 | printf("%d\n", (int)n->key); 68 | else 69 | printf("<%d>\n", (int)n->key); 70 | if (n->left != NULL) { 71 | print_tree_helper(n->left, indent + INDENT_STEP); 72 | } 73 | } 74 | 75 | int main() { 76 | int i; 77 | struct rbtree_t t; 78 | rbtree_init(&t, compare_int); 79 | print_tree(&t); 80 | 81 | for(i=0; i<5000; i++) { 82 | int x = rand() % 10000; 83 | int y = rand() % 10000; 84 | #ifdef TRACE 85 | print_tree(&t); 86 | printf("Inserting %d -> %d\n\n", x, y); 87 | #endif 88 | rbtree_node node = (rbtree_node) malloc(sizeof(struct rbtree_node_t)); 89 | node->key = (void*)x; 90 | node->value = (void*)y; 91 | rbtree_insert(&t, node); 92 | assert(rbtree_lookup(&t, (void*)x) == (void*)y); 93 | } 94 | for(i=0; i<60000; i++) { 95 | int x = rand() % 10000; 96 | #ifdef TRACE 97 | print_tree(&t); 98 | printf("Deleting key %d\n\n", x); 99 | #endif 100 | rbtree_node n = rbtree_delete(&t, (void*)x); 101 | if(n != NULL) { 102 | free(n); 103 | } 104 | } 105 | printf("Okay\n"); 106 | return 0; 107 | } 108 | 109 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #based on the makefiles in rubinius 2 | 3 | # Set this such that $(LIBEVDIR)/lib include libev.so and 4 | # $(LIBEVDIR)/include has ev.h 5 | LIBEVDIR=$(HOME)/local/libev 6 | 7 | # Respect the environment 8 | ifeq ($(CC),) 9 | CC=gcc 10 | endif 11 | 12 | UNAME=$(shell uname) 13 | CPU=$(shell uname -p) 14 | MARCH=$(shell uname -m) 15 | OSVER=$(shell uname -r) 16 | 17 | WARNINGS = -Wall 18 | DEBUG = -g -ggdb3 19 | 20 | CFLAGS = -I. $(WARNINGS) $(DEBUG) 21 | 22 | 23 | 24 | COMP=$(CC) 25 | ifeq ($(UNAME),Darwin) 26 | LDOPT=-dynamiclib 27 | SUFFIX=dylib 28 | SONAME=-current_version $(VERSION) -compatibility_version $(VERSION) 29 | else 30 | LDOPT=-shared 31 | SUFFIX=so 32 | ifneq ($(UNAME),SunOS) 33 | SONAME=-Wl,-soname,libptr_array-$(VERSION).$(SUFFIX) 34 | endif 35 | endif 36 | 37 | BIN_RPATH= 38 | LINKER=$(CC) $(LDOPT) 39 | RANLIB = ranlib 40 | 41 | ifndef VERBOSE 42 | COMP=@echo CC $@;$(CC) 43 | LINKER=@echo LINK $@;$(CC) $(LDOPT) 44 | endif 45 | 46 | VERSION=0.1 47 | 48 | NAME=libebb 49 | OUTPUT_LIB=$(NAME).$(VERSION).$(SUFFIX) 50 | OUTPUT_A=$(NAME).a 51 | 52 | ifeq ($(UNAME),Darwin) 53 | SINGLE_MODULE=-Wl,-single_module 54 | ifeq ($(OSVER),9.1.0) 55 | export MACOSX_DEPLOYMENT_TARGET=10.5 56 | else 57 | export MACOSX_DEPLOYMENT_TARGET=10.4 58 | endif 59 | else 60 | SINGLE_MODULE= 61 | endif 62 | 63 | ifeq ($(UNAME),SunOS) 64 | CFLAGS+=-D__C99FEATURES__ 65 | endif 66 | 67 | ifdef DEV 68 | OPTIMIZATIONS= 69 | else 70 | INLINE_OPTS= 71 | OPTIMIZATIONS=-O2 -funroll-loops -finline-functions $(INLINE_OPTS) 72 | endif 73 | 74 | ifeq ($(CPU), powerpc) 75 | OPTIMIZATIONS+=-falign-loops=16 76 | endif 77 | 78 | CFLAGS += -fPIC $(CPPFLAGS) 79 | DEPS = ebb.h ebb_request_parser.h rbtree.h 80 | LIBS = 81 | 82 | GNUTLS_EXISTS = $(shell pkg-config --silence-errors --exists gnutls || echo "no") 83 | ifeq ($(GNUTLS_EXISTS),no) 84 | USING_GNUTLS = "no" 85 | else 86 | CFLAGS += $(shell pkg-config --cflags gnutls) -DHAVE_GNUTLS=1 87 | LIBS += $(shell pkg-config --libs gnutls) 88 | USING_GNUTLS = "yes" 89 | endif 90 | CFLAGS += -I$(LIBEVDIR)/include 91 | LIBS += -L$(LIBEVDIR)/lib -lev 92 | 93 | SOURCES=ebb.c ebb_request_parser.c rbtree.c 94 | OBJS=$(SOURCES:.c=.o) 95 | 96 | %.o: %.c 97 | $(COMP) $(CFLAGS) $(OPTIMIZATIONS) -c $< -o $@ 98 | 99 | %.o: %.S 100 | $(COMP) $(CFLAGS) $(OPTIMIZATIONS) -c $< -o $@ 101 | 102 | .%.d: %.c $(DEPS) 103 | @echo DEP $< 104 | @set -e; rm -f $@; \ 105 | $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \ 106 | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ 107 | rm -f $@.$$$$ 108 | 109 | library: $(OUTPUT_LIB) $(OUTPUT_A) 110 | @echo "using GnuTLS... ${USING_GNUTLS}" 111 | 112 | $(OUTPUT_LIB): $(DEPS) $(OBJS) 113 | $(LINKER) -o $(OUTPUT_LIB) $(OBJS) $(SONAME) $(LIBS) 114 | 115 | $(OUTPUT_A): $(DEPS) $(OBJS) 116 | $(AR) cru $(OUTPUT_A) $(OBJS) 117 | $(RANLIB) $(OUTPUT_A) 118 | 119 | .PHONY: library 120 | 121 | ebb_request_parser.c: ebb_request_parser.rl 122 | ragel -s -G2 $< -o $@ 123 | 124 | clean: 125 | rm -f *.o *.lo *.la *.so *.dylib *.a test_rbtree test_request_parser examples/hello_world 126 | 127 | clobber: clean 128 | rm -f ebb_request_parser.c 129 | 130 | .PHONY: clean clobber 131 | 132 | test: test_request_parser test_rbtree 133 | ./test_request_parser 134 | ./test_rbtree 135 | 136 | test_rbtree: test_rbtree.o $(OUTPUT_A) 137 | $(CC) -o $@ $< $(OUTPUT_A) 138 | 139 | test_request_parser: test_request_parser.o $(OUTPUT_A) 140 | $(CC) -o $@ $< $(OUTPUT_A) 141 | 142 | .PHONY: test 143 | 144 | examples: examples/hello_world 145 | 146 | examples/hello_world: examples/hello_world.o $(OUTPUT_A) 147 | $(CC) $(CFLAGS) -lev -o $@ $< $(OUTPUT_A) $(LIBS) 148 | 149 | .PHONY: examples 150 | 151 | tags: *.c *.h *.rl examples/*.c 152 | ctags $^ 153 | 154 | install: $(OUTPUT_A) 155 | install -d -m755 ${prefix}/lib 156 | install -d -m755 ${prefix}/include 157 | install -m644 $(OUTPUT_A) ${prefix}/lib 158 | install -m644 ebb.h ebb_request_parser.h ${prefix}/include 159 | 160 | upload_website: 161 | scp -r doc/index.html doc/icon.png rydahl@tinyclouds.org:~/web/public/libebb 162 | 163 | 164 | ifneq ($(MAKECMDGOALS),clean) 165 | -include $(SOURCES:%.c=.%.d) 166 | endif 167 | -------------------------------------------------------------------------------- /ebb.h: -------------------------------------------------------------------------------- 1 | /* libebb web server library 2 | * Copyright 2008 ryah dahl, ry at tiny clouds punkt org 3 | * 4 | * This software may be distributed under the "MIT" license included in the 5 | * README 6 | */ 7 | #ifndef EBB_H 8 | #define EBB_H 9 | 10 | /* remove this if you want to embed libebb without GNUTLS */ 11 | //#define HAVE_GNUTLS 1 12 | 13 | #include 14 | #include 15 | #include 16 | #ifdef HAVE_GNUTLS 17 | # include 18 | # include "rbtree.h" /* for ebb_server.session_cache */ 19 | #endif 20 | #include "ebb_request_parser.h" 21 | 22 | #define EBB_MAX_CONNECTIONS 1024 23 | #define EBB_DEFAULT_TIMEOUT 30.0 24 | 25 | #define EBB_AGAIN 0 26 | #define EBB_STOP 1 27 | 28 | typedef struct ebb_buf ebb_buf; 29 | typedef struct ebb_server ebb_server; 30 | typedef struct ebb_connection ebb_connection; 31 | typedef void (*ebb_after_write_cb) (ebb_connection *connection); 32 | 33 | typedef void (*ebb_connection_cb)(ebb_connection *connection, void *data); 34 | 35 | struct ebb_buf { 36 | size_t written; /* private */ 37 | 38 | /* public */ 39 | char *base; 40 | size_t len; 41 | void (*on_release)(ebb_buf*); 42 | void *data; 43 | }; 44 | 45 | struct ebb_server { 46 | int fd; /* ro */ 47 | struct sockaddr_in sockaddr; /* ro */ 48 | socklen_t socklen; /* ro */ 49 | char port[6]; /* ro */ 50 | struct ev_loop *loop; /* ro */ 51 | unsigned listening:1; /* ro */ 52 | unsigned secure:1; /* ro */ 53 | #ifdef HAVE_GNUTLS 54 | gnutls_certificate_credentials_t credentials; /* private */ 55 | struct rbtree_t session_cache; /* private */ 56 | #endif 57 | ev_io connection_watcher; /* private */ 58 | 59 | /* Public */ 60 | 61 | /* Allocates and initializes an ebb_connection. NULL by default. */ 62 | ebb_connection* (*new_connection) (ebb_server*, struct sockaddr_in*); 63 | 64 | void *data; 65 | }; 66 | 67 | struct ebb_connection { 68 | int fd; /* ro */ 69 | struct sockaddr_in sockaddr; /* ro */ 70 | socklen_t socklen; /* ro */ 71 | ebb_server *server; /* ro */ 72 | char *ip; /* ro */ 73 | unsigned open:1; /* ro */ 74 | 75 | const char *to_write; /* ro */ 76 | size_t to_write_len; /* ro */ 77 | size_t written; /* ro */ 78 | ebb_after_write_cb after_write_cb; /* ro */ 79 | 80 | ebb_request_parser parser; /* private */ 81 | ev_io write_watcher; /* private */ 82 | ev_io read_watcher; /* private */ 83 | ev_timer timeout_watcher; /* private */ 84 | ev_timer goodbye_watcher; /* private */ 85 | #ifdef HAVE_GNUTLS 86 | ev_io handshake_watcher; /* private */ 87 | gnutls_session_t session; /* private */ 88 | ev_io goodbye_tls_watcher; /* private */ 89 | #endif 90 | 91 | /* Public */ 92 | 93 | ebb_request* (*new_request) (ebb_connection*); 94 | 95 | /* The new_buf callback allocates and initializes an ebb_buf structure. 96 | * By default this is set to a simple malloc() based callback which always 97 | * returns 4 kilobyte bufs. Write over it with your own to use your own 98 | * custom allocation 99 | * 100 | * new_buf is called each time there is data from a client connection to 101 | * be read. See on_readable() in server.c to see exactly how this is used. 102 | */ 103 | ebb_buf* (*new_buf) (ebb_connection*); 104 | 105 | /* Returns EBB_STOP or EBB_AGAIN 106 | * NULL by default. 107 | */ 108 | int (*on_timeout) (ebb_connection*); 109 | 110 | /* The connection was closed */ 111 | void (*on_close) (ebb_connection*); 112 | 113 | void *data; 114 | }; 115 | 116 | void ebb_server_init (ebb_server *server, struct ev_loop *loop); 117 | #ifdef HAVE_GNUTLS 118 | int ebb_server_set_secure (ebb_server *server, const char *cert_file, 119 | const char *key_file); 120 | #endif 121 | int ebb_server_listen_on_port (ebb_server *server, const int port); 122 | int ebb_server_listen_on_fd (ebb_server *server, const int sfd); 123 | void ebb_server_unlisten (ebb_server *server); 124 | 125 | void ebb_connection_init (ebb_connection *connection); 126 | void ebb_connection_schedule_close (ebb_connection *); 127 | void ebb_connection_reset_timeout (ebb_connection *connection); 128 | int ebb_connection_write (ebb_connection *connection, const char *buf, size_t len, ebb_after_write_cb); 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 41 |
42 | 43 |

libebb

44 | 45 |

46 | libebb is a lightweight HTTP server library for C. It lays the 47 | foundation for writing a web server by providing the socket juggling 48 | and request parsing. By implementing the HTTP/1.1 grammar provided in 49 | RFC2612, libebb understands most most valid HTTP/1.1 connections 50 | (persistent, pipelined, and chunked requests included) and rejects 51 | invalid or malicious requests. libebb supports SSL over HTTP. 52 |

53 | 54 |

55 | The library embraces a minimalistic single-threaded evented design. 56 | No control is removed from the user. For example, all allocations are 57 | done through callbacks so that the user might implement in optimal 58 | ways for their specific application. By design libebb is not 59 | thread-safe and all provided callbacks must not block. libebb uses 60 | the high-performance 61 | libev event loop, but does not control it. The user of the library may 62 | start and stop the loop at will, they may attach thier own watchers. 63 |

64 | 65 |

66 | libebb depends on POSIX sockets, libev, and optionally GnuTLS. 67 |

68 | 69 |

70 | libebb is in the early stages of development and probably contains 71 | many bugs. The API is subject to radical changes. If you're 72 | interested checkout 73 | the source code and join the mailing 75 | list. A release will be made when it proves stable. 76 |

77 | 78 |

libebb is released under the MIT license.

79 | 80 |

Usage

81 | 82 |

83 | libebb is a simple API, mostly it is providing callbacks. There are 84 | two types of callbacks that one will work with: 85 |

86 | 87 |
    88 |
  • callbacks to allocate and initialize data for libebb. These are 89 | named new_* like new_connection and 90 | new_request
  • 91 |
  • callbacks which happen on an event and might provide a pointer to 92 | a chunk of data. These are named on_* like 93 | on_body and on_close.
  • 94 |
95 | 96 |

97 | In libebb there are three important classes: ebb_server, 98 | ebb_connection, and ebb_request. 99 | Each server has many peer connections. Each peer connection may have many 100 | requests. 101 | There are two additional classes ebb_buf and ebb_request_parser 102 | which may or may not be useful. 103 |

104 | 105 |

ebb_server

106 |

107 | ebb_server represents a single web server listening on a 108 | single port. The user must allocate the structure themselves, then 109 | call ebb_server_init() and provide a libev event loop. 110 | ebb_server_set_secure() will make the server understand 111 | HTTPS connections. 112 |

113 | 114 |

115 | After initialized the ebb_server_listen_on_port() can be 116 | called to open the server up to new connections. libebb does not 117 | control the event loop, it is the user's responsibility to start the 118 | event loop (using ev_loop()) after 119 | ebb_server_listen_on_port() is called. 120 |

121 | 122 |

123 | To accept connections you must provide the new server with a callback 124 | called new_connection. This callback must return an allocated 125 | and initialized ebb_connection structure. 126 | To set this callback do 127 |

128 | 129 |
my_server->new_connection = my_new_connection_callback;
130 | 131 |

132 | Additional documentation can be found in ebb.h 133 |

134 | 135 |

ebb_connection

136 | 137 |

138 | This structure contains information and callbacks for a single client 139 | connection. It is allocated and initialized through the 140 | new_connection callback in ebb_server. 141 | To initialize a newly allocated ebb_connection use 142 | ebb_connection_init(). 143 |

144 | 145 |

146 | After ebb_connection_init() is called a number of 147 | callbacks can be set: new_request, new_buf, 148 | on_timeout, and on_close. 149 |

150 | 151 |

152 | When an ebb_connection is returned to an 153 | ebb_server, data is immediately data is read from the 154 | socket. This data must be stored somewhere. Because libebb is 155 | agnostic about allocation decisions, it passes this off to the user in 156 | the form of a callback: connection->new_buf. This 157 | callback returns a newly allocated and initialized 158 | ebb_buf structure. How much libebb attempts to read from 159 | the socket is determined by how large the returned 160 | ebb_buf structure is. Using new_buf is 161 | optional. By default libebb reads data into a static buffer 162 | (allocated at compile time), writing over it on each read. In many 163 | web server using the static buffer will be sufficent because callbacks 164 | made during the parsing will buffer the data elsewhere. Providing a 165 | new_buf callback is necessary only if you want to save 166 | the raw data coming from the socket. 167 |

168 | 169 |

170 | The new_request callback is called at the beginning of a 171 | request. It must return a newly allocated and initialized 172 | ebb_request structure. Because HTTP/1.1 supports peristant 174 | connections, there may be many requests per connection. 175 |

176 | 177 |

178 | You may access the file descriptor for the client socket inside the 179 | ebb_connection structure. Writing the response, in valid 180 | HTTP, is the user's responsibility. Remember, requests must be 181 | returned to client in the same order that they were received. 182 |

183 | 184 |

185 | A convience function, ebb_connection_write, is provided 186 | which will write a single string to the peer. You may use 187 | this function or you may write to the file descriptor directly. 188 |

189 | 190 |

191 | To close a peer connection use 192 | ebb_connnection_schedule_close(). Because SSL may require 193 | some additional communication to close the connection properly, the 194 | file descriptor cannot be closed immediately. The 195 | on_close callback will be made when the peer socket is 196 | finally closed. 197 | Only once on_close is called may the 198 | user free the ebb_connection structure. 199 |

200 | 201 | 202 |

ebb_request

203 | 204 |

205 | This structure provides information about a request. For example, 206 | request->method == EBB_POST would mean the method of 207 | the request is POST. There are also many callbacks 208 | which can be set to handle data from a request as it is parsed. 209 |

210 | 211 |

212 | The on_uri callback and all other 213 | ebb_element_cb callbacks provide pointers to request 214 | data. The annoying thing is they do not necessarily provide a 215 | complete string. This is because the reads from the socket may not 216 | contain an entire request and for efficency libebb does not attempt to 217 | buffer the data. Theoretically, one might receive an 218 | on_uri callback 10 times, each providing just a single 219 | character of the request's URI. See ebb_request_parser.h for 220 | a full list of callbacks that you may provide. (If you don't set them, 221 | they are ignored.) 222 |

223 | 224 |

225 | The on_complete callback is called at the end of 226 | each request. 227 | Only once on_complete is called may the 228 | user free the ebb_request structure. 229 |

230 | 231 |

Example

232 | 233 |

234 | A simple example is provided in examples/hello_world.c. 235 |

236 | 237 |
238 | 239 | -------------------------------------------------------------------------------- /rbtree.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2008 Derrick Coetzee 2 | * http://en.literateprograms.org/Red-black_tree_(C)?oldid=7982 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to permit 9 | * persons to whom the Software is furnished to do so, subject to the 10 | * following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included 13 | * in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 20 | * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR 21 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | #include "rbtree.h" 25 | #include 26 | 27 | #ifndef NULL 28 | # define NULL ((void*)0) 29 | #endif 30 | 31 | 32 | typedef rbtree_node node; 33 | typedef enum rbtree_node_color color; 34 | 35 | 36 | static node grandparent(node n); 37 | static node sibling(node n); 38 | static node uncle(node n); 39 | static void verify_properties(rbtree t); 40 | static void verify_property_1(node root); 41 | static void verify_property_2(node root); 42 | static color node_color(node n); 43 | static void verify_property_4(node root); 44 | static void verify_property_5(node root); 45 | static void verify_property_5_helper(node n, int black_count, int* black_count_path); 46 | 47 | static node lookup_node(rbtree t, void* key); 48 | static void rotate_left(rbtree t, node n); 49 | static void rotate_right(rbtree t, node n); 50 | 51 | static void replace_node(rbtree t, node oldn, node newn); 52 | static void insert_case1(rbtree t, node n); 53 | static void insert_case2(rbtree t, node n); 54 | static void insert_case3(rbtree t, node n); 55 | static void insert_case4(rbtree t, node n); 56 | static void insert_case5(rbtree t, node n); 57 | static node maximum_node(node root); 58 | static void delete_case1(rbtree t, node n); 59 | static void delete_case2(rbtree t, node n); 60 | static void delete_case3(rbtree t, node n); 61 | static void delete_case4(rbtree t, node n); 62 | static void delete_case5(rbtree t, node n); 63 | static void delete_case6(rbtree t, node n); 64 | 65 | node grandparent(node n) { 66 | assert (n != NULL); 67 | assert (n->parent != NULL); /* Not the root node */ 68 | assert (n->parent->parent != NULL); /* Not child of root */ 69 | return n->parent->parent; 70 | } 71 | 72 | node sibling(node n) { 73 | assert (n != NULL); 74 | assert (n->parent != NULL); /* Root node has no sibling */ 75 | if (n == n->parent->left) 76 | return n->parent->right; 77 | else 78 | return n->parent->left; 79 | } 80 | 81 | node uncle(node n) { 82 | assert (n != NULL); 83 | assert (n->parent != NULL); /* Root node has no uncle */ 84 | assert (n->parent->parent != NULL); /* Children of root have no uncle */ 85 | return sibling(n->parent); 86 | } 87 | 88 | void verify_properties(rbtree t) { 89 | #ifdef VERIFY_RBTREE 90 | verify_property_1(t->root); 91 | verify_property_2(t->root); 92 | /* Property 3 is implicit */ 93 | verify_property_4(t->root); 94 | verify_property_5(t->root); 95 | #endif 96 | } 97 | 98 | void verify_property_1(node n) { 99 | assert(node_color(n) == RED || node_color(n) == BLACK); 100 | if (n == NULL) return; 101 | verify_property_1(n->left); 102 | verify_property_1(n->right); 103 | } 104 | 105 | void verify_property_2(node root) { 106 | assert(node_color(root) == BLACK); 107 | } 108 | 109 | color node_color(node n) { 110 | return n == NULL ? BLACK : n->color; 111 | } 112 | 113 | void verify_property_4(node n) { 114 | if (node_color(n) == RED) { 115 | assert (node_color(n->left) == BLACK); 116 | assert (node_color(n->right) == BLACK); 117 | assert (node_color(n->parent) == BLACK); 118 | } 119 | if (n == NULL) return; 120 | verify_property_4(n->left); 121 | verify_property_4(n->right); 122 | } 123 | 124 | void verify_property_5(node root) { 125 | int black_count_path = -1; 126 | verify_property_5_helper(root, 0, &black_count_path); 127 | } 128 | 129 | void verify_property_5_helper(node n, int black_count, int* path_black_count) { 130 | if (node_color(n) == BLACK) { 131 | black_count++; 132 | } 133 | if (n == NULL) { 134 | if (*path_black_count == -1) { 135 | *path_black_count = black_count; 136 | } else { 137 | assert (black_count == *path_black_count); 138 | } 139 | return; 140 | } 141 | verify_property_5_helper(n->left, black_count, path_black_count); 142 | verify_property_5_helper(n->right, black_count, path_black_count); 143 | } 144 | 145 | void rbtree_init(rbtree t, rbtree_compare_func compare) { 146 | t->root = NULL; 147 | t->compare = compare; 148 | verify_properties(t); 149 | } 150 | 151 | node lookup_node(rbtree t, void* key) { 152 | node n = t->root; 153 | while (n != NULL) { 154 | int comp_result = t->compare(key, n->key); 155 | if (comp_result == 0) { 156 | return n; 157 | } else if (comp_result < 0) { 158 | n = n->left; 159 | } else { 160 | assert(comp_result > 0); 161 | n = n->right; 162 | } 163 | } 164 | return n; 165 | } 166 | 167 | void* rbtree_lookup(rbtree t, void* key) { 168 | node n = lookup_node(t, key); 169 | return n == NULL ? NULL : n->value; 170 | } 171 | 172 | void rotate_left(rbtree t, node n) { 173 | node r = n->right; 174 | replace_node(t, n, r); 175 | n->right = r->left; 176 | if (r->left != NULL) { 177 | r->left->parent = n; 178 | } 179 | r->left = n; 180 | n->parent = r; 181 | } 182 | 183 | void rotate_right(rbtree t, node n) { 184 | node L = n->left; 185 | replace_node(t, n, L); 186 | n->left = L->right; 187 | if (L->right != NULL) { 188 | L->right->parent = n; 189 | } 190 | L->right = n; 191 | n->parent = L; 192 | } 193 | 194 | void replace_node(rbtree t, node oldn, node newn) { 195 | if (oldn->parent == NULL) { 196 | t->root = newn; 197 | } else { 198 | if (oldn == oldn->parent->left) 199 | oldn->parent->left = newn; 200 | else 201 | oldn->parent->right = newn; 202 | } 203 | if (newn != NULL) { 204 | newn->parent = oldn->parent; 205 | } 206 | } 207 | 208 | void rbtree_insert(rbtree t, rbtree_node inserted_node) { 209 | inserted_node->color = RED; 210 | inserted_node->left = NULL; 211 | inserted_node->right = NULL; 212 | inserted_node->parent = NULL; 213 | 214 | if (t->root == NULL) { 215 | t->root = inserted_node; 216 | } else { 217 | node n = t->root; 218 | while (1) { 219 | int comp_result = t->compare(inserted_node->key, n->key); 220 | if (comp_result == 0) { 221 | n->value = inserted_node->value; 222 | return; 223 | } else if (comp_result < 0) { 224 | if (n->left == NULL) { 225 | n->left = inserted_node; 226 | break; 227 | } else { 228 | n = n->left; 229 | } 230 | } else { 231 | assert (comp_result > 0); 232 | if (n->right == NULL) { 233 | n->right = inserted_node; 234 | break; 235 | } else { 236 | n = n->right; 237 | } 238 | } 239 | } 240 | inserted_node->parent = n; 241 | } 242 | insert_case1(t, inserted_node); 243 | verify_properties(t); 244 | } 245 | 246 | void insert_case1(rbtree t, node n) { 247 | if (n->parent == NULL) 248 | n->color = BLACK; 249 | else 250 | insert_case2(t, n); 251 | } 252 | 253 | void insert_case2(rbtree t, node n) { 254 | if (node_color(n->parent) == BLACK) 255 | return; /* Tree is still valid */ 256 | else 257 | insert_case3(t, n); 258 | } 259 | 260 | void insert_case3(rbtree t, node n) { 261 | if (node_color(uncle(n)) == RED) { 262 | n->parent->color = BLACK; 263 | uncle(n)->color = BLACK; 264 | grandparent(n)->color = RED; 265 | insert_case1(t, grandparent(n)); 266 | } else { 267 | insert_case4(t, n); 268 | } 269 | } 270 | 271 | void insert_case4(rbtree t, node n) { 272 | if (n == n->parent->right && n->parent == grandparent(n)->left) { 273 | rotate_left(t, n->parent); 274 | n = n->left; 275 | } else if (n == n->parent->left && n->parent == grandparent(n)->right) { 276 | rotate_right(t, n->parent); 277 | n = n->right; 278 | } 279 | insert_case5(t, n); 280 | } 281 | 282 | void insert_case5(rbtree t, node n) { 283 | n->parent->color = BLACK; 284 | grandparent(n)->color = RED; 285 | if (n == n->parent->left && n->parent == grandparent(n)->left) { 286 | rotate_right(t, grandparent(n)); 287 | } else { 288 | assert (n == n->parent->right && n->parent == grandparent(n)->right); 289 | rotate_left(t, grandparent(n)); 290 | } 291 | } 292 | 293 | rbtree_node rbtree_delete(rbtree t, void* key) { 294 | node child; 295 | node n = lookup_node(t, key); 296 | if (n == NULL) return NULL; /* Key not found, do nothing */ 297 | if (n->left != NULL && n->right != NULL) { 298 | /* Copy key/value from predecessor and then delete it instead */ 299 | node pred = maximum_node(n->left); 300 | n->key = pred->key; 301 | n->value = pred->value; 302 | n = pred; 303 | } 304 | 305 | assert(n->left == NULL || n->right == NULL); 306 | child = n->right == NULL ? n->left : n->right; 307 | if (node_color(n) == BLACK) { 308 | n->color = node_color(child); 309 | delete_case1(t, n); 310 | } 311 | replace_node(t, n, child); 312 | 313 | verify_properties(t); 314 | return n; 315 | } 316 | 317 | static node maximum_node(node n) { 318 | assert (n != NULL); 319 | while (n->right != NULL) { 320 | n = n->right; 321 | } 322 | return n; 323 | } 324 | 325 | void delete_case1(rbtree t, node n) { 326 | if (n->parent == NULL) 327 | return; 328 | else 329 | delete_case2(t, n); 330 | } 331 | 332 | void delete_case2(rbtree t, node n) { 333 | if (node_color(sibling(n)) == RED) { 334 | n->parent->color = RED; 335 | sibling(n)->color = BLACK; 336 | if (n == n->parent->left) 337 | rotate_left(t, n->parent); 338 | else 339 | rotate_right(t, n->parent); 340 | } 341 | delete_case3(t, n); 342 | } 343 | 344 | void delete_case3(rbtree t, node n) { 345 | if (node_color(n->parent) == BLACK && 346 | node_color(sibling(n)) == BLACK && 347 | node_color(sibling(n)->left) == BLACK && 348 | node_color(sibling(n)->right) == BLACK) 349 | { 350 | sibling(n)->color = RED; 351 | delete_case1(t, n->parent); 352 | } 353 | else 354 | delete_case4(t, n); 355 | } 356 | 357 | void delete_case4(rbtree t, node n) { 358 | if (node_color(n->parent) == RED && 359 | node_color(sibling(n)) == BLACK && 360 | node_color(sibling(n)->left) == BLACK && 361 | node_color(sibling(n)->right) == BLACK) 362 | { 363 | sibling(n)->color = RED; 364 | n->parent->color = BLACK; 365 | } 366 | else 367 | delete_case5(t, n); 368 | } 369 | 370 | void delete_case5(rbtree t, node n) { 371 | if (n == n->parent->left && 372 | node_color(sibling(n)) == BLACK && 373 | node_color(sibling(n)->left) == RED && 374 | node_color(sibling(n)->right) == BLACK) 375 | { 376 | sibling(n)->color = RED; 377 | sibling(n)->left->color = BLACK; 378 | rotate_right(t, sibling(n)); 379 | } 380 | else if (n == n->parent->right && 381 | node_color(sibling(n)) == BLACK && 382 | node_color(sibling(n)->right) == RED && 383 | node_color(sibling(n)->left) == BLACK) 384 | { 385 | sibling(n)->color = RED; 386 | sibling(n)->right->color = BLACK; 387 | rotate_left(t, sibling(n)); 388 | } 389 | delete_case6(t, n); 390 | } 391 | 392 | void delete_case6(rbtree t, node n) { 393 | sibling(n)->color = node_color(n->parent); 394 | n->parent->color = BLACK; 395 | if (n == n->parent->left) { 396 | assert (node_color(sibling(n)->right) == RED); 397 | sibling(n)->right->color = BLACK; 398 | rotate_left(t, n->parent); 399 | } 400 | else 401 | { 402 | assert (node_color(sibling(n)->left) == RED); 403 | sibling(n)->left->color = BLACK; 404 | rotate_right(t, n->parent); 405 | } 406 | } 407 | 408 | 409 | -------------------------------------------------------------------------------- /ebb_request_parser.rl: -------------------------------------------------------------------------------- 1 | /* HTTP/1.1 Parser 2 | * Copyright 2008 ryah dahl, ry at tiny clouds punkt org 3 | * 4 | * Based on Zed Shaw's parser for Mongrel. 5 | * Copyright (c) 2005 Zed A. Shaw 6 | * 7 | * This software may be distributed under the "MIT" license included in the 8 | * README 9 | */ 10 | #include "ebb_request_parser.h" 11 | 12 | #include 13 | #include 14 | 15 | #define TRUE 1 16 | #define FALSE 0 17 | #define MIN(a,b) (a < b ? a : b) 18 | 19 | #define REMAINING (pe - p) 20 | #define CURRENT (parser->current_request) 21 | #define CONTENT_LENGTH (parser->current_request->content_length) 22 | 23 | #define LEN(FROM) (p - parser->FROM##_mark) 24 | #define CALLBACK(FOR) \ 25 | if(parser->FOR##_mark && CURRENT->on_##FOR) { \ 26 | CURRENT->on_##FOR( CURRENT \ 27 | , parser->FOR##_mark \ 28 | , p - parser->FOR##_mark \ 29 | ); \ 30 | } 31 | #define HEADER_CALLBACK(FOR) \ 32 | if(parser->FOR##_mark && CURRENT->on_##FOR) { \ 33 | CURRENT->on_##FOR( CURRENT \ 34 | , parser->FOR##_mark \ 35 | , p - parser->FOR##_mark \ 36 | , CURRENT->number_of_headers \ 37 | ); \ 38 | } 39 | #define END_REQUEST \ 40 | if(CURRENT->on_complete) \ 41 | CURRENT->on_complete(CURRENT); \ 42 | CURRENT = NULL; 43 | 44 | %%{ 45 | machine ebb_request_parser; 46 | 47 | action mark_header_field { parser->header_field_mark = p; } 48 | action mark_header_value { parser->header_value_mark = p; } 49 | action mark_fragment { parser->fragment_mark = p; } 50 | action mark_query_string { parser->query_string_mark = p; } 51 | action mark_request_path { parser->path_mark = p; } 52 | action mark_request_uri { parser->uri_mark = p; } 53 | 54 | action method_copy { CURRENT->method = EBB_COPY; } 55 | action method_delete { CURRENT->method = EBB_DELETE; } 56 | action method_get { CURRENT->method = EBB_GET; } 57 | action method_head { CURRENT->method = EBB_HEAD; } 58 | action method_lock { CURRENT->method = EBB_LOCK; } 59 | action method_mkcol { CURRENT->method = EBB_MKCOL; } 60 | action method_move { CURRENT->method = EBB_MOVE; } 61 | action method_options { CURRENT->method = EBB_OPTIONS; } 62 | action method_post { CURRENT->method = EBB_POST; } 63 | action method_propfind { CURRENT->method = EBB_PROPFIND; } 64 | action method_proppatch { CURRENT->method = EBB_PROPPATCH; } 65 | action method_put { CURRENT->method = EBB_PUT; } 66 | action method_trace { CURRENT->method = EBB_TRACE; } 67 | action method_unlock { CURRENT->method = EBB_UNLOCK; } 68 | 69 | action write_field { 70 | //printf("write_field!\n"); 71 | HEADER_CALLBACK(header_field); 72 | parser->header_field_mark = NULL; 73 | } 74 | 75 | action write_value { 76 | //printf("write_value!\n"); 77 | HEADER_CALLBACK(header_value); 78 | parser->header_value_mark = NULL; 79 | } 80 | 81 | action request_uri { 82 | //printf("request uri\n"); 83 | CALLBACK(uri); 84 | parser->uri_mark = NULL; 85 | } 86 | 87 | action fragment { 88 | //printf("fragment\n"); 89 | CALLBACK(fragment); 90 | parser->fragment_mark = NULL; 91 | } 92 | 93 | action query_string { 94 | //printf("query string\n"); 95 | CALLBACK(query_string); 96 | parser->query_string_mark = NULL; 97 | } 98 | 99 | action request_path { 100 | //printf("request path\n"); 101 | CALLBACK(path); 102 | parser->path_mark = NULL; 103 | } 104 | 105 | action content_length { 106 | //printf("content_length!\n"); 107 | CURRENT->content_length *= 10; 108 | CURRENT->content_length += *p - '0'; 109 | } 110 | 111 | action use_identity_encoding { CURRENT->transfer_encoding = EBB_IDENTITY; } 112 | action use_chunked_encoding { CURRENT->transfer_encoding = EBB_CHUNKED; } 113 | 114 | action set_keep_alive { CURRENT->keep_alive = TRUE; } 115 | action set_not_keep_alive { CURRENT->keep_alive = FALSE; } 116 | 117 | action multipart_boundary { 118 | if(CURRENT->multipart_boundary_len == EBB_MAX_MULTIPART_BOUNDARY_LEN) { 119 | cs = -1; 120 | fbreak; 121 | } 122 | CURRENT->multipart_boundary[CURRENT->multipart_boundary_len++] = *p; 123 | } 124 | 125 | action expect_continue { 126 | CURRENT->expect_continue = TRUE; 127 | } 128 | 129 | action trailer { 130 | //printf("trailer\n"); 131 | /* not implemenetd yet. (do requests even have trailing headers?) */ 132 | } 133 | 134 | action version_major { 135 | CURRENT->version_major *= 10; 136 | CURRENT->version_major += *p - '0'; 137 | } 138 | 139 | action version_minor { 140 | CURRENT->version_minor *= 10; 141 | CURRENT->version_minor += *p - '0'; 142 | } 143 | 144 | action end_header_line { 145 | CURRENT->number_of_headers++; 146 | } 147 | 148 | action end_headers { 149 | if(CURRENT->on_headers_complete) 150 | CURRENT->on_headers_complete(CURRENT); 151 | } 152 | 153 | action add_to_chunk_size { 154 | //printf("add to chunk size\n"); 155 | parser->chunk_size *= 16; 156 | /* XXX: this can be optimized slightly */ 157 | if( 'A' <= *p && *p <= 'F') 158 | parser->chunk_size += *p - 'A' + 10; 159 | else if( 'a' <= *p && *p <= 'f') 160 | parser->chunk_size += *p - 'a' + 10; 161 | else if( '0' <= *p && *p <= '9') 162 | parser->chunk_size += *p - '0'; 163 | else 164 | assert(0 && "bad hex char"); 165 | } 166 | 167 | action skip_chunk_data { 168 | //printf("skip chunk data\n"); 169 | //printf("chunk_size: %d\n", parser->chunk_size); 170 | if(parser->chunk_size > REMAINING) { 171 | parser->eating = TRUE; 172 | CURRENT->on_body(CURRENT, p, REMAINING); 173 | parser->chunk_size -= REMAINING; 174 | fhold; 175 | fbreak; 176 | } else { 177 | CURRENT->on_body(CURRENT, p, parser->chunk_size); 178 | p += parser->chunk_size; 179 | parser->chunk_size = 0; 180 | parser->eating = FALSE; 181 | fhold; 182 | fgoto chunk_end; 183 | } 184 | } 185 | 186 | action end_chunked_body { 187 | //printf("end chunked body\n"); 188 | END_REQUEST; 189 | fret; // goto Request; 190 | } 191 | 192 | action start_req { 193 | assert(CURRENT == NULL); 194 | CURRENT = parser->new_request(parser->data); 195 | } 196 | 197 | action body_logic { 198 | if(CURRENT->transfer_encoding == EBB_CHUNKED) { 199 | fcall ChunkedBody; 200 | } else { 201 | /* 202 | * EAT BODY 203 | * this is very ugly. sorry. 204 | * 205 | */ 206 | if( CURRENT->content_length == 0) { 207 | 208 | END_REQUEST; 209 | 210 | } else if( CURRENT->content_length < REMAINING ) { 211 | /* 212 | * 213 | * FINISH EATING THE BODY. there is still more 214 | * on the buffer - so we just let it continue 215 | * parsing after we're done 216 | * 217 | */ 218 | p += 1; 219 | if( CURRENT->on_body ) 220 | CURRENT->on_body(CURRENT, p, CURRENT->content_length); 221 | 222 | p += CURRENT->content_length; 223 | CURRENT->body_read = CURRENT->content_length; 224 | 225 | assert(0 <= REMAINING); 226 | 227 | END_REQUEST; 228 | 229 | fhold; 230 | 231 | } else { 232 | /* 233 | * The body is larger than the buffer 234 | * EAT REST OF BUFFER 235 | * there is still more to read though. this will 236 | * be handled on the next invokion of ebb_request_parser_execute 237 | * right before we enter the state machine. 238 | * 239 | */ 240 | p += 1; 241 | size_t eat = REMAINING; 242 | 243 | if( CURRENT->on_body && eat > 0) 244 | CURRENT->on_body(CURRENT, p, eat); 245 | 246 | p += eat; 247 | CURRENT->body_read += eat; 248 | CURRENT->eating_body = TRUE; 249 | //printf("eating body!\n"); 250 | 251 | assert(CURRENT->body_read < CURRENT->content_length); 252 | assert(REMAINING == 0); 253 | 254 | fhold; fbreak; 255 | } 256 | } 257 | } 258 | 259 | # 260 | ## 261 | ### 262 | #### HTTP/1.1 STATE MACHINE 263 | ### 264 | ## RequestHeaders and character types are from 265 | # Zed Shaw's beautiful Mongrel parser. 266 | 267 | CRLF = "\r\n"; 268 | 269 | # character types 270 | CTL = (cntrl | 127); 271 | safe = ("$" | "-" | "_" | "."); 272 | extra = ("!" | "*" | "'" | "(" | ")" | ","); 273 | reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+"); 274 | unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">"); 275 | national = any -- (alpha | digit | reserved | extra | safe | unsafe); 276 | unreserved = (alpha | digit | safe | extra | national); 277 | escape = ("%" xdigit xdigit); 278 | uchar = (unreserved | escape); 279 | pchar = (uchar | ":" | "@" | "&" | "=" | "+"); 280 | tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t"); 281 | 282 | # elements 283 | token = (ascii -- (CTL | tspecials)); 284 | quote = "\""; 285 | # qdtext = token -- "\""; 286 | # quoted_pair = "\" ascii; 287 | # quoted_string = "\"" (qdtext | quoted_pair )* "\""; 288 | 289 | # headers 290 | 291 | Method = ( "COPY" %method_copy 292 | | "DELETE" %method_delete 293 | | "GET" %method_get 294 | | "HEAD" %method_head 295 | | "LOCK" %method_lock 296 | | "MKCOL" %method_mkcol 297 | | "MOVE" %method_move 298 | | "OPTIONS" %method_options 299 | | "POST" %method_post 300 | | "PROPFIND" %method_propfind 301 | | "PROPPATCH" %method_proppatch 302 | | "PUT" %method_put 303 | | "TRACE" %method_trace 304 | | "UNLOCK" %method_unlock 305 | ); # Not allowing extension methods 306 | 307 | HTTP_Version = "HTTP/" digit+ $version_major "." digit+ $version_minor; 308 | 309 | scheme = ( alpha | digit | "+" | "-" | "." )* ; 310 | absolute_uri = (scheme ":" (uchar | reserved )*); 311 | path = ( pchar+ ( "/" pchar* )* ) ; 312 | query = ( uchar | reserved )* >mark_query_string %query_string ; 313 | param = ( pchar | "/" )* ; 314 | params = ( param ( ";" param )* ) ; 315 | rel_path = ( path? (";" params)? ) ; 316 | absolute_path = ( "/"+ rel_path ) >mark_request_path %request_path ("?" query)?; 317 | Request_URI = ( "*" | absolute_uri | absolute_path ) >mark_request_uri %request_uri; 318 | Fragment = ( uchar | reserved )* >mark_fragment %fragment; 319 | 320 | field_name = ( token -- ":" )+; 321 | Field_Name = field_name >mark_header_field %write_field; 322 | 323 | field_value = ((any - " ") any*)?; 324 | Field_Value = field_value >mark_header_value %write_value; 325 | 326 | hsep = ":" " "*; 327 | header = (field_name hsep field_value) :> CRLF; 328 | Header = ( ("Content-Length"i hsep digit+ $content_length) 329 | | ("Connection"i hsep 330 | ( "Keep-Alive"i %set_keep_alive 331 | | "close"i %set_not_keep_alive 332 | ) 333 | ) 334 | | ("Content-Type"i hsep 335 | "multipart/form-data" any* 336 | "boundary=" quote token+ $multipart_boundary quote 337 | ) 338 | | ("Transfer-Encoding"i %use_chunked_encoding hsep "identity" %use_identity_encoding) 339 | | ("Expect"i hsep "100-continue"i %expect_continue) 340 | | ("Trailer"i hsep field_value %trailer) 341 | | (Field_Name hsep Field_Value) 342 | ) :> CRLF; 343 | 344 | Request_Line = ( Method " " Request_URI ("#" Fragment)? " " HTTP_Version CRLF ) ; 345 | RequestHeader = Request_Line (Header %end_header_line)* :> CRLF @end_headers; 346 | 347 | # chunked message 348 | trailing_headers = header*; 349 | #chunk_ext_val = token | quoted_string; 350 | chunk_ext_val = token*; 351 | chunk_ext_name = token*; 352 | chunk_extension = ( ";" " "* chunk_ext_name ("=" chunk_ext_val)? )*; 353 | last_chunk = "0"+ chunk_extension CRLF; 354 | chunk_size = (xdigit* [1-9a-fA-F] xdigit*) $add_to_chunk_size; 355 | chunk_end = CRLF; 356 | chunk_body = any >skip_chunk_data; 357 | chunk_begin = chunk_size chunk_extension CRLF; 358 | chunk = chunk_begin chunk_body chunk_end; 359 | ChunkedBody := chunk* last_chunk trailing_headers CRLF @end_chunked_body; 360 | 361 | Request = RequestHeader >start_req @body_logic; 362 | 363 | main := Request+; # sequence of requests (for keep-alive) 364 | }%% 365 | 366 | %% write data; 367 | 368 | #define COPYSTACK(dest, src) for(i = 0; i < EBB_RAGEL_STACK_SIZE; i++) { dest[i] = src[i]; } 369 | 370 | void ebb_request_parser_init(ebb_request_parser *parser) 371 | { 372 | int i; 373 | 374 | int cs = 0; 375 | int top = 0; 376 | int stack[EBB_RAGEL_STACK_SIZE]; 377 | %% write init; 378 | parser->cs = cs; 379 | parser->top = top; 380 | COPYSTACK(parser->stack, stack); 381 | 382 | parser->chunk_size = 0; 383 | parser->eating = 0; 384 | 385 | parser->current_request = NULL; 386 | 387 | parser->header_field_mark = parser->header_value_mark = 388 | parser->query_string_mark = parser->path_mark = 389 | parser->uri_mark = parser->fragment_mark = NULL; 390 | 391 | parser->new_request = NULL; 392 | } 393 | 394 | 395 | /** exec **/ 396 | size_t ebb_request_parser_execute(ebb_request_parser *parser, const char *buffer, size_t len) 397 | { 398 | const char *p, *pe; 399 | int i, cs = parser->cs; 400 | 401 | int top = parser->top; 402 | int stack[EBB_RAGEL_STACK_SIZE]; 403 | COPYSTACK(stack, parser->stack); 404 | 405 | assert(parser->new_request && "undefined callback"); 406 | 407 | p = buffer; 408 | pe = buffer+len; 409 | 410 | if(0 < parser->chunk_size && parser->eating) { 411 | /* 412 | * 413 | * eat chunked body 414 | * 415 | */ 416 | //printf("eat chunk body (before parse)\n"); 417 | size_t eat = MIN(len, parser->chunk_size); 418 | if(eat == parser->chunk_size) { 419 | parser->eating = FALSE; 420 | } 421 | CURRENT->on_body(CURRENT, p, eat); 422 | p += eat; 423 | parser->chunk_size -= eat; 424 | //printf("eat: %d\n", eat); 425 | } else if( parser->current_request && CURRENT->eating_body ) { 426 | /* 427 | * 428 | * eat normal body 429 | * 430 | */ 431 | //printf("eat normal body (before parse)\n"); 432 | size_t eat = MIN(len, CURRENT->content_length - CURRENT->body_read); 433 | 434 | CURRENT->on_body(CURRENT, p, eat); 435 | p += eat; 436 | CURRENT->body_read += eat; 437 | 438 | if(CURRENT->body_read == CURRENT->content_length) { 439 | END_REQUEST; 440 | } 441 | } 442 | 443 | if(parser->header_field_mark) parser->header_field_mark = buffer; 444 | if(parser->header_value_mark) parser->header_value_mark = buffer; 445 | if(parser->fragment_mark) parser->fragment_mark = buffer; 446 | if(parser->query_string_mark) parser->query_string_mark = buffer; 447 | if(parser->path_mark) parser->path_mark = buffer; 448 | if(parser->uri_mark) parser->uri_mark = buffer; 449 | 450 | %% write exec; 451 | 452 | parser->cs = cs; 453 | parser->top = top; 454 | COPYSTACK(parser->stack, stack); 455 | 456 | HEADER_CALLBACK(header_field); 457 | HEADER_CALLBACK(header_value); 458 | CALLBACK(fragment); 459 | CALLBACK(query_string); 460 | CALLBACK(path); 461 | CALLBACK(uri); 462 | 463 | assert(p <= pe && "buffer overflow after parsing execute"); 464 | 465 | return(p - buffer); 466 | } 467 | 468 | int ebb_request_parser_has_error(ebb_request_parser *parser) 469 | { 470 | return parser->cs == ebb_request_parser_error; 471 | } 472 | 473 | int ebb_request_parser_is_finished(ebb_request_parser *parser) 474 | { 475 | return parser->cs == ebb_request_parser_first_final; 476 | } 477 | 478 | void ebb_request_init(ebb_request *request) 479 | { 480 | request->expect_continue = FALSE; 481 | request->eating_body = 0; 482 | request->body_read = 0; 483 | request->content_length = 0; 484 | request->version_major = 0; 485 | request->version_minor = 0; 486 | request->number_of_headers = 0; 487 | request->transfer_encoding = EBB_IDENTITY; 488 | request->multipart_boundary_len = 0; 489 | request->keep_alive = -1; 490 | 491 | request->on_complete = NULL; 492 | request->on_headers_complete = NULL; 493 | request->on_body = NULL; 494 | request->on_header_field = NULL; 495 | request->on_header_value = NULL; 496 | request->on_uri = NULL; 497 | request->on_fragment = NULL; 498 | request->on_path = NULL; 499 | request->on_query_string = NULL; 500 | } 501 | 502 | int ebb_request_should_keep_alive(ebb_request *request) 503 | { 504 | if(request->keep_alive == -1) 505 | if(request->version_major == 1) 506 | return (request->version_minor != 0); 507 | else if(request->version_major == 0) 508 | return FALSE; 509 | else 510 | return TRUE; 511 | else 512 | return request->keep_alive; 513 | } 514 | -------------------------------------------------------------------------------- /ebb.c: -------------------------------------------------------------------------------- 1 | /* libebb web server library 2 | * Copyright 2008 ryah dahl, ry at tiny clouds punkt org 3 | * 4 | * This software may be distributed under the "MIT" license included in the 5 | * README 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include /* TCP_NODELAY */ 14 | #include /* inet_ntoa */ 15 | #include /* inet_ntoa */ 16 | #include 17 | #include /* perror */ 18 | #include /* perror */ 19 | #include /* for the default methods */ 20 | #include 21 | 22 | #include "ebb.h" 23 | #include "ebb_request_parser.h" 24 | #ifdef HAVE_GNUTLS 25 | # include 26 | # include "rbtree.h" /* for session_cache */ 27 | #endif 28 | 29 | #ifndef TRUE 30 | # define TRUE 1 31 | #endif 32 | #ifndef FALSE 33 | # define FALSE 0 34 | #endif 35 | #ifndef MIN 36 | # define MIN(a,b) (a < b ? a : b) 37 | #endif 38 | 39 | #define error(FORMAT, ...) fprintf(stderr, "error: " FORMAT "\n", ##__VA_ARGS__) 40 | 41 | #define CONNECTION_HAS_SOMETHING_TO_WRITE (connection->to_write != NULL) 42 | 43 | static void 44 | set_nonblock (int fd) 45 | { 46 | int flags = fcntl(fd, F_GETFL, 0); 47 | int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK); 48 | assert(0 <= r && "Setting socket non-block failed!"); 49 | } 50 | 51 | static ssize_t 52 | nosigpipe_push(void *data, const void *buf, size_t len) 53 | { 54 | int fd = (int)data; 55 | int flags = 0; 56 | #ifdef MSG_NOSIGNAL 57 | flags = MSG_NOSIGNAL; 58 | #endif 59 | return send(fd, buf, len, flags); 60 | } 61 | 62 | static void 63 | close_connection(ebb_connection *connection) 64 | { 65 | #ifdef HAVE_GNUTLS 66 | if(connection->server->secure) 67 | ev_io_stop(connection->server->loop, &connection->handshake_watcher); 68 | #endif 69 | ev_io_stop(connection->server->loop, &connection->read_watcher); 70 | ev_io_stop(connection->server->loop, &connection->write_watcher); 71 | ev_timer_stop(connection->server->loop, &connection->timeout_watcher); 72 | 73 | if(0 > close(connection->fd)) 74 | error("problem closing connection fd"); 75 | 76 | connection->open = FALSE; 77 | 78 | if(connection->on_close) 79 | connection->on_close(connection); 80 | /* No access to the connection past this point! 81 | * The user is allowed to free in the callback 82 | */ 83 | } 84 | 85 | #ifdef HAVE_GNUTLS 86 | #define GNUTLS_NEED_WRITE (gnutls_record_get_direction(connection->session) == 1) 87 | #define GNUTLS_NEED_READ (gnutls_record_get_direction(connection->session) == 0) 88 | 89 | #define EBB_MAX_SESSION_KEY 32 90 | #define EBB_MAX_SESSION_VALUE 512 91 | 92 | struct session_cache { 93 | struct rbtree_node_t node; 94 | 95 | gnutls_datum_t key; 96 | gnutls_datum_t value; 97 | 98 | char key_storage[EBB_MAX_SESSION_KEY]; 99 | char value_storage[EBB_MAX_SESSION_VALUE]; 100 | }; 101 | 102 | static int 103 | session_cache_compare (void *left, void *right) 104 | { 105 | gnutls_datum_t *left_key = left; 106 | gnutls_datum_t *right_key = right; 107 | if(left_key->size < right_key->size) 108 | return -1; 109 | else if(left_key->size > right_key->size) 110 | return 1; 111 | else 112 | return memcmp( left_key->data 113 | , right_key->data 114 | , MIN(left_key->size, right_key->size) 115 | ); 116 | } 117 | 118 | static int 119 | session_cache_store(void *data, gnutls_datum_t key, gnutls_datum_t value) 120 | { 121 | rbtree tree = data; 122 | 123 | if( tree == NULL 124 | || key.size > EBB_MAX_SESSION_KEY 125 | || value.size > EBB_MAX_SESSION_VALUE 126 | ) return -1; 127 | 128 | struct session_cache *cache = gnutls_malloc(sizeof(struct session_cache)); 129 | 130 | memcpy (cache->key_storage, key.data, key.size); 131 | cache->key.size = key.size; 132 | cache->key.data = (void*)cache->key_storage; 133 | 134 | memcpy (cache->value_storage, value.data, value.size); 135 | cache->value.size = value.size; 136 | cache->value.data = (void*)cache->value_storage; 137 | 138 | cache->node.key = &cache->key; 139 | cache->node.value = &cache; 140 | 141 | rbtree_insert(tree, (rbtree_node)cache); 142 | 143 | //printf("session_cache_store\n"); 144 | 145 | return 0; 146 | } 147 | 148 | static gnutls_datum_t 149 | session_cache_retrieve (void *data, gnutls_datum_t key) 150 | { 151 | rbtree tree = data; 152 | gnutls_datum_t res = { NULL, 0 }; 153 | struct session_cache *cache = rbtree_lookup(tree, &key); 154 | 155 | if(cache == NULL) 156 | return res; 157 | 158 | res.size = cache->value.size; 159 | res.data = gnutls_malloc (res.size); 160 | if(res.data == NULL) 161 | return res; 162 | 163 | memcpy(res.data, cache->value.data, res.size); 164 | 165 | //printf("session_cache_retrieve\n"); 166 | 167 | return res; 168 | } 169 | 170 | static int 171 | session_cache_remove (void *data, gnutls_datum_t key) 172 | { 173 | rbtree tree = data; 174 | 175 | if(tree == NULL) 176 | return -1; 177 | 178 | struct session_cache *cache = (struct session_cache *)rbtree_delete(tree, &key); 179 | if(cache == NULL) 180 | return -1; 181 | 182 | gnutls_free(cache); 183 | 184 | //printf("session_cache_remove\n"); 185 | 186 | return 0; 187 | } 188 | 189 | static void 190 | on_handshake(struct ev_loop *loop ,ev_io *watcher, int revents) 191 | { 192 | ebb_connection *connection = watcher->data; 193 | 194 | //printf("on_handshake\n"); 195 | 196 | assert(ev_is_active(&connection->timeout_watcher)); 197 | assert(!ev_is_active(&connection->read_watcher)); 198 | assert(!ev_is_active(&connection->write_watcher)); 199 | 200 | if(EV_ERROR & revents) { 201 | error("on_handshake() got error event, closing connection.n"); 202 | goto error; 203 | } 204 | 205 | int r = gnutls_handshake(connection->session); 206 | if(r < 0) { 207 | if(gnutls_error_is_fatal(r)) goto error; 208 | if(r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) 209 | ev_io_set( watcher 210 | , connection->fd 211 | , EV_ERROR | (GNUTLS_NEED_WRITE ? EV_WRITE : EV_READ) 212 | ); 213 | return; 214 | } 215 | 216 | ebb_connection_reset_timeout(connection); 217 | ev_io_stop(loop, watcher); 218 | 219 | ev_io_start(loop, &connection->read_watcher); 220 | if(CONNECTION_HAS_SOMETHING_TO_WRITE) 221 | ev_io_start(loop, &connection->write_watcher); 222 | 223 | return; 224 | error: 225 | close_connection(connection); 226 | } 227 | 228 | #endif /* HAVE_GNUTLS */ 229 | 230 | 231 | /* Internal callback 232 | * called by connection->timeout_watcher 233 | */ 234 | static void 235 | on_timeout(struct ev_loop *loop, ev_timer *watcher, int revents) 236 | { 237 | ebb_connection *connection = watcher->data; 238 | 239 | assert(watcher == &connection->timeout_watcher); 240 | 241 | //printf("on_timeout\n"); 242 | 243 | /* if on_timeout returns true, we don't time out */ 244 | if(connection->on_timeout) { 245 | int r = connection->on_timeout(connection); 246 | 247 | if(r == EBB_AGAIN) { 248 | ebb_connection_reset_timeout(connection); 249 | return; 250 | } 251 | } 252 | 253 | ebb_connection_schedule_close(connection); 254 | } 255 | 256 | /* Internal callback 257 | * called by connection->read_watcher 258 | */ 259 | static void 260 | on_readable(struct ev_loop *loop, ev_io *watcher, int revents) 261 | { 262 | ebb_connection *connection = watcher->data; 263 | char base[TCP_MAXWIN]; 264 | char *recv_buffer = base; 265 | size_t recv_buffer_size = TCP_MAXWIN; 266 | ssize_t recved; 267 | 268 | //printf("on_readable\n"); 269 | 270 | // TODO -- why is this broken? 271 | //assert(ev_is_active(&connection->timeout_watcher)); 272 | assert(watcher == &connection->read_watcher); 273 | 274 | if(EV_ERROR & revents) { 275 | error("on_readable() got error event, closing connection."); 276 | goto error; 277 | } 278 | 279 | ebb_buf *buf = NULL; 280 | if(connection->new_buf) { 281 | buf = connection->new_buf(connection); 282 | if(buf == NULL) return; 283 | recv_buffer = buf->base; 284 | recv_buffer_size = buf->len; 285 | } 286 | 287 | #ifdef HAVE_GNUTLS 288 | assert(!ev_is_active(&connection->handshake_watcher)); 289 | 290 | if(connection->server->secure) { 291 | recved = gnutls_record_recv( connection->session 292 | , recv_buffer 293 | , recv_buffer_size 294 | ); 295 | if(recved <= 0) { 296 | if(gnutls_error_is_fatal(recved)) goto error; 297 | if( (recved == GNUTLS_E_INTERRUPTED || recved == GNUTLS_E_AGAIN) 298 | && GNUTLS_NEED_WRITE 299 | ) ev_io_start(loop, &connection->write_watcher); 300 | return; 301 | } 302 | } else { 303 | #endif /* HAVE_GNUTLS */ 304 | 305 | recved = recv(connection->fd, recv_buffer, recv_buffer_size, 0); 306 | if(recved < 0) goto error; 307 | if(recved == 0) return; 308 | 309 | #ifdef HAVE_GNUTLS 310 | } 311 | #endif /* HAVE_GNUTLS */ 312 | 313 | ebb_connection_reset_timeout(connection); 314 | 315 | ebb_request_parser_execute(&connection->parser, recv_buffer, recved); 316 | 317 | /* parse error? just drop the client. screw the 400 response */ 318 | if(ebb_request_parser_has_error(&connection->parser)) goto error; 319 | 320 | if(buf && buf->on_release) 321 | buf->on_release(buf); 322 | 323 | return; 324 | error: 325 | ebb_connection_schedule_close(connection); 326 | } 327 | 328 | /* Internal callback 329 | * called by connection->write_watcher 330 | */ 331 | static void 332 | on_writable(struct ev_loop *loop, ev_io *watcher, int revents) 333 | { 334 | ebb_connection *connection = watcher->data; 335 | ssize_t sent; 336 | 337 | //printf("on_writable\n"); 338 | 339 | assert(CONNECTION_HAS_SOMETHING_TO_WRITE); 340 | assert(connection->written <= connection->to_write_len); 341 | // TODO -- why is this broken? 342 | //assert(ev_is_active(&connection->timeout_watcher)); 343 | assert(watcher == &connection->write_watcher); 344 | 345 | #ifdef HAVE_GNUTLS 346 | assert(!ev_is_active(&connection->handshake_watcher)); 347 | 348 | if(connection->server->secure) { 349 | sent = gnutls_record_send( connection->session 350 | , connection->to_write + connection->written 351 | , connection->to_write_len - connection->written 352 | ); 353 | if(sent <= 0) { 354 | if(gnutls_error_is_fatal(sent)) goto error; 355 | if( (sent == GNUTLS_E_INTERRUPTED || sent == GNUTLS_E_AGAIN) 356 | && GNUTLS_NEED_READ 357 | ) ev_io_stop(loop, watcher); 358 | return; 359 | } 360 | } else { 361 | #endif /* HAVE_GNUTLS */ 362 | 363 | sent = nosigpipe_push( (void*)connection->fd 364 | , connection->to_write + connection->written 365 | , connection->to_write_len - connection->written 366 | ); 367 | if(sent < 0) goto error; 368 | if(sent == 0) return; 369 | 370 | #ifdef HAVE_GNUTLS 371 | } 372 | #endif /* HAVE_GNUTLS */ 373 | 374 | ebb_connection_reset_timeout(connection); 375 | 376 | connection->written += sent; 377 | 378 | if(connection->written == connection->to_write_len) { 379 | ev_io_stop(loop, watcher); 380 | connection->to_write = NULL; 381 | 382 | if(connection->after_write_cb) 383 | connection->after_write_cb(connection); 384 | } 385 | return; 386 | error: 387 | error("close connection on write."); 388 | ebb_connection_schedule_close(connection); 389 | } 390 | 391 | #ifdef HAVE_GNUTLS 392 | 393 | static void 394 | on_goodbye_tls(struct ev_loop *loop, ev_io *watcher, int revents) 395 | { 396 | ebb_connection *connection = watcher->data; 397 | assert(watcher == &connection->goodbye_tls_watcher); 398 | 399 | if(EV_ERROR & revents) { 400 | error("on_goodbye() got error event, closing connection."); 401 | goto die; 402 | } 403 | 404 | int r = gnutls_bye(connection->session, GNUTLS_SHUT_RDWR); 405 | if(r < 0) { 406 | if(gnutls_error_is_fatal(r)) goto die; 407 | if(r == GNUTLS_E_INTERRUPTED || r == GNUTLS_E_AGAIN) 408 | ev_io_set( watcher 409 | , connection->fd 410 | , EV_ERROR | (GNUTLS_NEED_WRITE ? EV_WRITE : EV_READ) 411 | ); 412 | return; 413 | } 414 | 415 | die: 416 | ev_io_stop(loop, watcher); 417 | if(connection->session) 418 | gnutls_deinit(connection->session); 419 | close_connection(connection); 420 | } 421 | #endif /* HAVE_GNUTLS*/ 422 | 423 | static void 424 | on_goodbye(struct ev_loop *loop, ev_timer *watcher, int revents) 425 | { 426 | ebb_connection *connection = watcher->data; 427 | assert(watcher == &connection->goodbye_watcher); 428 | 429 | close_connection(connection); 430 | } 431 | 432 | 433 | static ebb_request* 434 | new_request_wrapper(void *data) 435 | { 436 | ebb_connection *connection = data; 437 | if(connection->new_request) 438 | return connection->new_request(connection); 439 | return NULL; 440 | } 441 | 442 | /* Internal callback 443 | * Called by server->connection_watcher. 444 | */ 445 | static void 446 | on_connection(struct ev_loop *loop, ev_io *watcher, int revents) 447 | { 448 | ebb_server *server = watcher->data; 449 | 450 | //printf("on connection!\n"); 451 | 452 | assert(server->listening); 453 | assert(server->loop == loop); 454 | assert(&server->connection_watcher == watcher); 455 | 456 | if(EV_ERROR & revents) { 457 | error("on_connection() got error event, closing server."); 458 | ebb_server_unlisten(server); 459 | return; 460 | } 461 | 462 | 463 | struct sockaddr_in addr; // connector's address information 464 | socklen_t addr_len = sizeof(addr); 465 | int fd = accept( server->fd 466 | , (struct sockaddr*) & addr 467 | , & addr_len 468 | ); 469 | if(fd < 0) { 470 | perror("accept()"); 471 | return; 472 | } 473 | 474 | ebb_connection *connection = NULL; 475 | if(server->new_connection) 476 | connection = server->new_connection(server, &addr); 477 | if(connection == NULL) { 478 | close(fd); 479 | return; 480 | } 481 | 482 | set_nonblock(fd); 483 | connection->fd = fd; 484 | connection->open = TRUE; 485 | connection->server = server; 486 | memcpy(&connection->sockaddr, &addr, addr_len); 487 | if(server->port[0] != '\0') 488 | connection->ip = inet_ntoa(connection->sockaddr.sin_addr); 489 | 490 | #ifdef SO_NOSIGPIPE 491 | int arg = 1; 492 | setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &arg, sizeof(int)); 493 | #endif 494 | 495 | #ifdef HAVE_GNUTLS 496 | if(server->secure) { 497 | gnutls_init(&connection->session, GNUTLS_SERVER); 498 | gnutls_transport_set_lowat(connection->session, 0); 499 | gnutls_set_default_priority(connection->session); 500 | gnutls_credentials_set(connection->session, GNUTLS_CRD_CERTIFICATE, connection->server->credentials); 501 | 502 | gnutls_transport_set_ptr(connection->session, (gnutls_transport_ptr) fd); 503 | gnutls_transport_set_push_function(connection->session, nosigpipe_push); 504 | 505 | gnutls_db_set_ptr (connection->session, &server->session_cache); 506 | gnutls_db_set_store_function (connection->session, session_cache_store); 507 | gnutls_db_set_retrieve_function (connection->session, session_cache_retrieve); 508 | gnutls_db_set_remove_function (connection->session, session_cache_remove); 509 | } 510 | 511 | ev_io_set(&connection->handshake_watcher, connection->fd, EV_READ | EV_WRITE | EV_ERROR); 512 | #endif /* HAVE_GNUTLS */ 513 | 514 | /* Note: not starting the write watcher until there is data to be written */ 515 | ev_io_set(&connection->write_watcher, connection->fd, EV_WRITE); 516 | ev_io_set(&connection->read_watcher, connection->fd, EV_READ | EV_ERROR); 517 | /* XXX: seperate error watcher? */ 518 | 519 | ev_timer_start(loop, &connection->timeout_watcher); 520 | 521 | #ifdef HAVE_GNUTLS 522 | if(server->secure) { 523 | ev_io_start(loop, &connection->handshake_watcher); 524 | return; 525 | } 526 | #endif 527 | 528 | ev_io_start(loop, &connection->read_watcher); 529 | } 530 | 531 | /** 532 | * Begin the server listening on a file descriptor. This DOES NOT start the 533 | * event loop. Start the event loop after making this call. 534 | */ 535 | int 536 | ebb_server_listen_on_fd(ebb_server *server, const int fd) 537 | { 538 | assert(server->listening == FALSE); 539 | 540 | if (listen(fd, EBB_MAX_CONNECTIONS) < 0) { 541 | perror("listen()"); 542 | return -1; 543 | } 544 | 545 | set_nonblock(fd); /* XXX superfluous? */ 546 | 547 | server->fd = fd; 548 | server->listening = TRUE; 549 | 550 | ev_io_set (&server->connection_watcher, server->fd, EV_READ | EV_ERROR); 551 | ev_io_start (server->loop, &server->connection_watcher); 552 | 553 | return server->fd; 554 | } 555 | 556 | 557 | /** 558 | * Begin the server listening on a file descriptor This DOES NOT start the 559 | * event loop. Start the event loop after making this call. 560 | */ 561 | int 562 | ebb_server_listen_on_port(ebb_server *server, const int port) 563 | { 564 | int fd = -1; 565 | struct linger ling = {0, 0}; 566 | struct sockaddr_in addr; 567 | int flags = 1; 568 | 569 | if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 570 | perror("socket()"); 571 | goto error; 572 | } 573 | 574 | flags = 1; 575 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags)); 576 | setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)); 577 | setsockopt(fd, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling)); 578 | 579 | /* XXX: Sending single byte chunks in a response body? Perhaps there is a 580 | * need to enable the Nagel algorithm dynamically. For now disabling. 581 | */ 582 | setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); 583 | 584 | /* the memset call clears nonstandard fields in some impementations that 585 | * otherwise mess things up. 586 | */ 587 | memset(&addr, 0, sizeof(addr)); 588 | 589 | addr.sin_family = AF_INET; 590 | addr.sin_port = htons(port); 591 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 592 | 593 | if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 594 | perror("bind()"); 595 | goto error; 596 | } 597 | 598 | int ret = ebb_server_listen_on_fd(server, fd); 599 | if (ret >= 0) { 600 | sprintf(server->port, "%d", port); 601 | } 602 | return ret; 603 | error: 604 | if(fd > 0) close(fd); 605 | return -1; 606 | } 607 | 608 | /** 609 | * Stops the server. Will not accept new connections. Does not drop 610 | * existing connections. 611 | */ 612 | void 613 | ebb_server_unlisten(ebb_server *server) 614 | { 615 | if(server->listening) { 616 | ev_io_stop(server->loop, &server->connection_watcher); 617 | close(server->fd); 618 | server->port[0] = '\0'; 619 | server->listening = FALSE; 620 | } 621 | } 622 | 623 | /** 624 | * Initialize an ebb_server structure. After calling ebb_server_init set 625 | * the callback server->new_connection and, optionally, callback data 626 | * server->data. The new connection MUST be initialized with 627 | * ebb_connection_init before returning it to the server. 628 | * 629 | * @param server the server to initialize 630 | * @param loop a libev loop 631 | */ 632 | void 633 | ebb_server_init(ebb_server *server, struct ev_loop *loop) 634 | { 635 | server->loop = loop; 636 | server->listening = FALSE; 637 | server->port[0] = '\0'; 638 | server->fd = -1; 639 | server->connection_watcher.data = server; 640 | ev_init (&server->connection_watcher, on_connection); 641 | server->secure = FALSE; 642 | 643 | #ifdef HAVE_GNUTLS 644 | rbtree_init(&server->session_cache, session_cache_compare); 645 | server->credentials = NULL; 646 | #endif 647 | 648 | server->new_connection = NULL; 649 | server->data = NULL; 650 | } 651 | 652 | 653 | #ifdef HAVE_GNUTLS 654 | /* similar to server_init. 655 | * 656 | * the user of secure server might want to set additional callbacks from 657 | * GNUTLS. In particular 658 | * gnutls_global_set_mem_functions() 659 | * gnutls_global_set_log_function() 660 | * Also see the note above ebb_connection_init() about setting gnutls cache 661 | * access functions 662 | * 663 | * cert_file: the filename of a PEM certificate file 664 | * 665 | * key_file: the filename of a private key. Currently only PKCS-1 encoded 666 | * RSA and DSA private keys are accepted. 667 | */ 668 | int 669 | ebb_server_set_secure (ebb_server *server, const char *cert_file, const char *key_file) 670 | { 671 | server->secure = TRUE; 672 | gnutls_global_init(); 673 | gnutls_certificate_allocate_credentials(&server->credentials); 674 | /* todo gnutls_certificate_free_credentials */ 675 | int r = gnutls_certificate_set_x509_key_file( server->credentials 676 | , cert_file 677 | , key_file 678 | , GNUTLS_X509_FMT_PEM 679 | ); 680 | if(r < 0) { 681 | error("loading certificates"); 682 | return -1; 683 | } 684 | return 1; 685 | } 686 | #endif /* HAVE_GNUTLS */ 687 | 688 | /** 689 | * Initialize an ebb_connection structure. After calling this function you 690 | * must setup callbacks for the different actions the server can take. See 691 | * server.h for which callbacks are availible. 692 | * 693 | * This should be called immediately after allocating space for a new 694 | * ebb_connection structure. Most likely, this will only be called within 695 | * the ebb_server->new_connection callback which you supply. 696 | * 697 | * If using SSL do consider setting 698 | * gnutls_db_set_retrieve_function (connection->session, _); 699 | * gnutls_db_set_remove_function (connection->session, _); 700 | * gnutls_db_set_store_function (connection->session, _); 701 | * gnutls_db_set_ptr (connection->session, _); 702 | * To provide a better means of storing SSL session caches. libebb provides 703 | * only a simple default implementation. 704 | * 705 | * @param connection the connection to initialize 706 | * @param timeout the timeout in seconds 707 | */ 708 | void 709 | ebb_connection_init(ebb_connection *connection) 710 | { 711 | connection->fd = -1; 712 | connection->server = NULL; 713 | connection->ip = NULL; 714 | connection->open = FALSE; 715 | 716 | ebb_request_parser_init( &connection->parser ); 717 | connection->parser.data = connection; 718 | connection->parser.new_request = new_request_wrapper; 719 | 720 | ev_init (&connection->write_watcher, on_writable); 721 | connection->write_watcher.data = connection; 722 | connection->to_write = NULL; 723 | 724 | ev_init(&connection->read_watcher, on_readable); 725 | connection->read_watcher.data = connection; 726 | 727 | #ifdef HAVE_GNUTLS 728 | connection->handshake_watcher.data = connection; 729 | ev_init(&connection->handshake_watcher, on_handshake); 730 | 731 | ev_init(&connection->goodbye_tls_watcher, on_goodbye_tls); 732 | connection->goodbye_tls_watcher.data = connection; 733 | 734 | connection->session = NULL; 735 | #endif /* HAVE_GNUTLS */ 736 | 737 | ev_timer_init(&connection->goodbye_watcher, on_goodbye, 0., 0.); 738 | connection->goodbye_watcher.data = connection; 739 | 740 | ev_timer_init(&connection->timeout_watcher, on_timeout, EBB_DEFAULT_TIMEOUT, 0.); 741 | connection->timeout_watcher.data = connection; 742 | 743 | connection->new_buf = NULL; 744 | connection->new_request = NULL; 745 | connection->on_timeout = NULL; 746 | connection->on_close = NULL; 747 | connection->data = NULL; 748 | } 749 | 750 | void 751 | ebb_connection_schedule_close (ebb_connection *connection) 752 | { 753 | #ifdef HAVE_GNUTLS 754 | if(connection->server->secure) { 755 | ev_io_set(&connection->goodbye_tls_watcher, connection->fd, EV_ERROR | EV_READ | EV_WRITE); 756 | ev_io_start(connection->server->loop, &connection->goodbye_tls_watcher); 757 | return; 758 | } 759 | #endif 760 | ev_timer_start(connection->server->loop, &connection->goodbye_watcher); 761 | } 762 | 763 | /* 764 | * Resets the timeout to stay alive for another connection->timeout seconds 765 | */ 766 | void 767 | ebb_connection_reset_timeout(ebb_connection *connection) 768 | { 769 | ev_timer_again(connection->server->loop, &connection->timeout_watcher); 770 | } 771 | 772 | /** 773 | * Writes a string to the socket. This is actually sets a watcher 774 | * which may take multiple iterations to write the entire string. 775 | * 776 | * The buf->on_release() callback will be made when the operation is complete. 777 | * 778 | * This can only be called once at a time. If you call it again 779 | * while the connection is writing another buffer the ebb_connection_write 780 | * will return FALSE and ignore the request. 781 | */ 782 | int 783 | ebb_connection_write (ebb_connection *connection, const char *buf, size_t len, ebb_after_write_cb cb) 784 | { 785 | if(ev_is_active(&connection->write_watcher)) 786 | return FALSE; 787 | assert(!CONNECTION_HAS_SOMETHING_TO_WRITE); 788 | connection->to_write = buf; 789 | connection->to_write_len = len; 790 | connection->written = 0; 791 | connection->after_write_cb = cb; 792 | ev_io_start(connection->server->loop, &connection->write_watcher); 793 | return TRUE; 794 | } 795 | -------------------------------------------------------------------------------- /test_request_parser.c: -------------------------------------------------------------------------------- 1 | /* unit tests for request parser 2 | * Copyright 2008 ryah dahl, ry at tiny clouds punkt org 3 | * 4 | * This software may be distributed under the "MIT" license included in the 5 | * README 6 | */ 7 | #include "ebb_request_parser.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define TRUE 1 14 | #define FALSE 0 15 | 16 | #define MAX_HEADERS 500 17 | #define MAX_ELEMENT_SIZE 500 18 | 19 | static ebb_request_parser parser; 20 | struct request_data { 21 | const char *raw; 22 | int request_method; 23 | char request_path[MAX_ELEMENT_SIZE]; 24 | char request_uri[MAX_ELEMENT_SIZE]; 25 | char fragment[MAX_ELEMENT_SIZE]; 26 | char query_string[MAX_ELEMENT_SIZE]; 27 | char body[MAX_ELEMENT_SIZE]; 28 | int num_headers; 29 | char header_fields[MAX_HEADERS][MAX_ELEMENT_SIZE]; 30 | char header_values[MAX_HEADERS][MAX_ELEMENT_SIZE]; 31 | int should_keep_alive; 32 | ebb_request request; 33 | }; 34 | static struct request_data requests[5]; 35 | static int num_requests; 36 | 37 | const struct request_data curl_get = 38 | { raw: "GET /test HTTP/1.1\r\n" 39 | "User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n" 40 | "Host: 0.0.0.0:5000\r\n" 41 | "Accept: */*\r\n" 42 | "\r\n" 43 | , should_keep_alive: TRUE 44 | , request_method: EBB_GET 45 | , query_string: "" 46 | , fragment: "" 47 | , request_path: "/test" 48 | , request_uri: "/test" 49 | , num_headers: 3 50 | , header_fields: { "User-Agent", "Host", "Accept" } 51 | , header_values: { "curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1", "0.0.0.0:5000", "*/*" } 52 | , body: "" 53 | }; 54 | 55 | const struct request_data firefox_get = 56 | { raw: "GET /favicon.ico HTTP/1.1\r\n" 57 | "Host: 0.0.0.0:5000\r\n" 58 | "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n" 59 | "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" 60 | "Accept-Language: en-us,en;q=0.5\r\n" 61 | "Accept-Encoding: gzip,deflate\r\n" 62 | "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" 63 | "Keep-Alive: 300\r\n" 64 | "Connection: keep-alive\r\n" 65 | "\r\n" 66 | , should_keep_alive: TRUE 67 | , request_method: EBB_GET 68 | , query_string: "" 69 | , fragment: "" 70 | , request_path: "/favicon.ico" 71 | , request_uri: "/favicon.ico" 72 | , num_headers: 8 73 | , header_fields: 74 | { "Host" 75 | , "User-Agent" 76 | , "Accept" 77 | , "Accept-Language" 78 | , "Accept-Encoding" 79 | , "Accept-Charset" 80 | , "Keep-Alive" 81 | , "Connection" 82 | } 83 | , header_values: 84 | { "0.0.0.0:5000" 85 | , "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" 86 | , "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" 87 | , "en-us,en;q=0.5" 88 | , "gzip,deflate" 89 | , "ISO-8859-1,utf-8;q=0.7,*;q=0.7" 90 | , "300" 91 | , "keep-alive" 92 | } 93 | , body: "" 94 | }; 95 | 96 | const struct request_data dumbfuck = 97 | { raw: "GET /dumbfuck HTTP/1.1\r\n" 98 | "aaaaaaaaaaaaa:++++++++++\r\n" 99 | "\r\n" 100 | , should_keep_alive: TRUE 101 | , request_method: EBB_GET 102 | , query_string: "" 103 | , fragment: "" 104 | , request_path: "/dumbfuck" 105 | , request_uri: "/dumbfuck" 106 | , num_headers: 1 107 | , header_fields: { "aaaaaaaaaaaaa" } 108 | , header_values: { "++++++++++" } 109 | , body: "" 110 | }; 111 | 112 | const struct request_data fragment_in_uri = 113 | { raw: "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n" 114 | "\r\n" 115 | , should_keep_alive: TRUE 116 | , request_method: EBB_GET 117 | , query_string: "page=1" 118 | , fragment: "posts-17408" 119 | , request_path: "/forums/1/topics/2375" 120 | /* XXX request uri does not include fragment? */ 121 | , request_uri: "/forums/1/topics/2375?page=1" 122 | , num_headers: 0 123 | , body: "" 124 | }; 125 | 126 | 127 | // get - no headers - no body 128 | const struct request_data get_no_headers_no_body = 129 | { raw: "GET /get_no_headers_no_body/world HTTP/1.1\r\n" 130 | "\r\n" 131 | , should_keep_alive: TRUE 132 | , request_method: EBB_GET 133 | , query_string: "" 134 | , fragment: "" 135 | , request_path: "/get_no_headers_no_body/world" 136 | , request_uri: "/get_no_headers_no_body/world" 137 | , num_headers: 0 138 | , body: "" 139 | }; 140 | 141 | // get - one header - no body 142 | const struct request_data get_one_header_no_body = 143 | { raw: "GET /get_one_header_no_body HTTP/1.1\r\n" 144 | "Accept: */*\r\n" 145 | "\r\n" 146 | , should_keep_alive: TRUE 147 | , request_method: EBB_GET 148 | , query_string: "" 149 | , fragment: "" 150 | , request_path: "/get_one_header_no_body" 151 | , request_uri: "/get_one_header_no_body" 152 | , num_headers: 1 153 | , header_fields: { "Accept" } 154 | , header_values: { "*/*" } 155 | , body: "" 156 | }; 157 | 158 | // get - no headers - body "HELLO" 159 | const struct request_data get_funky_content_length_body_hello = 160 | { raw: "GET /get_funky_content_length_body_hello HTTP/1.0\r\n" 161 | "conTENT-Length: 5\r\n" 162 | "\r\n" 163 | "HELLO" 164 | , should_keep_alive: FALSE 165 | , request_method: EBB_GET 166 | , query_string: "" 167 | , fragment: "" 168 | , request_path: "/get_funky_content_length_body_hello" 169 | , request_uri: "/get_funky_content_length_body_hello" 170 | , num_headers: 1 171 | , header_fields: { "conTENT-Length" } 172 | , header_values: { "5" } 173 | , body: "HELLO" 174 | }; 175 | 176 | // post - one header - body "World" 177 | const struct request_data post_identity_body_world = 178 | { raw: "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n" 179 | "Accept: */*\r\n" 180 | "Transfer-Encoding: identity\r\n" 181 | "Content-Length: 5\r\n" 182 | "\r\n" 183 | "World" 184 | , should_keep_alive: TRUE 185 | , request_method: EBB_POST 186 | , query_string: "q=search" 187 | , fragment: "hey" 188 | , request_path: "/post_identity_body_world" 189 | , request_uri: "/post_identity_body_world?q=search" 190 | , num_headers: 3 191 | , header_fields: { "Accept", "Transfer-Encoding", "Content-Length" } 192 | , header_values: { "*/*", "identity", "5" } 193 | , body: "World" 194 | }; 195 | 196 | // post - no headers - chunked body "all your base are belong to us" 197 | const struct request_data post_chunked_all_your_base = 198 | { raw: "POST /post_chunked_all_your_base HTTP/1.1\r\n" 199 | "Transfer-Encoding: chunked\r\n" 200 | "\r\n" 201 | "1e\r\nall your base are belong to us\r\n" 202 | "0\r\n" 203 | "\r\n" 204 | , should_keep_alive: TRUE 205 | , request_method: EBB_POST 206 | , query_string: "" 207 | , fragment: "" 208 | , request_path: "/post_chunked_all_your_base" 209 | , request_uri: "/post_chunked_all_your_base" 210 | , num_headers: 1 211 | , header_fields: { "Transfer-Encoding" } 212 | , header_values: { "chunked" } 213 | , body: "all your base are belong to us" 214 | }; 215 | 216 | // two chunks ; triple zero ending 217 | const struct request_data two_chunks_mult_zero_end = 218 | { raw: "POST /two_chunks_mult_zero_end HTTP/1.1\r\n" 219 | "Transfer-Encoding: chunked\r\n" 220 | "\r\n" 221 | "5\r\nhello\r\n" 222 | "6\r\n world\r\n" 223 | "000\r\n" 224 | "\r\n" 225 | , should_keep_alive: TRUE 226 | , request_method: EBB_POST 227 | , query_string: "" 228 | , fragment: "" 229 | , request_path: "/two_chunks_mult_zero_end" 230 | , request_uri: "/two_chunks_mult_zero_end" 231 | , num_headers: 1 232 | , header_fields: { "Transfer-Encoding" } 233 | , header_values: { "chunked" } 234 | , body: "hello world" 235 | }; 236 | 237 | 238 | // chunked with trailing headers. blech. 239 | const struct request_data chunked_w_trailing_headers = 240 | { raw: "POST /chunked_w_trailing_headers HTTP/1.1\r\n" 241 | "Transfer-Encoding: chunked\r\n" 242 | "\r\n" 243 | "5\r\nhello\r\n" 244 | "6\r\n world\r\n" 245 | "0\r\n" 246 | "Vary: *\r\n" 247 | "Content-Type: text/plain\r\n" 248 | "\r\n" 249 | , should_keep_alive: TRUE 250 | , request_method: EBB_POST 251 | , query_string: "" 252 | , fragment: "" 253 | , request_path: "/chunked_w_trailing_headers" 254 | , request_uri: "/chunked_w_trailing_headers" 255 | , num_headers: 1 256 | , header_fields: { "Transfer-Encoding" } 257 | , header_values: { "chunked" } 258 | , body: "hello world" 259 | }; 260 | 261 | // with bullshit after the length 262 | const struct request_data chunked_w_bullshit_after_length = 263 | { raw: "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n" 264 | "Transfer-Encoding: chunked\r\n" 265 | "\r\n" 266 | "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n" 267 | "6; blahblah; blah\r\n world\r\n" 268 | "0\r\n" 269 | "\r\n" 270 | , should_keep_alive: TRUE 271 | , request_method: EBB_POST 272 | , query_string: "" 273 | , fragment: "" 274 | , request_path: "/chunked_w_bullshit_after_length" 275 | , request_uri: "/chunked_w_bullshit_after_length" 276 | , num_headers: 1 277 | , header_fields: { "Transfer-Encoding" } 278 | , header_values: { "chunked" } 279 | , body: "hello world" 280 | }; 281 | 282 | const struct request_data *fixtures[] = 283 | { &curl_get 284 | , &firefox_get 285 | , &dumbfuck 286 | , &fragment_in_uri 287 | , &get_no_headers_no_body 288 | , &get_one_header_no_body 289 | , &get_funky_content_length_body_hello 290 | , &post_identity_body_world 291 | , &post_chunked_all_your_base 292 | , &two_chunks_mult_zero_end 293 | , &chunked_w_trailing_headers 294 | , &chunked_w_bullshit_after_length 295 | , NULL 296 | }; 297 | 298 | 299 | int request_data_eq 300 | ( struct request_data *r1 301 | , const struct request_data *r2 302 | ) 303 | { 304 | if(ebb_request_should_keep_alive(&r1->request) != r2->should_keep_alive) { 305 | printf("requests disagree on keep-alive"); 306 | return FALSE; 307 | } 308 | 309 | if(0 != strcmp(r1->body, r2->body)) { 310 | printf("body '%s' != '%s'\n", r1->body, r2->body); 311 | return FALSE; 312 | } 313 | if(0 != strcmp(r1->fragment, r2->fragment)) { 314 | printf("fragment '%s' != '%s'\n", r1->fragment, r2->fragment); 315 | return FALSE; 316 | } 317 | if(0 != strcmp(r1->query_string, r2->query_string)) { 318 | printf("query_string '%s' != '%s'\n", r1->query_string, r2->query_string); 319 | return FALSE; 320 | } 321 | if(r1->request.method != r2->request_method) { 322 | printf("request_method '%d' != '%d'\n", r1->request.method, r2->request_method); 323 | return FALSE; 324 | } 325 | if(0 != strcmp(r1->request_path, r2->request_path)) { 326 | printf("request_path '%s' != '%s'\n", r1->request_path, r2->request_path); 327 | return FALSE; 328 | } 329 | if(0 != strcmp(r1->request_uri, r2->request_uri)) { 330 | printf("request_uri '%s' != '%s'\n", r1->request_uri, r2->request_uri); 331 | return FALSE; 332 | } 333 | if(r1->num_headers != r2->num_headers) { 334 | printf("num_headers '%d' != '%d'\n", r1->num_headers, r2->num_headers); 335 | return FALSE; 336 | } 337 | int i; 338 | for(i = 0; i < r1->num_headers; i++) { 339 | if(0 != strcmp(r1->header_fields[i], r2->header_fields[i])) { 340 | printf("header field '%s' != '%s'\n", r1->header_fields[i], r2->header_fields[i]); 341 | return FALSE; 342 | } 343 | if(0 != strcmp(r1->header_values[i], r2->header_values[i])) { 344 | printf("header field '%s' != '%s'\n", r1->header_values[i], r2->header_values[i]); 345 | return FALSE; 346 | } 347 | } 348 | return TRUE; 349 | } 350 | 351 | int request_eq 352 | ( int index 353 | , const struct request_data *expected 354 | ) 355 | { 356 | return request_data_eq(&requests[index], expected); 357 | } 358 | 359 | void request_complete(ebb_request *info) 360 | { 361 | // printf("request complete\n"); 362 | num_requests++; 363 | } 364 | 365 | void request_path_cb(ebb_request *request, const char *p, size_t len) 366 | { 367 | strncat(requests[num_requests].request_path, p, len); 368 | } 369 | 370 | void request_uri_cb(ebb_request *request, const char *p, size_t len) 371 | { 372 | strncat(requests[num_requests].request_uri, p, len); 373 | } 374 | 375 | void query_string_cb(ebb_request *request, const char *p, size_t len) 376 | { 377 | strncat(requests[num_requests].query_string, p, len); 378 | } 379 | 380 | void fragment_cb(ebb_request *request, const char *p, size_t len) 381 | { 382 | strncat(requests[num_requests].fragment, p, len); 383 | } 384 | 385 | void header_field_cb(ebb_request *request, const char *p, size_t len, int header_index) 386 | { 387 | strncat(requests[num_requests].header_fields[header_index], p, len); 388 | } 389 | 390 | void header_value_cb(ebb_request *request, const char *p, size_t len, int header_index) 391 | { 392 | strncat(requests[num_requests].header_values[header_index], p, len); 393 | requests[num_requests].num_headers = header_index + 1; 394 | } 395 | 396 | void body_handler(ebb_request *request, const char *p, size_t len) 397 | { 398 | strncat(requests[num_requests].body, p, len); 399 | // printf("body_handler: '%s'\n", requests[num_requests].body); 400 | } 401 | 402 | ebb_request* new_request () 403 | { 404 | requests[num_requests].num_headers = 0; 405 | requests[num_requests].request_method = -1; 406 | requests[num_requests].request_path[0] = 0; 407 | requests[num_requests].request_uri[0] = 0; 408 | requests[num_requests].fragment[0] = 0; 409 | requests[num_requests].query_string[0] = 0; 410 | requests[num_requests].body[0] = 0; 411 | int i; 412 | for(i = 0; i < MAX_HEADERS; i++) { 413 | requests[num_requests].header_fields[i][0] = 0; 414 | requests[num_requests].header_values[i][0] = 0; 415 | } 416 | 417 | ebb_request *r = &requests[num_requests].request; 418 | ebb_request_init(r); 419 | 420 | r->on_complete = request_complete; 421 | r->on_header_field = header_field_cb; 422 | r->on_header_value = header_value_cb; 423 | r->on_path = request_path_cb; 424 | r->on_uri = request_uri_cb; 425 | r->on_fragment = fragment_cb; 426 | r->on_query_string = query_string_cb; 427 | r->on_body = body_handler; 428 | 429 | r->data = &requests[num_requests]; 430 | // printf("new request %d\n", num_requests); 431 | return r; 432 | } 433 | 434 | void parser_init() 435 | { 436 | num_requests = 0; 437 | 438 | ebb_request_parser_init(&parser); 439 | 440 | parser.new_request = new_request; 441 | } 442 | 443 | int test_request 444 | ( const struct request_data *request_data 445 | ) 446 | { 447 | size_t traversed = 0; 448 | parser_init(); 449 | 450 | traversed = ebb_request_parser_execute( &parser 451 | , request_data->raw 452 | , strlen(request_data->raw) 453 | ); 454 | if( ebb_request_parser_has_error(&parser) ) 455 | return FALSE; 456 | if(! ebb_request_parser_is_finished(&parser) ) 457 | return FALSE; 458 | if(num_requests != 1) 459 | return FALSE; 460 | 461 | return request_eq(0, request_data); 462 | } 463 | 464 | int test_error 465 | ( const char *buf 466 | ) 467 | { 468 | size_t traversed = 0; 469 | parser_init(); 470 | 471 | traversed = ebb_request_parser_execute(&parser, buf, strlen(buf)); 472 | 473 | return ebb_request_parser_has_error(&parser); 474 | } 475 | 476 | 477 | int test_multiple3 478 | ( const struct request_data *r1 479 | , const struct request_data *r2 480 | , const struct request_data *r3 481 | ) 482 | { 483 | char total[80*1024] = "\0"; 484 | 485 | strcat(total, r1->raw); 486 | strcat(total, r2->raw); 487 | strcat(total, r3->raw); 488 | 489 | size_t traversed = 0; 490 | parser_init(); 491 | 492 | traversed = ebb_request_parser_execute(&parser, total, strlen(total)); 493 | 494 | 495 | if( ebb_request_parser_has_error(&parser) ) 496 | return FALSE; 497 | if(! ebb_request_parser_is_finished(&parser) ) 498 | return FALSE; 499 | if(num_requests != 3) 500 | return FALSE; 501 | 502 | if(!request_eq(0, r1)){ 503 | printf("request 1 error.\n"); 504 | return FALSE; 505 | } 506 | if(!request_eq(1, r2)){ 507 | printf("request 2 error.\n"); 508 | return FALSE; 509 | } 510 | if(!request_eq(2, r3)){ 511 | printf("request 3 error.\n"); 512 | return FALSE; 513 | } 514 | 515 | return TRUE; 516 | } 517 | 518 | /** 519 | * SCAN through every possible breaking to make sure the 520 | * parser can handle getting the content in any chunks that 521 | * might come from the socket 522 | */ 523 | int test_scan2 524 | ( const struct request_data *r1 525 | , const struct request_data *r2 526 | , const struct request_data *r3 527 | ) 528 | { 529 | char total[80*1024] = "\0"; 530 | char buf1[80*1024] = "\0"; 531 | char buf2[80*1024] = "\0"; 532 | 533 | strcat(total, r1->raw); 534 | strcat(total, r2->raw); 535 | strcat(total, r3->raw); 536 | 537 | int total_len = strlen(total); 538 | 539 | //printf("total_len = %d\n", total_len); 540 | int i; 541 | for(i = 1; i < total_len - 1; i ++ ) { 542 | 543 | parser_init(); 544 | 545 | int buf1_len = i; 546 | strncpy(buf1, total, buf1_len); 547 | buf1[buf1_len] = 0; 548 | 549 | int buf2_len = total_len - i; 550 | strncpy(buf2, total+i, buf2_len); 551 | buf2[buf2_len] = 0; 552 | 553 | ebb_request_parser_execute(&parser, buf1, buf1_len); 554 | 555 | if( ebb_request_parser_has_error(&parser) ) { 556 | return FALSE; 557 | } 558 | /* 559 | if(ebb_request_parser_is_finished(&parser)) 560 | return FALSE; 561 | */ 562 | 563 | ebb_request_parser_execute(&parser, buf2, buf2_len); 564 | 565 | if( ebb_request_parser_has_error(&parser)) 566 | return FALSE; 567 | if(!ebb_request_parser_is_finished(&parser)) 568 | return FALSE; 569 | 570 | if(3 != num_requests) { 571 | printf("scan error: got %d requests in iteration %d\n", num_requests, i); 572 | return FALSE; 573 | } 574 | 575 | if(!request_eq(0, r1)) { 576 | printf("not maching r1\n"); 577 | return FALSE; 578 | } 579 | if(!request_eq(1, r2)) { 580 | printf("not maching r2\n"); 581 | return FALSE; 582 | } 583 | if(!request_eq(2, r3)) { 584 | printf("not maching r3\n"); 585 | return FALSE; 586 | } 587 | } 588 | return TRUE; 589 | } 590 | 591 | int test_scan3 592 | ( const struct request_data *r1 593 | , const struct request_data *r2 594 | , const struct request_data *r3 595 | ) 596 | { 597 | char total[80*1024] = "\0"; 598 | char buf1[80*1024] = "\0"; 599 | char buf2[80*1024] = "\0"; 600 | char buf3[80*1024] = "\0"; 601 | 602 | strcat(total, r1->raw); 603 | strcat(total, r2->raw); 604 | strcat(total, r3->raw); 605 | 606 | int total_len = strlen(total); 607 | 608 | //printf("total_len = %d\n", total_len); 609 | int i,j; 610 | for(j = 2; j < total_len - 1; j ++ ) { 611 | for(i = 1; i < j; i ++ ) { 612 | 613 | parser_init(); 614 | 615 | 616 | 617 | 618 | int buf1_len = i; 619 | strncpy(buf1, total, buf1_len); 620 | buf1[buf1_len] = 0; 621 | 622 | int buf2_len = j - i; 623 | strncpy(buf2, total+i, buf2_len); 624 | buf2[buf2_len] = 0; 625 | 626 | int buf3_len = total_len - j; 627 | strncpy(buf3, total+j, buf3_len); 628 | buf3[buf3_len] = 0; 629 | 630 | /* 631 | printf("buf1: %s - %d\n", buf1, buf1_len); 632 | printf("buf2: %s - %d \n", buf2, buf2_len ); 633 | printf("buf3: %s - %d\n\n", buf3, buf3_len); 634 | */ 635 | 636 | ebb_request_parser_execute(&parser, buf1, buf1_len); 637 | 638 | if( ebb_request_parser_has_error(&parser) ) { 639 | return FALSE; 640 | } 641 | 642 | ebb_request_parser_execute(&parser, buf2, buf2_len); 643 | 644 | if( ebb_request_parser_has_error(&parser) ) { 645 | return FALSE; 646 | } 647 | 648 | ebb_request_parser_execute(&parser, buf3, buf3_len); 649 | 650 | if( ebb_request_parser_has_error(&parser)) 651 | return FALSE; 652 | if(!ebb_request_parser_is_finished(&parser)) 653 | return FALSE; 654 | 655 | if(3 != num_requests) { 656 | printf("scan error: only got %d requests in iteration %d\n", num_requests, i); 657 | return FALSE; 658 | } 659 | 660 | if(!request_eq(0, r1)) { 661 | printf("not maching r1\n"); 662 | return FALSE; 663 | } 664 | if(!request_eq(1, r2)) { 665 | printf("not maching r2\n"); 666 | return FALSE; 667 | } 668 | if(!request_eq(2, r3)) { 669 | printf("not maching r3\n"); 670 | return FALSE; 671 | } 672 | } 673 | } 674 | return TRUE; 675 | } 676 | 677 | int main() 678 | { 679 | 680 | assert(test_error("hello world")); 681 | assert(test_error("GET / HTP/1.1\r\n\r\n")); 682 | 683 | assert(test_request(&curl_get)); 684 | assert(test_request(&firefox_get)); 685 | 686 | // Zed's header tests 687 | 688 | assert(test_request(&dumbfuck)); 689 | 690 | const char *dumbfuck2 = "GET / HTTP/1.1\r\nX-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n\tRA==\r\n\t-----END CERTIFICATE-----\r\n\r\n"; 691 | assert(test_error(dumbfuck2)); 692 | 693 | assert(test_request(&fragment_in_uri)); 694 | 695 | /* TODO sending junk and large headers gets rejected */ 696 | 697 | 698 | /* check to make sure our predefined requests are okay */ 699 | 700 | assert(test_request(&get_no_headers_no_body)); 701 | assert(test_request(&get_one_header_no_body)); 702 | assert(test_request(&get_no_headers_no_body)); 703 | 704 | // no content-length 705 | const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\nAccept: */*\r\nHELLO\r\n"; 706 | assert(test_error(bad_get_no_headers_no_body)); // error if there is a body without content length 707 | 708 | assert(test_request(&get_funky_content_length_body_hello)); 709 | assert(test_request(&post_identity_body_world)); 710 | assert(test_request(&post_chunked_all_your_base)); 711 | assert(test_request(&two_chunks_mult_zero_end)); 712 | assert(test_request(&chunked_w_trailing_headers)); 713 | 714 | assert(test_request(&chunked_w_bullshit_after_length)); 715 | assert(1 == requests[0].request.version_major); 716 | assert(1 == requests[0].request.version_minor); 717 | 718 | // three requests - no bodies 719 | assert( test_multiple3( &get_no_headers_no_body 720 | , &get_one_header_no_body 721 | , &get_no_headers_no_body 722 | )); 723 | 724 | // three requests - one body 725 | assert( test_multiple3(&get_no_headers_no_body, &get_funky_content_length_body_hello, &get_no_headers_no_body)); 726 | 727 | // three requests with bodies -- last is chunked 728 | assert( test_multiple3(&get_funky_content_length_body_hello, &post_identity_body_world, &post_chunked_all_your_base)); 729 | 730 | // three chunked requests 731 | assert( test_multiple3(&two_chunks_mult_zero_end, &post_chunked_all_your_base, &chunked_w_trailing_headers)); 732 | 733 | 734 | assert(test_scan2(&get_no_headers_no_body, &get_one_header_no_body, &get_no_headers_no_body)); 735 | assert(test_scan2(&get_funky_content_length_body_hello, &post_identity_body_world, &post_chunked_all_your_base)); 736 | assert(test_scan2(&two_chunks_mult_zero_end, &chunked_w_trailing_headers, &chunked_w_bullshit_after_length)); 737 | 738 | assert(test_scan3(&get_no_headers_no_body, &get_one_header_no_body, &get_no_headers_no_body)); 739 | assert(test_scan3(&get_funky_content_length_body_hello, &post_identity_body_world, &post_chunked_all_your_base)); 740 | assert(test_scan3(&two_chunks_mult_zero_end, &chunked_w_trailing_headers, &chunked_w_bullshit_after_length)); 741 | 742 | 743 | printf("okay\n"); 744 | return 0; 745 | } 746 | 747 | --------------------------------------------------------------------------------