├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── deps └── lwlog │ └── lwlog.h ├── start_build.sh ├── webserver.c └── www └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | /build 31 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.4) 2 | 3 | project(libuv_webserver) 4 | 5 | set(SOURCE_FILES webserver.c) 6 | 7 | include_directories(${PROJECT_SOURCE_DIR}/deps "/usr/local/include") 8 | 9 | link_directories("/usr/local/lib" "${PROJECT_SOURCE_DIR}/deps/http-parser") 10 | 11 | add_executable(libuv_webserver ${SOURCE_FILES}) 12 | 13 | target_link_libraries(libuv_webserver "libuv.a" "libhttp_parser.a") 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Bob Liu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | libuv-webserver 2 | =============== 3 | 4 | A lightweight webserver based on libuv and http-parser 5 | 6 | ## Programming Live Video 7 | * [Ryan Dahl's tutorial](http://vimeo.com/24713213) 8 | 9 | ## Good http serve on libuv 10 | * 11 | * 12 | 13 | ## Build & Run 14 | * `./start_build.sh` 15 | * `cd build` 16 | * `make` 17 | * `./libuv_webserver` 18 | * `curl http://127.0.0.1:8000/` 19 | 20 | ## Stress test 21 | * `ab -n 5000 -c 500 http://127.0.0.1:8000/` 22 | 23 | ## TODO 24 | * split code into tcp and http part 25 | * http part will transform between structs and http-parser output 26 | * check memory leaks on libuv part. 27 | * build a http response part for easy responses. 28 | * -------------------------------------------------------------------------------- /deps/lwlog/lwlog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * @file lwlog.h 3 | * @auther Akagi201 4 | * 5 | * @date 2014/11/30 6 | */ 7 | 8 | #ifndef LWLOG_H_ 9 | #define LWLOG_H_ (1) 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #ifndef LOG_LEVEL 16 | #define LOG_LEVEL (7) 17 | #endif 18 | 19 | #ifndef LOG_COLOR 20 | #define LOG_COLOR (1) 21 | #endif 22 | 23 | // log levels the same as syslog 24 | #define EMERG (0) 25 | #define ALERT (1) 26 | #define CRIT (2) 27 | #define ERR (3) 28 | #define WARNING (4) 29 | #define NOTICE (5) 30 | #define INFO (6) 31 | #define DEBUG (7) 32 | 33 | // colors 34 | #define NONE "\e[0m" 35 | #define BLACK "\e[0;30m" 36 | #define L_BLACK "\e[1;30m" 37 | #define RED "\e[0;31m" 38 | #define L_RED "\e[1;31m" 39 | #define GREEN "\e[0;32m" 40 | #define L_GREEN "\e[1;32m" 41 | #define BROWN "\e[0;33m" 42 | #define YELLOW "\e[1;33m" 43 | #define BLUE "\e[0;34m" 44 | #define L_BLUE "\e[1;34m" 45 | #define PURPLE "\e[0;35m" 46 | #define L_PURPLE "\e[1;35m" 47 | #define CYAN "\e[0;36m" 48 | #define L_CYAN "\e[1;36m" 49 | #define GRAY "\e[0;37m" 50 | #define WHITE "\e[1;37m" 51 | 52 | #define BOLD "\e[1m" 53 | #define UNDERLINE "\e[4m" 54 | #define BLINK "\e[5m" 55 | #define REVERSE "\e[7m" 56 | #define HIDE "\e[8m" 57 | #define CLEAR "\e[2J" 58 | #define CLRLINE "\r\e[K" //or "\e[1K\r" 59 | 60 | #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) 61 | 62 | /* safe readable version of errno */ 63 | #define clean_errno() (errno == 0 ? "None" : strerror(errno)) 64 | 65 | #define lwlog_emerg(M, ...) fprintf(stderr, RED "[EMERG] " "%s (%s:%d) " NONE M YELLOW " errno: %s\n" NONE, __func__, __FILE__, __LINE__, ##__VA_ARGS__, clean_errno()); 66 | #define lwlog_alert(M, ...) fprintf(stderr, PURPLE "[ALERT] " "%s (%s:%d) " NONE M YELLOW " errno: %s\n" NONE, __func__, __FILE__, __LINE__, ##__VA_ARGS__, clean_errno()); 67 | #define lwlog_crit(M, ...) fprintf(stderr, YELLOW "[CRIT] " "%s (%s:%d) " NONE M YELLOW " errno: %s\n" NONE, __func__, __FILE__, __LINE__, ##__VA_ARGS__, clean_errno()); 68 | #define lwlog_err(M, ...) fprintf(stderr, BROWN "[ERR] " "%s (%s:%d) " NONE M YELLOW " errno: %s\n" NONE, __func__, __FILE__, __LINE__, ##__VA_ARGS__, clean_errno()); 69 | #define lwlog_warning(M, ...) fprintf(stderr, BLUE "[WARNING] " "%s (%s:%d) " NONE M YELLOW " errno: %s\n" NONE, __func__, __FILE__, __LINE__, ##__VA_ARGS__, clean_errno()); 70 | #define lwlog_notice(M, ...) fprintf(stderr, CYAN "[NOTICE] " "%s (%s:%d) " NONE M YELLOW " errno: %s\n" NONE, __func__, __FILE__, __LINE__, ##__VA_ARGS__, clean_errno()); 71 | #define lwlog_info(M, ...) fprintf(stderr, GREEN "[INFO] " "%s (%s:%d) " NONE M "\n", __func__, __FILE__, __LINE__, ##__VA_ARGS__); 72 | #define lwlog_debug(M, ...) fprintf(stderr, GRAY "[DEBUG] " "%s (%s:%d) " NONE M "\n", __func__, __FILE__, __LINE__, ##__VA_ARGS__); 73 | 74 | /* LOG_LEVEL controls */ 75 | #if LOG_LEVEL < DEBUG 76 | #undef lwlog_debug 77 | #define lwlog_debug(M, ...) do{}while(0) 78 | #endif 79 | 80 | #if LOG_LEVEL < INFO 81 | #undef lwlog_info 82 | #define lwlog_info(M, ...) do{}while(0) 83 | #endif 84 | 85 | #if LOG_LEVEL < NOTICE 86 | #undef lwlog_notice 87 | #define lwlog_notice(M, ...) do{}while(0) 88 | #endif 89 | 90 | #if LOG_LEVEL < WARNING 91 | #undef lwlog_warning 92 | #define lwlog_warning(M, ...) do{}while(0) 93 | #endif 94 | 95 | #if LOG_LEVEL < ERR 96 | #undef lwlog_err 97 | #define lwlog_err(M, ...) do{}while(0) 98 | #endif 99 | 100 | #if LOG_LEVEL < CRIT 101 | #undef lwlog_crit 102 | #define lwlog_crit(M, ...) do{}while(0) 103 | #endif 104 | 105 | #if LOG_LEVEL < ALERT 106 | #undef lwlog_alert 107 | #define lwlog_alert(M, ...) do{}while(0) 108 | #endif 109 | 110 | #if LOG_LEVEL < EMERG 111 | #undef lwlog_emerg 112 | #define lwlog_emerg(M, ...) do{}while(0) 113 | #endif 114 | 115 | /* LOG_COLOR controls */ 116 | #if LOG_COLOR < 1 117 | 118 | #undef NONE 119 | #define NONE 120 | 121 | #undef RED 122 | #define RED 123 | 124 | #undef PURPLE 125 | #define PURPLE 126 | 127 | #undef YELLOW 128 | #define YELLOW 129 | 130 | #undef BROWN 131 | #define BROWN 132 | 133 | #undef GREEN 134 | #define GREEN 135 | 136 | #undef CYAN 137 | #define CYAN 138 | 139 | #undef BLUE 140 | #define BLUE 141 | 142 | #undef GRAY 143 | #define GRAY 144 | 145 | #endif 146 | 147 | #endif // LWLOG_H_ 148 | -------------------------------------------------------------------------------- /start_build.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | if test -d build ; 4 | then rm -rf build 5 | fi 6 | mkdir build 7 | 8 | cd build 9 | cmake .. 10 | #cmake .. && make -------------------------------------------------------------------------------- /webserver.c: -------------------------------------------------------------------------------- 1 | /* 2 | * @file webserver.c 3 | * @author Akagi201 4 | * @date 2015/01/03 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include // _SC_NPROCESSORS_ONLN on OS X 12 | #include "uv.h" 13 | #include "http-parser/http_parser.h" 14 | #include "lwlog/lwlog.h" 15 | 16 | #define HTTP_PORT (8000) 17 | #define MAX_WRITE_HANDLES (1000) 18 | #define INDEX_HTML "../www/index.html" 19 | 20 | #define HTTP_HEADER "HTTP/1.1 200 OK\r\n" \ 21 | "Content-Type: text/html\r\n" \ 22 | "\r\n" 23 | 24 | #define MAX_HTTP_HEADERS (20) 25 | 26 | #define UV_ERR(err, msg) lwlog_err("%s: [%s(%d): %s]\n", msg, uv_err_name((err)), (int)err, uv_strerror((err))) 27 | 28 | #define UV_CHECK(err, msg) \ 29 | do { \ 30 | if (err != 0) { \ 31 | UV_ERR(err, msg); \ 32 | exit(1); \ 33 | } \ 34 | } while(0) 35 | 36 | /** 37 | * Represents a single http header. 38 | */ 39 | typedef struct { 40 | char *field; 41 | char *value; 42 | size_t field_length; 43 | size_t value_length; 44 | } http_header_t; 45 | 46 | /** 47 | * Represents a http request with internal dependencies. 48 | * 49 | * - write request for sending the response 50 | * - reference to tcp socket as write stream 51 | * - instance of http_parser parser 52 | * - string of the http url 53 | * - string of the http method 54 | * - amount of total header lines 55 | * - http header array 56 | * - body content 57 | */ 58 | typedef struct { 59 | uv_write_t req; 60 | uv_stream_t stream; 61 | http_parser parser; 62 | char *url; 63 | char *method; 64 | int header_lines; 65 | http_header_t headers[MAX_HTTP_HEADERS]; 66 | char *body; 67 | size_t body_length; 68 | uv_buf_t resp_buf[2]; 69 | } http_request_t; 70 | 71 | static uv_loop_t *uv_loop; 72 | static uv_tcp_t server; 73 | static http_parser_settings parser_settings; 74 | 75 | void on_close(uv_handle_t *handle) { 76 | 77 | http_request_t *http_request = (http_request_t *) handle->data; 78 | 79 | lwlog_info("connection closed"); 80 | 81 | if (NULL != http_request) { 82 | free(http_request); 83 | http_request = NULL; 84 | } 85 | 86 | return; 87 | } 88 | 89 | void alloc_cb(uv_handle_t *handle/*handle*/, size_t suggested_size, uv_buf_t *buf) { 90 | *buf = uv_buf_init((char *) malloc(suggested_size), (unsigned int)suggested_size); 91 | 92 | return; 93 | } 94 | 95 | void on_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { 96 | ssize_t parsed = 0; 97 | lwlog_info("on read, nread: %ld", nread); 98 | 99 | /* get back our http request*/ 100 | http_request_t *http_request = stream->data; 101 | 102 | if (nread >= 0) { 103 | /* call our http parser on the received tcp payload */ 104 | parsed = (ssize_t) http_parser_execute( 105 | &http_request->parser, &parser_settings, buf->base, (size_t)nread); 106 | if (parsed < nread) { 107 | lwlog_err("parse error"); 108 | uv_close((uv_handle_t *) &http_request->stream, on_close); 109 | } 110 | } else { 111 | if (nread != UV_EOF) { 112 | UV_ERR(nread, "Read error"); 113 | } 114 | uv_close((uv_handle_t *) &http_request->stream, on_close); 115 | } 116 | 117 | if (NULL != buf->base) { 118 | free(buf->base); 119 | } 120 | 121 | return; 122 | } 123 | 124 | /** 125 | * Initializes default values, counters. 126 | */ 127 | int on_message_begin(http_parser *parser/*parser*/) { 128 | lwlog_info("***MESSAGE BEGIN***"); 129 | http_request_t *http_request = parser->data; 130 | http_request->header_lines = 0; 131 | 132 | return 0; 133 | } 134 | 135 | /** 136 | * Extract the method name. 137 | */ 138 | int on_headers_complete(http_parser *parser/*parser*/) { 139 | lwlog_info("***HEADERS COMPLETE***"); 140 | 141 | http_request_t *http_request = parser->data; 142 | 143 | const char *method = http_method_str((enum http_method)parser->method); 144 | 145 | http_request->method = malloc(sizeof(method)); 146 | strncpy(http_request->method, method, strlen(method)); 147 | 148 | return 0; 149 | } 150 | 151 | /** 152 | * Copies url string to http_request->url. 153 | */ 154 | int on_url(http_parser *parser/*parser*/, const char *at, size_t length) { 155 | lwlog_info("Url: %.*s", (int) length, at); 156 | 157 | http_request_t *http_request = parser->data; 158 | 159 | http_request->url = malloc(length + 1); 160 | 161 | strncpy(http_request->url, at, length); 162 | 163 | http_request->url[length] = '\0'; 164 | 165 | return 0; 166 | } 167 | 168 | /** 169 | * Copy the header field name to the current header item. 170 | */ 171 | int on_header_field(http_parser *parser/*parser*/, const char *at, size_t length) { 172 | lwlog_info("Header field: %.*s", (int) length, at); 173 | 174 | http_request_t *http_request = parser->data; 175 | 176 | http_header_t *header = &http_request->headers[http_request->header_lines]; 177 | 178 | header->field = malloc(length + 1); 179 | header->field_length = length; 180 | 181 | strncpy(header->field, at, length); 182 | 183 | header->field[length] = '\0'; 184 | 185 | return 0; 186 | } 187 | 188 | /** 189 | * Now copy its assigned value. 190 | */ 191 | int on_header_value(http_parser *parser/*parser*/, const char *at, size_t length) { 192 | lwlog_info("Header value: %.*s", (int) length, at); 193 | 194 | http_request_t *http_request = parser->data; 195 | 196 | http_header_t *header = &http_request->headers[http_request->header_lines]; 197 | 198 | header->value = malloc(length + 1); 199 | header->value_length = length; 200 | 201 | strncpy(header->value, at, length); 202 | 203 | header->value[length] = '\0'; 204 | 205 | ++http_request->header_lines; 206 | 207 | return 0; 208 | } 209 | 210 | int on_body(http_parser *parser/*parser*/, const char *at, size_t length) { 211 | lwlog_info("Body: %.*s", (int) length, at); 212 | http_request_t *http_request = parser->data; 213 | 214 | http_request->body = malloc(length + 1); 215 | http_request->body_length = length; 216 | 217 | strncpy(http_request->body, at, length); 218 | 219 | http_request->body[length] = '\0'; 220 | 221 | return 0; 222 | } 223 | 224 | /** 225 | * Closes current tcp socket after write. 226 | */ 227 | void on_put_write(uv_write_t *req, int status) { 228 | int i = 0; 229 | http_header_t *header = NULL; 230 | http_request_t *http_request = req->data; 231 | 232 | UV_CHECK(status, "on_put_write"); 233 | 234 | if (http_request->url != NULL) { 235 | free(http_request->url); 236 | http_request->url = NULL; 237 | } 238 | 239 | if (http_request->body != NULL) { 240 | free(http_request->body); 241 | http_request->body = NULL; 242 | } 243 | 244 | if (http_request->method != NULL) { 245 | free(http_request->method); 246 | http_request->method = NULL; 247 | } 248 | 249 | for (i = 0; i < http_request->header_lines; ++i) { 250 | header = &http_request->headers[i]; 251 | if (header->field != NULL) { 252 | free(header->field); 253 | header->field = NULL; 254 | } 255 | if (header->value != NULL) { 256 | free(header->value); 257 | header->value = NULL; 258 | } 259 | } 260 | 261 | if (!uv_is_closing((uv_handle_t*)req->handle)) { 262 | uv_close((uv_handle_t *) req->handle, on_close); 263 | } 264 | 265 | return; 266 | } 267 | 268 | /** 269 | * Closes current tcp socket after write. 270 | */ 271 | void on_get_write(uv_write_t *req, int status){ 272 | int i = 0; 273 | http_header_t *header = NULL; 274 | http_request_t *http_request = req->data; 275 | UV_CHECK(status, "on_get_write"); 276 | 277 | if (http_request->url != NULL) { 278 | free(http_request->url); 279 | http_request->url = NULL; 280 | } 281 | 282 | if (http_request->method != NULL) { 283 | free(http_request->method); 284 | http_request->method = NULL; 285 | } 286 | 287 | for (i = 0; i < http_request->header_lines; ++i) { 288 | header = &http_request->headers[i]; 289 | if (header->field != NULL) { 290 | free(header->field); 291 | header->field = NULL; 292 | } 293 | if (header->value != NULL) { 294 | free(header->value); 295 | header->value = NULL; 296 | } 297 | } 298 | 299 | if (!uv_is_closing((uv_handle_t*)req->handle)) { 300 | uv_close((uv_handle_t *) req->handle, on_close); 301 | } 302 | 303 | return; 304 | } 305 | 306 | void on_html_write(uv_write_t *req, int status) { 307 | char *buf = NULL; 308 | int i = 0; 309 | http_header_t *header = NULL; 310 | http_request_t *http_request = req->data; 311 | buf = http_request->resp_buf[1].base; 312 | 313 | UV_CHECK(status, "on_html_write"); 314 | 315 | if (NULL != buf) { 316 | free(buf); 317 | buf = NULL; 318 | } 319 | 320 | if (http_request->url != NULL) { 321 | free(http_request->url); 322 | http_request->url = NULL; 323 | } 324 | 325 | if (http_request->method != NULL) { 326 | free(http_request->method); 327 | http_request->method = NULL; 328 | } 329 | 330 | for (i = 0; i < http_request->header_lines; ++i) { 331 | header = &http_request->headers[i]; 332 | if (header->field != NULL) { 333 | free(header->field); 334 | header->field = NULL; 335 | } 336 | if (header->value != NULL) { 337 | free(header->value); 338 | header->value = NULL; 339 | } 340 | } 341 | 342 | if (!uv_is_closing((uv_handle_t*)req->handle)) { 343 | uv_close((uv_handle_t *) req->handle, on_close); 344 | } 345 | 346 | return; 347 | } 348 | 349 | int on_message_complete(http_parser *parser) { 350 | int i = 0; 351 | http_header_t *header = NULL; 352 | 353 | lwlog_info("***MESSAGE COMPLETE***"); 354 | 355 | http_request_t *http_request = parser->data; 356 | 357 | /* now print the ordered http http_request to console */ 358 | printf("url: %s\n", http_request->url); 359 | printf("method: %s\n", http_request->method); 360 | for (i = 0; i < 5; ++i) { 361 | header = &http_request->headers[i]; 362 | if (header->field) { 363 | printf("Header: %s: %s\n", header->field, header->value); 364 | } 365 | } 366 | printf("body: %s\n", http_request->body); 367 | printf("\n"); 368 | 369 | if (0 == strcmp(http_request->url, "/")) { 370 | lwlog_info("root"); 371 | char *file_contents; 372 | long input_file_size; 373 | 374 | // send http response header 375 | http_request->resp_buf[0].base = HTTP_HEADER; 376 | http_request->resp_buf[0].len = sizeof(HTTP_HEADER) - 1; 377 | 378 | // send http response body 379 | FILE *input_file = fopen(INDEX_HTML, "rb"); 380 | fseek(input_file, 0, SEEK_END); 381 | input_file_size = ftell(input_file); 382 | rewind(input_file); 383 | file_contents = malloc(input_file_size * (sizeof(char))); 384 | fread(file_contents, sizeof(char), (size_t)input_file_size, input_file); 385 | fclose(input_file); 386 | 387 | lwlog_info("input_file_size: %ld", input_file_size); 388 | 389 | http_request->resp_buf[1].base = file_contents; 390 | http_request->resp_buf[1].len = (size_t)input_file_size; 391 | 392 | /* lets send our short http hello world response and close the socket */ 393 | uv_write(&http_request->req, &http_request->stream, http_request->resp_buf, 2, on_html_write); 394 | } else if (0 == strcmp(http_request->url, "/favicon.ico")) { 395 | lwlog_info("favicon"); 396 | http_request->resp_buf[0].base = HTTP_HEADER; 397 | http_request->resp_buf[0].len = sizeof(HTTP_HEADER) - 1; 398 | /* lets send our short http hello world response and close the socket */ 399 | uv_write(&http_request->req, &http_request->stream, http_request->resp_buf, 1, on_get_write); 400 | } else { 401 | lwlog_err("undefined url: %s", http_request->url); 402 | http_request->resp_buf[0].base = HTTP_HEADER; 403 | http_request->resp_buf[0].len = sizeof(HTTP_HEADER) - 1; 404 | /* lets send our short http hello world response and close the socket */ 405 | uv_write(&http_request->req, &http_request->stream, http_request->resp_buf, 1, on_get_write); 406 | } 407 | 408 | return 0; 409 | } 410 | 411 | void on_connect(uv_stream_t *server_handle, int status) { 412 | int ret = 0; 413 | int i = 0; 414 | UV_CHECK(status, "connect"); 415 | assert((uv_tcp_t *) server_handle == &server); 416 | 417 | /* initialize a new http http_request struct */ 418 | http_request_t *http_request = malloc(sizeof(http_request_t)); 419 | 420 | /* create an extra tcp handle for the http_request */ 421 | uv_tcp_init(uv_loop, (uv_tcp_t *) &http_request->stream); 422 | 423 | /* set references so we can use our http_request in http_parser and libuv */ 424 | http_request->stream.data = http_request; 425 | http_request->parser.data = http_request; 426 | http_request->req.data = http_request; 427 | 428 | /* accept the created http_request */ 429 | ret = uv_accept(server_handle, &http_request->stream); 430 | if (0 == ret) { 431 | http_request->url = NULL; 432 | http_request->method = NULL; 433 | http_request->body = NULL; 434 | http_request->header_lines = 0; 435 | for (i = 0; i < MAX_HTTP_HEADERS; ++i) { 436 | http_request->headers[i].field = NULL; 437 | http_request->headers[i].field_length = 0; 438 | http_request->headers[i].value = NULL; 439 | http_request->headers[i].value_length = 0; 440 | } 441 | /* initialize our http parser */ 442 | http_parser_init(&http_request->parser, HTTP_REQUEST); 443 | /* start reading from the tcp http_request socket */ 444 | uv_read_start(&http_request->stream, alloc_cb, on_read); 445 | } else { 446 | /* we seem to have an error and quit */ 447 | uv_close((uv_handle_t *) &http_request->stream, on_close); 448 | //UV_CHECK(ret, "accept"); 449 | } 450 | 451 | return; 452 | } 453 | 454 | int main(int argc, char *argv[]) { 455 | long cores = 0; 456 | char cores_string[10] = {0}; 457 | int ret = 0; 458 | struct sockaddr_in address; 459 | 460 | signal(SIGPIPE, SIG_IGN); 461 | 462 | cores = sysconf(_SC_NPROCESSORS_ONLN); 463 | lwlog_info("Number of available CPU cores %ld", cores); 464 | snprintf(cores_string, sizeof(cores_string), "%ld", cores); 465 | setenv("UV_THREADPOOL_SIZE", cores_string, 1); 466 | 467 | parser_settings.on_message_begin = on_message_begin; 468 | parser_settings.on_url = on_url; 469 | parser_settings.on_header_field = on_header_field; 470 | parser_settings.on_header_value = on_header_value; 471 | parser_settings.on_headers_complete = on_headers_complete; 472 | parser_settings.on_body = on_body; 473 | parser_settings.on_message_complete = on_message_complete; 474 | 475 | uv_loop = uv_default_loop(); 476 | 477 | ret = uv_tcp_init(uv_loop, &server); 478 | UV_CHECK(ret, "tcp_init"); 479 | 480 | //ret = uv_tcp_keepalive(&server, 1, 60); 481 | //UV_CHECK(ret, "tcp_keepalive"); 482 | 483 | ret = uv_ip4_addr("0.0.0.0", HTTP_PORT, &address); 484 | UV_CHECK(ret, "ip4_addr"); 485 | 486 | ret = uv_tcp_bind(&server, (const struct sockaddr *) &address, 0); 487 | UV_CHECK(ret, "tcp_bind"); 488 | 489 | ret = uv_listen((uv_stream_t *) &server, MAX_WRITE_HANDLES, on_connect); 490 | UV_CHECK(ret, "uv_listen"); 491 | 492 | lwlog_info("Listening on port %d", HTTP_PORT); 493 | 494 | uv_run(uv_loop, UV_RUN_DEFAULT); 495 | 496 | return 0; 497 | } 498 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Libuv Webserver 5 | 6 | 7 | 8 | 9 | This is a lightweight http server based on libuv and http-parser. 10 | 11 | 12 | --------------------------------------------------------------------------------