From e82f9cb23e3db79c2e9bb5e476a6683d0578ede7 Mon Sep 17 00:00:00 2001 From: "dana_maria.caruntu" <dana_maria.caruntu@stud.acs.upb.ro> Date: Wed, 15 Jan 2025 22:13:33 +0200 Subject: [PATCH] final version (89p) --- src/aws.c | 452 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 237 insertions(+), 215 deletions(-) diff --git a/src/aws.c b/src/aws.c index c29fb41..4968b48 100644 --- a/src/aws.c +++ b/src/aws.c @@ -26,9 +26,6 @@ #define MAX_EVENTS 1024 -#define SERVER_PORT AWS_LISTEN_PORT -#define LISTEN_BACKLOG DEFAULT_LISTEN_BACKLOG - #define HTTP_404_RESPONSE \ "HTTP/1.1 404 Not Found\r\n" \ @@ -39,12 +36,9 @@ #define HTTP_200_RESPONSE \ "HTTP/1.1 200 OK\r\n" \ "Content-Type: text/html\r\n" \ - "Content-Length: %ld\r\n" \ + "Content-Length: %zu\r\n" \ "\r\n" -#define END_OF_HEADER "\r\n\r\n" -#define LENGTH_END_OF_HEADER 4 - /* server socket file descriptor */ @@ -69,12 +63,12 @@ static int aws_on_path_cb(http_parser *p, const char *buf, size_t len) static void connection_prepare_send_reply_header(struct connection *conn) { - /* TODO: Prepare the connection buffer to send the reply header. */ DIE(conn == NULL, "connection is NULL"); struct stat st; int rc = fstat(conn->fd, &st); + DIE(rc < 0, "fstat failed"); // initialize fields to prepare sending the file @@ -85,58 +79,59 @@ static void connection_prepare_send_reply_header(struct connection *conn) memset(conn->send_buffer, 0, sizeof(conn->send_buffer)); conn->send_len = snprintf(conn->send_buffer, sizeof(conn->send_buffer), HTTP_200_RESPONSE, conn->file_size); - //check for snprintf overflow - DIE(conn->send_len < 0 || conn->send_len >= (int)sizeof(conn->send_buffer), "snprintf overflow"); - // update the state to sending header conn->state = STATE_SENDING_HEADER; // for debugging purposes dlog(LOG_DEBUG, "Preparing to send header:\n%s", conn->send_buffer); - dlog(LOG_DEBUG, "File size: %ld bytes\n", conn->file_size); + dlog(LOG_DEBUG, "File size: %zu bytes\n", conn->file_size); } static void connection_prepare_send_404(struct connection *conn) { - /* TODO: Prepare the connection buffer to send the 404 header. */ DIE(conn == NULL, "connection is NULL"); - // make sure the file descriptor is closed + // Make sure the file descriptor is closed and reset file size conn->fd = -1; conn->file_size = 0; - // prepare the buffer to send the 404 header + // Clear the send buffer for a fresh response memset(conn->send_buffer, 0, sizeof(conn->send_buffer)); + // Format the HTTP 404 response into the send buffer int written = snprintf(conn->send_buffer, sizeof(conn->send_buffer), "%s", HTTP_404_RESPONSE); DIE(written < 0 || written >= (int)sizeof(conn->send_buffer), "snprintf overflow"); - // update the send length and set the state to sending 404 + // Update the send length based on the 404 response length conn->send_len = strlen(HTTP_404_RESPONSE); - // set the state to client error + + // Attempt to send the 404 response immediately int bytes_sent = send(conn->sockfd, conn->send_buffer, conn->send_len, 0); + DIE(bytes_sent < 0, "Failed to send 404 response"); - // for debugging purposes - dlog(LOG_DEBUG, "Prepared 404 Not Found response for sockfd %d. Message length: %d bytes.\n", conn->sockfd, conn->send_len); + // Log the preparation of the 404 response for debugging + dlog(LOG_DEBUG, "Prepared 404 Not Found response for sockfd %d. Message length: %zu bytes.\n", + conn->sockfd, conn->send_len); - // set the state to sending 404 + // Update connection state to indicate sending a 404 response conn->state = STATE_SENDING_404; } static enum resource_type connection_get_resource_type(struct connection *conn) { - /* Determine the type of resource based on the request path. */ + /* TODO: Get resource type depending on request path/filename. Filename should + * point to the static or dynamic folder. + */ int resource_type = 0; - if (strstr(conn->request_path, "static")) { + if (strstr(conn->request_path, "static")) resource_type = 1; - } else if (strstr(conn->request_path, "dynamic")) { + else if (strstr(conn->request_path, "dynamic")) resource_type = 2; - } switch (resource_type) { case 1: @@ -150,6 +145,8 @@ static enum resource_type connection_get_resource_type(struct connection *conn) struct connection *connection_create(int sockfd) { + /* TODO: Initialize connection structure on given socket. */ + // Allocate and initialize a new connection structure struct connection *connection = calloc(1, sizeof(struct connection)); @@ -178,33 +175,41 @@ struct connection *connection_create(int sockfd) connection->send_len = 0; connection->send_pos = 0; connection->file_pos = 0; - connection->async_read_len = 0; connection->file_size = 0; connection->have_path = 0; return connection; } - - void connection_start_async_io(struct connection *conn) { /* TODO: Start asynchronous operation (read from file). * Use io_submit(2) & friends for reading data asynchronously. */ + + // Create a non-blocking eventfd for asynchronous notifications. conn->eventfd = eventfd(0, EFD_NONBLOCK); DIE(conn->eventfd < 0, "eventfd creation failed"); + // Prepare an asynchronous pread operation: + // Read from conn->fd into conn->send_buffer, up to its size, starting at conn->file_pos. io_prep_pread(&conn->iocb, conn->fd, conn->send_buffer, sizeof(conn->send_buffer), conn->file_pos); + + // Associate the eventfd with the I/O control block for notification. io_set_eventfd(&conn->iocb, conn->eventfd); + // Create an array of I/O control blocks for submission. struct iocb *cbs[1] = { &conn->iocb }; + // Submit the asynchronous read request to the AIO context. int ret = io_submit(ctx, 1, cbs); + DIE(ret < 0, "io_submit failed"); + // Add the eventfd to the epoll instance to be notified when the read completes. DIE(w_epoll_add_ptr_in(epollfd, conn->eventfd, conn) < 0, "failed to add eventfd to epoll"); + // Update the connection state to indicate an asynchronous operation is ongoing. conn->state = STATE_ASYNC_ONGOING; } @@ -220,7 +225,7 @@ void connection_remove(struct connection *conn) } struct new_client_info { - // temporary structure to hold information about the new client + // Temporary structure to hold information about the new client int sockfd; // Socket file descriptor for the client struct sockaddr_in addr; // Client address socklen_t addr_len; // Length of the client address structure @@ -228,32 +233,40 @@ struct new_client_info { void handle_new_connection(void) { + /* TODO: Handle a new connection request on the server socket. */ + // Temporary structure to hold information about the new client struct new_client_info client_info = {0}; + client_info.addr_len = sizeof(client_info.addr); - // Accept the new connection and store the socket file descriptor + /* TODO: Accept new connection. */ client_info.sockfd = accept(listenfd, (struct sockaddr *)&client_info.addr, &client_info.addr_len); + DIE(client_info.sockfd < 0, "Failed to accept connection"); - // Configure the client socket as non-blocking + /* TODO: Set socket to be non-blocking. */ int flags = fcntl(client_info.sockfd, F_GETFL, 0); + DIE(flags < 0, "Failed to get socket flags"); DIE(fcntl(client_info.sockfd, F_SETFL, flags | O_NONBLOCK) < 0, "Failed to set socket to non-blocking mode"); - // Create a new connection object for the client + /* TODO: Instantiate new connection handler. */ struct connection *new_connection = connection_create(client_info.sockfd); - // Add the socket to the epoll instance for monitoring + /* TODO: Add socket to epoll. */ DIE(w_epoll_add_ptr_in(epollfd, client_info.sockfd, new_connection) < 0, "Failed to add socket to epoll"); - // Initialize the HTTP request parser and link it to the connection + /* TODO: Initialize HTTP_REQUEST parser. */ http_parser_init(&new_connection->request_parser, HTTP_REQUEST); new_connection->request_parser.data = new_connection; } void receive_data(struct connection *conn) -{ +{ + /* TODO: Receive message on socket. + * Store message in recv_buffer in struct connection. + */ // Set the initial state indicating that data is being received conn->state = STATE_RECEIVING_DATA; bool error = false; @@ -293,10 +306,9 @@ void receive_data(struct connection *conn) DIE(w_epoll_update_ptr_out(epollfd, conn->sockfd, conn) < 0, "w_epoll_update_ptr_out"); } - - int connection_open_file(struct connection *conn) { + /* TODO: Open file and update connection fields. */ if (conn == NULL) return -1; @@ -305,17 +317,16 @@ int connection_open_file(struct connection *conn) if (conn->res_type == RESOURCE_TYPE_NONE) return -1; - const char* base_path = NULL; - const char* relative_path = conn->request_path; + const char *base_path = NULL; + const char *relative_path = conn->request_path; // Set base path and adjust relative path for static resources if (conn->res_type == RESOURCE_TYPE_STATIC) { base_path = AWS_ABS_STATIC_FOLDER; if (strncmp(relative_path, "/static/", 8) == 0) relative_path += 8; - } - // Set base path and adjust relative path for dynamic resources - else if (conn->res_type == RESOURCE_TYPE_DYNAMIC) { + } else if (conn->res_type == RESOURCE_TYPE_DYNAMIC) { + // Set base path and adjust relative path for dynamic resources base_path = AWS_ABS_DYNAMIC_FOLDER; if (strncmp(relative_path, "/dynamic/", 9) == 0) relative_path += 9; @@ -324,6 +335,7 @@ int connection_open_file(struct connection *conn) // Construct full path to the requested file char full_path[BUFSIZ]; int written = snprintf(full_path, sizeof(full_path), "%s%s", base_path, relative_path); + if (written < 0 || (size_t)written >= sizeof(full_path)) return -1; @@ -335,27 +347,24 @@ int connection_open_file(struct connection *conn) return 0; } - - void connection_complete_async_io(struct connection *conn) { + /* TODO: Complete asynchronous operation; operation returns successfully. + * Prepare socket for sending. + */ struct io_event event; int ret; uint64_t val; - /* 1. "Drain" the eventfd by reading the counter, which resets the signal. */ + // Read from eventfd to clear the event notification ret = read(conn->eventfd, &val, sizeof(val)); if (ret < 0 && errno != EAGAIN) { perror("read eventfd"); - /* - * If this is a real error (not just EAGAIN), we can mark this connection - * as failed, e.g., by sending a 404 response. - */ conn->state = STATE_SENDING_404; return; } - /* 2. Retrieve the result of the asynchronous operation with io_getevents. */ + // Get the result of the completed asynchronous read ret = io_getevents(conn->ctx, 1, 1, &event, NULL); if (ret < 0) { perror("io_getevents"); @@ -363,53 +372,35 @@ void connection_complete_async_io(struct connection *conn) return; } - /* 3. Check how many bytes were actually read. */ + // Check how many bytes were read in this async operation ssize_t bytes_read = (ssize_t) event.res; + if (bytes_read < 0) { - /* - * An error occurred during the async read. - * We can mark this connection to send a 404 or handle otherwise. - */ conn->state = STATE_SENDING_404; return; } - /* - * 4. Update the fields in the connection structure. - * - send_len: how many bytes we have available to send (the chunk just read). - * - send_pos: the current offset in send_buffer (reset to 0). - * - file_pos: how far we've advanced in the file. - */ - conn->send_len = (size_t) bytes_read; - conn->send_pos = 0; - conn->file_pos += (size_t) bytes_read; - - /* - * 5. Decide if there's more data to read from the file or we've reached the end. - * If bytes_read == 0 or file_pos >= file_size, it means we've reached EOF. - */ + // Update connection fields with the new data read + conn->send_len = (size_t) bytes_read; // Amount of new data to send + conn->send_pos = 0; // Reset sending position + conn->file_pos += (size_t) bytes_read; // Advance file position + + // Determine if we've reached the end of the file if (bytes_read == 0 || conn->file_pos >= conn->file_size) { - /* We have finished reading the file. Remove eventfd from epoll and close it. */ + // No more data to read: clean up eventfd and mark as done w_epoll_remove_ptr(epollfd, conn->eventfd, conn); close(conn->eventfd); conn->eventfd = -1; - - /* Mark the connection as having finished sending all data. */ conn->state = STATE_DATA_SENT; } else { - /* There is still more data left to be read from the file. */ + // More data remains to be read conn->state = STATE_SENDING_DATA; } - /* - * 6. Switch the client socket (sockfd) to EPOLLOUT so we can - * send the data that we've just read into send_buffer. - */ + // Switch the socket to writable so we can send the read data to the client w_epoll_update_ptr_out(epollfd, conn->sockfd, conn); } - - int parse_header(struct connection *conn) { /* TODO: Parse the HTTP header and extract the file path. */ @@ -426,12 +417,11 @@ int parse_header(struct connection *conn) .on_headers_complete = 0, .on_message_complete = 0 }; - + // Associate the connection structure with the parser so callbacks can access connection context conn->request_parser.data = conn; + // Execute the parser on the received data to extract the path http_parser_execute(&conn->request_parser, &settings_on_path, conn->recv_buffer, conn->recv_len); - - return 0; } @@ -441,137 +431,161 @@ enum connection_state connection_send_static(struct connection *conn) if (!conn || conn->fd < 0 || conn->sockfd < 0) return STATE_CONNECTION_CLOSED; + // Set the starting offset and compute how many bytes remain to send. off_t offset = conn->file_pos; size_t remaining = conn->file_size - conn->file_pos; + // Attempt to send remaining file data using zero-copy sendfile. ssize_t bytes_sent = sendfile(conn->sockfd, conn->fd, &offset, remaining); if (bytes_sent < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { + // If it's a temporary issue, remain in sending state. + if (errno == EAGAIN || errno == EWOULDBLOCK) return STATE_SENDING_DATA; - } else { - // dlog(LOG_ERROR, "sendfile error: %s\n", strerror(errno)); - return STATE_CONNECTION_CLOSED; - } + // On other errors, close the connection. + return STATE_CONNECTION_CLOSED; } + // Update the connection's file position based on bytes sent. conn->file_pos = offset; + // If we've sent the entire file, indicate that data has been fully sent. if (conn->file_pos >= conn->file_size) return STATE_DATA_SENT; + // Otherwise, more data remains to be sent. return STATE_SENDING_DATA; - // return STATE_NO_STATE; } int connection_send_data(struct connection *conn) { - /* This helper function sends as much data as possible from the connection's send buffer. - * It returns the total number of bytes sent, or -1 if an error occurs. - */ - + /* May be used as a helper function. */ + /* TODO: Send as much data as possible from the connection send buffer. + * Returns the number of bytes sent or -1 if an error occurred + */ + // Validate connection and socket descriptor if (conn == NULL || conn->sockfd < 0) return -1; - dlog(LOG_DEBUG, "connection_send_data: initial send_pos=%zu, send_len=%zu\n", - conn->send_pos, conn->send_len); + dlog(LOG_DEBUG, + "%s: initial send_pos=%zu, send_len=%zu\n", + __func__, conn->send_pos, conn->send_len); ssize_t total_bytes_sent = 0; + // Loop until all data in the buffer is sent or we would block while (conn->send_pos < conn->send_len) { size_t remaining = conn->send_len - conn->send_pos; + // Attempt to send remaining data ssize_t bytes_sent = send(conn->sockfd, conn->send_buffer + conn->send_pos, remaining, 0); + // Handle non-blocking conditions and errors if (bytes_sent < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) { - dlog(LOG_DEBUG, "connection_send_data: would block now, break.\n"); + dlog(LOG_DEBUG, "%s: would block now, break.\n", __func__); break; } - dlog(LOG_ERR, "connection_send_data: send error: %s\n", strerror(errno)); + dlog(LOG_ERR, "%s: send error: %s\n", __func__, strerror(errno)); return -1; } else if (bytes_sent == 0) { - dlog(LOG_DEBUG, "connection_send_data: send returned 0, break.\n"); + dlog(LOG_DEBUG, "%s: send returned 0, break.\n", __func__); break; } + // Update positions after a successful send conn->send_pos += (size_t)bytes_sent; total_bytes_sent += bytes_sent; - - dlog(LOG_DEBUG, "connection_send_data: sent=%zd, new send_pos=%zu\n", - bytes_sent, conn->send_pos); + dlog(LOG_DEBUG, "%s: sent=%zd, new send_pos=%zu\n", + __func__, bytes_sent, conn->send_pos); } - dlog(LOG_DEBUG, "connection_send_data: total_bytes_sent=%zd, final send_pos=%zu\n", - total_bytes_sent, conn->send_pos); + // Log total bytes sent and final position for debugging + dlog(LOG_DEBUG, "%s: total_bytes_sent=%zd, final send_pos=%zu\n", + __func__, total_bytes_sent, conn->send_pos); return (int)total_bytes_sent; } - - int connection_send_dynamic(struct connection *conn) { + /* TODO: Read data asynchronously. + * Returns 0 on success and -1 on error. + */ + + // Loop until we have read and sent the entire file while (conn->file_pos < conn->file_size) { + // Start an asynchronous read operation for the next chunk of data connection_start_async_io(conn); + // Wait for the asynchronous read to complete and process its results connection_complete_async_io(conn); + // Send the data that was just read to the client if (connection_send_data(conn) < 0) return -1; - + + // If no new data was read, break the loop if (conn->send_len == 0) break; } + // Mark the connection state as finished sending all data conn->state = STATE_DATA_SENT; return 0; } + void handle_input(struct connection *conn) { /* TODO: Handle input information: may be a new message or notification of - * completion of an asynchronous I/O operation. - */ + * completion of an asynchronous I/O operation. + */ switch (conn->state) { + case STATE_INITIAL: + // If in initial state, attempt to receive data from the client. + receive_data(conn); + break; - case STATE_INITIAL: - receive_data(conn); //trebuie sa primeasca date + case STATE_SENDING_404: + // If sending a 404 response, try to send as much as possible. + if (connection_send_data(conn) < 0) { + // On error, remove the connection. + connection_remove(conn); break; - case STATE_SENDING_404: - // connection_send_data(conn); //trebuie sa trimita cat mai mult din data - // conn->state = STATE_404_SENT; // a trimis deja 404 - if (connection_send_data(conn) < 0) { - connection_remove(conn); - break; - } + } - if (conn->send_pos >= conn->send_len) //daca tot raspunsu de 404 a fost trimis - conn->state = STATE_404_SENT; + // If all 404 response data has been sent, update state. + if (conn->send_pos >= conn->send_len) + conn->state = STATE_404_SENT; + break; - break; + case STATE_404_SENT: + // Once the 404 response is fully sent, close the connection. + connection_remove(conn); + break; - case STATE_404_SENT: - connection_remove(conn); // a fost deja trimis deci poti inchide conexiunea - break; - case STATE_ASYNC_ONGOING: - // connection_complete_async_io(conn); // operatia asincrona e in curs, apeleaza connection send dynamic care completeaza operatia asincrona - // ori verificare dynamic in functie ori aici - if (connection_send_dynamic(conn) < 0) { - connection_prepare_send_404(conn); - conn->state = STATE_SENDING_404; - } - break; + case STATE_ASYNC_ONGOING: + // If an asynchronous operation is ongoing, call connection_send_dynamic + // to complete the operation and send data to the client. + if (connection_send_dynamic(conn) < 0) { + // On error, prepare a 404 response and update state. + connection_prepare_send_404(conn); + conn->state = STATE_SENDING_404; + } + break; - case STATE_CONNECTION_CLOSED: // conexiunea a fost inchisa - connection_remove(conn); - break; + case STATE_CONNECTION_CLOSED: + // If the connection is marked closed, remove it. + connection_remove(conn); + break; default: + // This should not happen: print unexpected state information. printf("shouldn't get here %d\n", conn->state); break; } @@ -580,110 +594,118 @@ void handle_input(struct connection *conn) void handle_output(struct connection *conn) { /* TODO: Handle output information: may be a new valid requests or notification of - * completion of an asynchronous I/O operation or invalid requests. - */ - dlog(LOG_DEBUG, "[handle_output] sockfd=%d, state=%d\n", - conn->sockfd, conn->state); + * completion of an asynchronous I/O operation or invalid requests. + */ + dlog(LOG_DEBUG, "%s: sockfd=%d, state=%d\n", + __func__, conn->sockfd, conn->state); switch (conn->state) { + case STATE_REQUEST_RECEIVED: + // After receiving a complete request, prepare the HTTP reply header. + connection_prepare_send_reply_header(conn); + conn->state = STATE_SENDING_HEADER; + dlog(LOG_DEBUG, "%s: -> STATE_SENDING_HEADER\n", __func__); + break; - case STATE_REQUEST_RECEIVED: // dupa ce serverul a primit cererea clientului, pregateste raspunsu http - connection_prepare_send_reply_header(conn); - conn->state = STATE_SENDING_HEADER; // pregateste raspunsul http - dlog(LOG_DEBUG, "[handle_output] -> STATE_SENDING_HEADER\n"); + case STATE_SENDING_HEADER: + // Attempt to send the header data to the client. + if (connection_send_data(conn) < 0) { + dlog(LOG_DEBUG, "%s: error sending header -> 404\n", __func__); + connection_prepare_send_404(conn); + conn->state = STATE_SENDING_404; break; + } + // If the entire header has been sent, update state. + if (conn->send_pos >= conn->send_len) { + conn->state = STATE_HEADER_SENT; + dlog(LOG_DEBUG, "%s: header fully sent -> STATE_HEADER_SENT\n", __func__); + } + break; - case STATE_SENDING_HEADER: // serverul trimite header ul clientului - - if (connection_send_data(conn) < 0) { // incearca sa trimita - dlog(LOG_DEBUG, - "[handle_output] error sending header -> 404\n"); - connection_prepare_send_404(conn); // daca e eroare faci raspunsu 404 - conn->state = STATE_SENDING_404; - break; - } - - if (conn->send_pos >= conn->send_len) { - conn->state = STATE_HEADER_SENT; - dlog(LOG_DEBUG, - "[handle_output] header fully sent -> STATE_HEADER_SENT\n"); - } - break; - case STATE_SENDING_404: // aceeasi situatie ca la handle_input - if (connection_send_data(conn) < 0) { - dlog(LOG_DEBUG, - "[handle_output] error sending 404 -> remove\n"); + case STATE_SENDING_404: + // Attempt to send 404 response. + if (connection_send_data(conn) < 0) { + dlog(LOG_DEBUG, "%s: error sending 404 -> remove\n", __func__); connection_remove(conn); break; - } - if (conn->send_pos >= conn->send_len) { - conn->state = STATE_404_SENT; - dlog(LOG_DEBUG, - "[handle_output] 404 fully sent -> STATE_404_SENT\n"); - } + } + // If the entire 404 response has been sent, update state. + if (conn->send_pos >= conn->send_len) { + conn->state = STATE_404_SENT; + dlog(LOG_DEBUG, "%s: 404 fully sent -> STATE_404_SENT\n", __func__); + } + break; - break; - case STATE_HEADER_SENT: // trb sa vezi daca resursa este sau nu valida, treci fie la sending_data fie la sending_404 - if(conn->res_type == RESOURCE_TYPE_NONE) { - connection_prepare_send_404(conn); - conn->state = STATE_SENDING_404; - } else { - conn->state = STATE_SENDING_DATA; - } - break; - case STATE_SENDING_DATA: // trb sa vezi fie daca este statica sau dinamica - if (conn->res_type == RESOURCE_TYPE_STATIC) { - conn->state = connection_send_static(conn); - } else if (conn->res_type == RESOURCE_TYPE_DYNAMIC) { - conn->state = connection_send_dynamic(conn); - } else { - connection_prepare_send_404(conn); - conn->state = STATE_SENDING_404; - } - break; - case STATE_ASYNC_ONGOING: // incepi o operatie asincrona de citire - connection_start_async_io(conn); - break; - case STATE_CONNECTION_CLOSED: // se inchide conexiunea - connection_remove(conn); - break; - case STATE_DATA_SENT: // toate datele au fost trimise, deci conexiunea trebuie inchisa - connection_remove(conn); - break; - case STATE_404_SENT: // se inchide conexiunea - connection_remove(conn); - break; - - default: + case STATE_HEADER_SENT: + // After header is sent, determine if the resource is valid. + if (conn->res_type == RESOURCE_TYPE_NONE) { + connection_prepare_send_404(conn); + conn->state = STATE_SENDING_404; + } else { + conn->state = STATE_SENDING_DATA; + } + break; + + case STATE_SENDING_DATA: + // Depending on resource type, send static or dynamic data. + if (conn->res_type == RESOURCE_TYPE_STATIC) { + conn->state = connection_send_static(conn); + } else if (conn->res_type == RESOURCE_TYPE_DYNAMIC) { + conn->state = connection_send_dynamic(conn); + } else { + connection_prepare_send_404(conn); + conn->state = STATE_SENDING_404; + } + break; - dlog(LOG_DEBUG, "handle_output: ignoring unexpected state %d\n", conn->state); + case STATE_ASYNC_ONGOING: + // If an async read is ongoing, start another asynchronous operation. + connection_start_async_io(conn); + break; + + case STATE_CONNECTION_CLOSED: + // If connection is closed, clean up. + connection_remove(conn); + break; + + case STATE_DATA_SENT: + // After all data has been sent, close the connection. + connection_remove(conn); + break; + + case STATE_404_SENT: + // After sending 404 fully, close the connection. + connection_remove(conn); + break; + + default: + dlog(LOG_DEBUG, "%s: ignoring unexpected state %d\n", __func__, conn->state); break; } } void handle_client(uint32_t event, struct connection *conn) { - dlog(LOG_DEBUG, - "[handle_client] sockfd=%d event=0x%X, current state=%d\n", - conn->sockfd, event, conn->state); + /* TODO: Handle new client. There can be input and output connections. + * Take care of what happened at the end of a connection. + */ + dlog(LOG_DEBUG, "%s: sockfd=%d event=0x%X, current state=%d\n", + __func__, conn->sockfd, event, conn->state); + // Check if the event indicates the socket is ready for reading if (event & EPOLLIN) { - dlog(LOG_DEBUG, "[handle_client] -> handle_input\n"); + dlog(LOG_DEBUG, "%s: -> handle_input\n", __func__); handle_input(conn); - } - else if (event & EPOLLOUT) { - dlog(LOG_DEBUG, "[handle_client] -> handle_output\n"); + } else if (event & EPOLLOUT) { // Check if the event indicates the socket is ready for writing + dlog(LOG_DEBUG, "%s: -> handle_output\n", __func__); handle_output(conn); - } - else { - dlog(LOG_DEBUG, "[handle_client] unknown event -> remove\n"); + } else { // If event is neither EPOLLIN nor EPOLLOUT, remove the connection + dlog(LOG_DEBUG, "%s: unknown event -> remove\n", __func__); connection_remove(conn); } } int main(void) { - int rc; - /* TODO: Initialize asynchronous operations. */ DIE(io_setup(MAX_EVENTS, &ctx) < 0, "io_setup"); @@ -707,9 +729,9 @@ int main(void) DIE(w_epoll_wait_infinite(epollfd, &rev) < 0, "w_epoll_wait_infinite"); /* TODO: Switch event types; consider - * - new connection requests (on server socket) - * - socket communication (on connection sockets) - */ + * - new connection requests (on server socket) + * - socket communication (on connection sockets) + */ if (rev.data.fd == listenfd) { if (rev.events & EPOLLIN) { dlog(LOG_DEBUG, "New connection event received.\n"); -- GitLab