feat: simple echoing server
							parent
							
								
									a163ee5155
								
							
						
					
					
						commit
						80e972ff54
					
				|  | @ -13,9 +13,6 @@ | |||
| #include "event_loop_internal.h" | ||||
| #include "picohttpparser.h" | ||||
| 
 | ||||
| const char http_200_ok[] = "HTTP/1.1 200 OK\n" | ||||
|                            "Connection: close\n"; | ||||
| 
 | ||||
| static int event_loop_fd_set_nb(int fd) { | ||||
|   int flags = fcntl(fd, F_GETFL, 0); | ||||
| 
 | ||||
|  | @ -101,6 +98,8 @@ int event_loop_accept(event_loop *loop, int fd) { | |||
|     return -4; | ||||
|   } | ||||
| 
 | ||||
|   /* printf("New connection with fd %i\n", connfd); */ | ||||
| 
 | ||||
|   return 0; | ||||
| } | ||||
| 
 | ||||
|  | @ -176,6 +175,7 @@ void event_loop_run(event_loop *el, int port) { | |||
|     // poll for active fds
 | ||||
|     // the timeout argument doesn't matter here
 | ||||
|     int rv = poll(poll_args, (nfds_t)poll_args_count, 1000); | ||||
|     /* printf("poll"); */ | ||||
| 
 | ||||
|     if (rv < 0) { | ||||
|       return; | ||||
|  | @ -188,6 +188,7 @@ void event_loop_run(event_loop *el, int port) { | |||
|         event_loop_conn_io(conn); | ||||
| 
 | ||||
|         if (conn->state == event_loop_conn_state_end) { | ||||
|           /* printf("free %i\n", conn->fd); */ | ||||
|           // client closed normally, or something bad happened.
 | ||||
|           // destroy this connection
 | ||||
|           el->connections[conn->fd] = NULL; | ||||
|  |  | |||
|  | @ -14,7 +14,9 @@ | |||
| 
 | ||||
| #include "event_loop_internal.h" | ||||
| 
 | ||||
| bool event_loop_conn_write_to_fd(event_loop_conn *conn) { | ||||
| const char http_200_ok[] = "HTTP/1.1 200 OK\nConnection: close\nContent-Length: 5\n\nhello"; | ||||
| 
 | ||||
| bool event_loop_conn_io_res(event_loop_conn *conn) { | ||||
|   ssize_t res = 0; | ||||
|   size_t remain = conn->wbuf_size - conn->wbuf_sent; | ||||
| 
 | ||||
|  | @ -36,9 +38,10 @@ bool event_loop_conn_write_to_fd(event_loop_conn *conn) { | |||
| 
 | ||||
|   conn->wbuf_sent += (size_t)res; | ||||
| 
 | ||||
|   // Everything is written from the buffer, so we exit
 | ||||
|   // After writing a response, we switch back to request mode to process a next
 | ||||
|   // request
 | ||||
|   if (conn->wbuf_sent == conn->wbuf_size) { | ||||
|     conn->state = event_loop_conn_state_end; | ||||
|     conn->state = conn->close_after_write ? event_loop_conn_state_end : event_loop_conn_state_req; | ||||
|     /* c->wbuf_sent = 0; */ | ||||
|     /* c->wbuf_size = 0; */ | ||||
| 
 | ||||
|  | @ -49,12 +52,12 @@ bool event_loop_conn_write_to_fd(event_loop_conn *conn) { | |||
|   return true; | ||||
| } | ||||
| 
 | ||||
| void try_one_request(event_loop_conn *conn) { | ||||
|   if (conn->process_func != NULL) { | ||||
|     conn->process_func(conn); | ||||
|   } | ||||
| bool try_one_request(event_loop_conn *conn) { | ||||
|   /* if (conn->process_func != NULL) { */ | ||||
|   /*   conn->process_func(conn); */ | ||||
|   /* } */ | ||||
| 
 | ||||
|   char *method, *path; | ||||
|   const char *method, *path; | ||||
|   struct phr_header headers[16]; | ||||
|   size_t method_len, path_len, num_headers; | ||||
|   int minor_version; | ||||
|  | @ -66,12 +69,38 @@ void try_one_request(event_loop_conn *conn) { | |||
|                               &minor_version, headers, &num_headers, 0); | ||||
| 
 | ||||
|   if (res > 0) { | ||||
|     // TODO allow HTTP pipelining
 | ||||
|     conn->close_after_write = true; | ||||
|     /* for (int i = 0; i < num_headers; i++) { */ | ||||
|     /*   printf("%.*s: ", headers[i].name_len, headers[i].name); */ | ||||
|     /*   printf("%.*s\n", headers[i].value_len, headers[i].value); */ | ||||
|     /*   if (strncmp("Connection", headers[i].name, headers[i].name_len) == 0) { */ | ||||
|     /*     if (strncmp("close", headers[i].value, headers[i].value_len) == 0) { */ | ||||
|     /*       printf("close\n"); */ | ||||
|     /*       break; */ | ||||
|     /*     } */ | ||||
|     /*   } */ | ||||
|     /* } */ | ||||
| 
 | ||||
|   } else if (res == -1) { | ||||
|     memcpy(conn->wbuf, http_200_ok, sizeof(http_200_ok) - 1); | ||||
| 
 | ||||
|     // Move the new request up to the front of the read buffer
 | ||||
|     memmove(conn->rbuf, &conn->rbuf[res], conn->rbuf_size - res); | ||||
|     conn->rbuf_size -= res; | ||||
| 
 | ||||
|     conn->state = event_loop_conn_state_res; | ||||
|     conn->wbuf_size = sizeof(http_200_ok) - 1; | ||||
|     conn->wbuf_sent = 0; | ||||
| 
 | ||||
|     event_loop_conn_io_res(conn); | ||||
| 
 | ||||
|     // End the connection if the http request is invalid, or when the response
 | ||||
|     // is too large to fit into a single read buffer
 | ||||
|   } else if (res == -1 || (res == -2 && conn->rbuf_size == EVENT_LOOP_BUFFER_SIZE)) { | ||||
|     conn->state = event_loop_conn_state_end; | ||||
|   } else if (res == -2) { | ||||
|     // We don't do anything here
 | ||||
|   } | ||||
| 
 | ||||
|   return conn->state == event_loop_conn_state_req; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -80,7 +109,7 @@ void try_one_request(event_loop_conn *conn) { | |||
|  * | ||||
|  * Returns whether the function should be retried immediately or not. | ||||
|  */ | ||||
| bool event_loop_conn_read_from_fd(event_loop_conn *conn) { | ||||
| bool event_loop_conn_io_req(event_loop_conn *conn) { | ||||
|   ssize_t res; | ||||
|   size_t cap = EVENT_LOOP_BUFFER_SIZE - conn->rbuf_size; | ||||
| 
 | ||||
|  | @ -101,8 +130,11 @@ bool event_loop_conn_read_from_fd(event_loop_conn *conn) { | |||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // An output of 0 zero means we've reached the end of the input
 | ||||
|   // Output of 0 and no error means we've reached the end of the input. We run
 | ||||
|   // try_one_request one more time and close the connection if this doesn't
 | ||||
|   // change the result.
 | ||||
|   if (res == 0) { | ||||
|     conn->state = event_loop_conn_state_end; | ||||
|   } | ||||
| 
 | ||||
|   // We switch to processing mode if we've reached the end of the data stream,
 | ||||
|  | @ -119,7 +151,7 @@ bool event_loop_conn_read_from_fd(event_loop_conn *conn) { | |||
|   // Try to process requests one by one.
 | ||||
|   // Try to process requests one by one.
 | ||||
|   // Why is there a loop? Please read the explanation of "pipelining".
 | ||||
|   try_one_request(conn); | ||||
|   while (try_one_request(conn)) {}; | ||||
| 
 | ||||
|   // We can keep reading as long as we're in request mode
 | ||||
|   return conn->state == event_loop_conn_state_req; | ||||
|  | @ -128,13 +160,14 @@ bool event_loop_conn_read_from_fd(event_loop_conn *conn) { | |||
| } | ||||
| 
 | ||||
| void conn_state_res(event_loop_conn *conn) { | ||||
|   while (event_loop_conn_write_to_fd(conn)) { | ||||
|   while (event_loop_conn_io_res(conn)) { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void conn_state_req(event_loop_conn *conn) { | ||||
|   while (event_loop_conn_read_from_fd(conn)) { | ||||
|   while (event_loop_conn_io_req(conn)) { | ||||
|   } | ||||
|   /* printf("done req"); */ | ||||
| } | ||||
| 
 | ||||
| void event_loop_conn_io(event_loop_conn *conn) { | ||||
|  | @ -145,5 +178,8 @@ void event_loop_conn_io(event_loop_conn *conn) { | |||
|   case event_loop_conn_state_res: | ||||
|     conn_state_res(conn); | ||||
|     break; | ||||
|   case event_loop_conn_state_end: | ||||
|     printf("we shouldn't be here\n"); | ||||
|     break; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <stdbool.h> | ||||
| 
 | ||||
| #include "event_loop.h" | ||||
| 
 | ||||
|  | @ -16,7 +17,11 @@ typedef struct event_loop_conn { | |||
|   size_t wbuf_size; | ||||
|   size_t wbuf_sent; | ||||
|   uint8_t wbuf[EVENT_LOOP_BUFFER_SIZE]; | ||||
|   void (*process_func)(struct event_loop_conn *); | ||||
| 
 | ||||
|   // If true, the server will close the connection after the final write buffer
 | ||||
|   // has been written
 | ||||
|   bool close_after_write; | ||||
|   /* void (*process_func)(struct event_loop_conn *); */ | ||||
| } event_loop_conn; | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue