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