diff --git a/docs/assets/resources/linux-epoll-wait.png b/docs/assets/resources/linux-epoll-wait.png new file mode 100644 index 0000000..f7e1b47 Binary files /dev/null and b/docs/assets/resources/linux-epoll-wait.png differ diff --git a/docs/assets/stage-13/implementation.png b/docs/assets/stage-13/implementation.png index a46d0f9..745bb87 100644 Binary files a/docs/assets/stage-13/implementation.png and b/docs/assets/stage-13/implementation.png differ diff --git a/docs/assets/stage-14/implementation.png b/docs/assets/stage-14/implementation.png index b869b50..15f357f 100644 Binary files a/docs/assets/stage-14/implementation.png and b/docs/assets/stage-14/implementation.png differ diff --git a/docs/assets/stage-8/implementation.png b/docs/assets/stage-8/implementation.png index b11d9b1..5a85995 100644 Binary files a/docs/assets/stage-8/implementation.png and b/docs/assets/stage-8/implementation.png differ diff --git a/docs/guides/index.md b/docs/guides/index.md index 6e72987..846ab1e 100644 --- a/docs/guides/index.md +++ b/docs/guides/index.md @@ -1,5 +1,9 @@ # Guides + + The guides feature supplementary documentation intended for your reference as you progress through the roadmap. You'll be directed to the relevant pages as necessary. These documents are broadly classified into two categories: - **Resources:** These articles include information designed to help us understand concepts. They may include links to third-party websites for deeper understanding of specific topics. @@ -7,27 +11,27 @@ The guides feature supplementary documentation intended for your reference as yo ## Resources -βœ… Reviewed -🟑 To be reviewed -βšͺ Partially reviewed +βœ… Reviewed +🟑 To be reviewed +βšͺ Partially reviewed -- βœ… [Architecture](/guides/resources/architecture) -- βœ… [Coding Conventions](/guides/resources/coding-conventions) -- βœ… [GDB](/guides/resources/gdb) -- βœ… [TCP/IP Model](/guides/resources/tcp-ip-model) -- βœ… [TCP Socket Programming](/guides/resources/tcp-socket-programming) -- βœ… [UDP Socket Programming](/guides/resources/udp-socket-programming) -- βœ… [Process and Threads](/guides/resources/process-and-threads) -- βœ… [System Calls](/guides/resources/system-calls) -- 🟑 [Introduction to Linux epoll](/guides/resources/introduction-to-linux-epoll) -- 🟑 [Linux epoll](/guides/resources/linux-epoll) -- 🟑 [Blocking & Non-Blocking Sockets](/guides/resources/blocking-and-non-blocking-sockets) -- 🟑 [HTTP](/guides/resources/http) +- βœ… [Architecture](/guides/resources/architecture) +- βœ… [Coding Conventions](/guides/resources/coding-conventions) +- βœ… [GDB](/guides/resources/gdb) +- βœ… [TCP/IP Model](/guides/resources/tcp-ip-model) +- βœ… [TCP Socket Programming](/guides/resources/tcp-socket-programming) +- βœ… [UDP Socket Programming](/guides/resources/udp-socket-programming) +- βœ… [Process and Threads](/guides/resources/process-and-threads) +- βœ… [System Calls](/guides/resources/system-calls) +- 🟑 [Introduction to Linux epoll](/guides/resources/introduction-to-linux-epoll) +- 🟑 [Linux epoll](/guides/resources/linux-epoll) +- 🟑 [Blocking & Non-Blocking Sockets](/guides/resources/blocking-and-non-blocking-sockets) +- 🟑 [HTTP](/guides/resources/http) ## References -- βœ… [vec](/guides/references/vec) -- βœ… [xps_logger](/guides/references/xps_logger) -- 🟑 [xps_buffer](/guides/references/xps_buffer) -- 🟑 [xps_utils](/guides/references/xps_utils) +- βœ… [vec](/guides/references/vec) +- βœ… [xps_logger](/guides/references/xps_logger) +- 🟑 [xps_buffer](/guides/references/xps_buffer) +- 🟑 [xps_utils](/guides/references/xps_utils) diff --git a/docs/guides/resources/linux-epoll.md b/docs/guides/resources/linux-epoll.md index 0de93a0..1342922 100644 --- a/docs/guides/resources/linux-epoll.md +++ b/docs/guides/resources/linux-epoll.md @@ -264,4 +264,5 @@ When the application closes a monitored FD: When an application calls `epoll_wait()`, it essentially hands control to the kernel, asking it to monitor all file descriptors that were previously registered through `epoll_ctl()`. Inside the kernel, each epoll instance is represented by an eventpoll object, which contains three key components β€” a red-black tree (holding all registered file descriptors), a ready list (containing file descriptors that currently have pending I/O events), and a wait queue (where user processes sleep when there are no ready events). When `epoll_wait()` is invoked, if the ready list is empty, the calling process is put to sleep on the wait queue. Meanwhile, every file descriptor (socket, pipe, etc.) in the system maintains its own internal `poll table`, a structure that records which epoll instances are interested in its state changes. When data arrives or an I/O state changes on any of those file descriptors, the kernel triggers the registered callback `ep_poll_callback()`. This callback runs in interrupt or softirq context, adds the corresponding `epitem` (representing that FD) to the eventpoll’s ready list, and then wakes up any processes sleeping on the epoll’s wait queue. Once the sleeping process wakes, `epoll_wait()` copies the list of ready events from the kernel’s ready list into user-space memory and returns control to the application with a list of file descriptors that are ready for I/O. -Thus, the sequence forms a complete chain: the application waits β†’ the kernel monitors the interest list β†’ a file event triggers the callback β†’ the ready list is updated β†’ the process is woken up β†’ and finally, ready events are delivered back to the user space. \ No newline at end of file +Workflow of `epoll_wait()` : +![epoll_wait.png](/assets/resources/linux-epoll-wait.png) \ No newline at end of file diff --git a/docs/roadmap/index.md b/docs/roadmap/index.md index e5defa5..b11c92c 100644 --- a/docs/roadmap/index.md +++ b/docs/roadmap/index.md @@ -1,5 +1,9 @@ # Roadmap + + The roadmap provides a structured guide for participants to build eXpServer gradually. It outlines the progression of learning objectives, starting from introductory concepts and building up to advanced features and architecture. Each stage builds upon the previous one, ensuring a systematic approach to the project. Links are provided within the stage documents for additional reference of concepts as and when necessary. There will be two types of links present throughout the documentation: @@ -16,39 +20,39 @@ The eXpServer project comprises 24 stages, organized into 5 phases. Prior to the ## Stages -βœ… Reviewed -🟑 To be reviewed -🟣 Working on it -πŸ”΄ Corrections +βœ… Reviewed +🟑 To be reviewed +🟣 Working on it +πŸ”΄ Corrections ### Phase 0: Introduction to Linux socket programming -- βœ… [Overview](phase-0/) -- βœ… [Stage 0: Setup](phase-0/stage-0) -- βœ… [Stage 1: TCP Server](phase-0/stage-1) -- βœ… [Stage 2: TCP Client](phase-0/stage-2) -- 🟑 [Stage 3: UDP with Multi-threading](phase-0/stage-3) -- βœ… [Stage 4: Linux Epoll](phase-0/stage-4) -- βœ… [Stage 5 a): TCP Proxy](phase-0/stage-5-a) -- 🟑 [Stage 5 b): File Transfer using TCP](phase-0/stage-5-b) +- βœ… [Overview](phase-0/) +- βœ… [Stage 0: Setup](phase-0/stage-0) +- βœ… [Stage 1: TCP Server](phase-0/stage-1) +- βœ… [Stage 2: TCP Client](phase-0/stage-2) +- 🟑 [Stage 3: UDP with Multi-threading](phase-0/stage-3) +- βœ… [Stage 4: Linux Epoll](phase-0/stage-4) +- βœ… [Stage 5 a): TCP Proxy](phase-0/stage-5-a) +- 🟑 [Stage 5 b): File Transfer using TCP](phase-0/stage-5-b) ### Phase 1: Building the core of eXpServer by creating reusable modules -- βœ… [Overview](phase-1/) -- βœ… [Stage 6: Listener & Connection Modules](phase-1/stage-6) -- 🟑 [Stage 7: Core & Loop Modules](phase-1/stage-7) -- 🟑 [Stage 8: Non-Blocking Sockets](phase-1/stage-8) -- 🟑 [Stage 9: epoll Edge Triggered](phase-1/stage-9) -- 🟑 [Stage 10: Pipe Module](phase-1/stage-10) -- 🟑 [Stage 11: Upstream Module](phase-1/stage-11) -- 🟑 [Stage 12: File Module](phase-1/stage-12) -- 🟑 [Stage 13: Session Module](phase-1/stage-13) +- βœ… [Overview](phase-1/) +- βœ… [Stage 6: Listener & Connection Modules](phase-1/stage-6) +- 🟑 [Stage 7: Core & Loop Modules](phase-1/stage-7) +- 🟑 [Stage 8: Non-Blocking Sockets](phase-1/stage-8) +- 🟑 [Stage 9: epoll Edge Triggered](phase-1/stage-9) +- 🟑 [Stage 10: Pipe Module](phase-1/stage-10) +- 🟑 [Stage 11: Upstream Module](phase-1/stage-11) +- 🟑 [Stage 12: File Module](phase-1/stage-12) +- 🟑 [Stage 13: Session Module](phase-1/stage-13) ### Phase 2: Implementing HTTP support -- 🟑 [Overview](phase-2/) -- 🟑 [Stage 14: HTTP Request Module](phase-2/stage-14) -- 🟑 [Stage 15: HTTP Response Module](phase-2/stage-15) +- 🟑 [Overview](phase-2/) +- 🟑 [Stage 14: HTTP Request Module](phase-2/stage-14) +- 🟑 [Stage 15: HTTP Response Module](phase-2/stage-15) - [Stage 16: Config Module](phase-2/stage-16) - [Stage 17: Directory Browsing](phase-2/stage-17) diff --git a/docs/roadmap/phase-0/stage-4.md b/docs/roadmap/phase-0/stage-4.md index ead4872..e842f79 100644 --- a/docs/roadmap/phase-0/stage-4.md +++ b/docs/roadmap/phase-0/stage-4.md @@ -60,7 +60,8 @@ The above code allowed us to connect to one client at a time and keep serving th Now let us modify this section and use epoll to achieve our goal of concurrency. ::: tip PRE-REQUISITE READING -Read the following [introduction to epoll](/guides/resources/introduction-to-linux-epoll) before proceeding further. +- Read about [Introduction to epoll](/guides/resources/introduction-to-linux-epoll). +- Read about [Linux epoll](/guides/resources/linux-epoll) ::: First we’ll create an epoll instance using [`epoll_create1()`](https://man7.org/linux/man-pages/man2/epoll_create.2.html) given by the `` header. This returns a file descriptor (FD), and lets call it `epoll_fd`. Remember FD’s are just integers (unsigned integers, to be specific). diff --git a/docs/roadmap/phase-1/stage-10.md b/docs/roadmap/phase-1/stage-10.md index bdde0ff..b68ead8 100644 --- a/docs/roadmap/phase-1/stage-10.md +++ b/docs/roadmap/phase-1/stage-10.md @@ -40,6 +40,7 @@ Here, the timeout in the `epoll_wait()` is set according to the existence of rea In Stage 6, we have discussed the issue of accumulating nulls in the events,connections and listeners list, here we would be filtering those. + ## Implementation A new module `xps_pipe` is added and the existing modules are modified. @@ -51,6 +52,93 @@ Modifications are carried out in the following order : - `xps_loop` - `xps_core` +Find below the updated `xps.h` file. + +::: details **expserver/src/xps.h** + +```c +#ifndef XPS_H +#define XPS_H + +// Header files +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 3rd party libraries +#include "lib/vec/vec.h" // https://github.com/rxi/vec + +// Constants +#define DEFAULT_BACKLOG 64 +#define MAX_EPOLL_EVENTS 32 +#define DEFAULT_BUFFER_SIZE 100000 // 100 KB +#define DEFAULT_PIPE_BUFF_THRESH 1000000 // 1 MB // [!code ++] +#define DEFAULT_NULLS_THRESH 32 + +// Error constants +#define OK 0 // Success +#define E_FAIL -1 // Un-recoverable error +#define E_AGAIN -2 // Try again +#define E_NEXT -3 // Do next +#define E_NOTFOUND -4 // File not found +#define E_PERMISSION -5 // File permission denied +#define E_EOF -6 // End of file reached + +// Data types +typedef unsigned char u_char; +typedef unsigned int u_int; +typedef unsigned long u_long; + +// Structs +struct xps_core_s; +struct xps_loop_s; +struct xps_listener_s; +struct xps_connection_s; +struct xps_buffer_s; +struct xps_buffer_list_s; +struct xps_pipe_s; // [!code ++] +struct xps_pipe_source_s; // [!code ++] +struct xps_pipe_sink_s; // [!code ++] + +// Struct typedefs +typedef struct xps_core_s xps_core_t; +typedef struct xps_loop_s xps_loop_t; +typedef struct xps_listener_s xps_listener_t; +typedef struct xps_connection_s xps_connection_t; +typedef struct xps_buffer_s xps_buffer_t; +typedef struct xps_buffer_list_s xps_buffer_list_t; +typedef struct xps_pipe_s xps_pipe_t; // [!code ++] +typedef struct xps_pipe_source_s xps_pipe_source_t; // [!code ++] +typedef struct xps_pipe_sink_s xps_pipe_sink_t; // [!code ++] + +// Function typedefs +typedef void (*xps_handler_t)(void *ptr); + + + // xps headers +#include "core/xps_core.h" +#include "core/xps_loop.h" +#include "core/xps_pipe.h" // [!code ++] +#include "network/xps_connection.h" +#include "network/xps_listener.h" +#include "utils/xps_logger.h" +#include "utils/xps_utils.h" +#include "utils/xps_buffer.h" + +#endif + +``` +::: + ## `xps_pipe` Module We are introducing pipes in this module. This will be connected to the core itself and will be implemented in a similar way how connections and listeners were implemented. @@ -764,7 +852,13 @@ bool handle_pipes(xps_loop_t *loop) { return false; } ``` -::: +::: + +:::tip NOTE +The logic for handling cases where **only** a source or **only** a sink exists might seem unnecessary right now, since we currently connect the source and sink of the same pipe to the same client connection. However, this design is crucial for future extensibility. + +In later stages (like Stage 11), when we introduce `upstream servers` or `file servers`, the **source** and **sink** of a pipe will often belong to *different* connections (e.g., reading from a client and writing to an upstream server). This independent checking ensures our pipe system can gracefully handle scenarios where one end of the data flow (like the upstream server) closes or disconnects while the other end (the client) is still active, or vice versa. +::: diff --git a/docs/roadmap/phase-1/stage-11.md b/docs/roadmap/phase-1/stage-11.md index 4d279c4..b1c7d10 100644 --- a/docs/roadmap/phase-1/stage-11.md +++ b/docs/roadmap/phase-1/stage-11.md @@ -73,14 +73,14 @@ xps_connection_t *xps_upstream_create(xps_core_t *core, const char *host, u_int } ``` :::warning - Dont forget to free allocated struct addrinfo + Dont forget to free the addrinfo object after use ::: ## Modifications to listener module In `xps_listener` the `listener_connection_handler()` function is having modifications. If the client requests are on port number 8001, an upstream connection instance is created using `xps_upstream_create()` function. Further using `xps_pipe_create()` pipes are created between client source and upstream sink as well as between upstream source and client sink. So the changes are as follows, -- An upstream connection instance is created if `listenerβ†’port` is 8001. +- An upstream connection instance is created if `listener->port` is 8001. The connection should be made to **127.0.0.1** on port **3000**. - A pipe is created between client source and upstream sink. - A pipe is created between upstream source and client sink. @@ -104,7 +104,7 @@ void listener_connection_handler(void *ptr) { // Handle connection based on listener port (upstream or direct) if (listener->port == 8001) { - /* create upstream connection */ + /* create upstream connection to 127.0.0.1:3000 */ /*create pipe connection to client source and upstream sink for the listener*/ /*create pipe connection to upstream source and client sink for the listener*/ } else { @@ -133,13 +133,15 @@ Now start the python file server to serve the current working directory as shown If file server is successfully operational it will display a message like the one below `Serving HTTP on 0.0.0.0 port 3000 (http://0.0.0.0:3000/) ...` +Ensure that the port used when creating the upstream connection matches the port on which the Python file server is running (which is **3000** in this case). + In another terminal compile and run the eXpServer code. It will start like this, ```bash -[INFO] xps_start() : Server listening on [http://0.0.0.0:8001](http://0.0.0.0:8001/) -[INFO] xps_start() : Server listening on [http://0.0.0.0:8002](http://0.0.0.0:8002/) -[INFO] xps_start() : Server listening on [http://0.0.0.0:8003](http://0.0.0.0:8003/) -[INFO] xps_start() : Server listening on [http://0.0.0.0:8004](http://0.0.0.0:8004/) +[INFO] xps_core_start() : Server listening on http://0.0.0.0:8001/ +[INFO] xps_core_start() : Server listening on http://0.0.0.0:8002/ +[INFO] xps_core_start() : Server listening on http://0.0.0.0:8003/ +[INFO] xps_core_start() : Server listening on http://0.0.0.0:8004/ ``` Now the python file server and our eXpServer are both running. If the implementation was correct then accessing `localhost:8001` will now show the files present in the current working directory. Whenever any files are selected on `localhost:8001` the corresponding request details can be seen as log in the terminal running the python server. diff --git a/docs/roadmap/phase-1/stage-9.md b/docs/roadmap/phase-1/stage-9.md index ed8020b..fc2cb93 100644 --- a/docs/roadmap/phase-1/stage-9.md +++ b/docs/roadmap/phase-1/stage-9.md @@ -34,6 +34,7 @@ We will also add a new function called `handle_connections()` in `xps_loop` modu In this stage we will be modifying the following modules in the given order: - `xps_connection` +- `xps_listener` - `xps_loop` ### Modifications to `xps_connection` module: @@ -139,7 +140,43 @@ Modify the `xps_loop_attach` in `xps_listener_create` function similarly ::: -## Modifying `xps_loop` module +### Modifications to `xps_listener` module + +In `xps_listener` module we will be modifying the `listener_connection_handler()` + + +```c + +void listener_connection_handler(void *ptr) { + /*check parameter validity*/ + + while (1) { // [!code ++] + + /*Accepting connection*/ + + if (conn_sock_fd < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) //[!code ++] + break; //[!code ++] + + /*Making socket non blocking*/ + + /*Creating connection instance*/ + + } // [!code ++] +} + +``` +:::tip NOTE +Here, the connection handling logic is enclosed inside a `while (true)` loop because the listener socket is registered using **edge-triggered epoll**. In **edge-triggered mode**, epoll generates an event only when there is a change in the state of the kernel buffer. As a result, if multiple connection requests arrive at the same time and we do not drain the accept queue completely, the remaining connections will not trigger another epoll event. + +By repeatedly calling `accept()` inside the loop, we ensure that all pending connections in the kernel buffer are processed in a single epoll notification. The loop exits when `accept()` returns `-1` with `errno` set to `EAGAIN` or `EWOULDBLOCK`, indicating that the kernel accept queue has been exhausted. + +::: + +:::danger IMPORTANT +This behavior is not evident during testing with a single client, since the correctness of this logic can only be verified when multiple clients establish connections simultaneously. +::: + +### Modifications to `xps_loop` module @@ -201,6 +238,7 @@ void xps_loop_run(xps_loop_t* loop) { } ``` + So now we have successfully made the required changes to enable epoll edge triggering. ## Experiment #1 diff --git a/docs/roadmap/phase-2/stage-14.md b/docs/roadmap/phase-2/stage-14.md index 1d0d780..e8865ad 100644 --- a/docs/roadmap/phase-2/stage-14.md +++ b/docs/roadmap/phase-2/stage-14.md @@ -349,6 +349,11 @@ For each component the start pointer is assigned on beginning and and end pointe :::details **expserver/src/http/xps_http.c -** `xps_http_parse_request_line()` ```c + + bool http_strcmp(u_char *str, const char *method, size_t length){ + /* complete this */ + } + int xps_http_parse_request_line(xps_http_req_t *http_req, xps_buffer_t *buff) { /*get current parser state*/ u_char *p_ch = buff->pos;//current buffer postion @@ -413,7 +418,7 @@ For each component the start pointer is assigned on beginning and and end pointe break; case RL_PATH: - /*on ' ' - path ends, assign end of path,pathname, next state is RL_VERSION_START*/ + /*on ' ' - path ends, assign end of path,pathname,uri next state is RL_VERSION_START*/ /*on '?'or'&'or'='or'#' - assign end of pathname, next state is RL_PATHNAME*/ /*on CR or LF, fails*/ break; @@ -569,7 +574,7 @@ Let us look into the states in header parsing: assert(http_req != NULL); assert(buff != NULL); - char *p_ch = buff->pos; + u_char *p_ch = buff->pos; xps_http_parser_state_t parser_state = http_req->parser_state; for (/*iterarte though buffer, also increments the buffer position*/) { @@ -717,7 +722,8 @@ xps_buffer_t *xps_http_serialize_headers(vec_void_t *headers) { sprintf(header_str, "%s: %s\n", header->key, header->val); if ((buff->size - buff->len) < header_str_len) { //buffer is small u_char *new_data = /*realloc() buffer to twice size*/ - /*updata buff->data and buff->size*/ + /*handle error*/ + /*update buff->data and buff->size*/ } strcat(buff->data, header_str); buff->len = strlen(buff->data); @@ -752,13 +758,14 @@ int http_process_request_line(xps_http_req_t *http_req, xps_buffer_t *buff) { /*find port_str from port start and end pointers*/ /*if port_str is null assign default port number 80 for http and 443 for https if not null assign atoi(port_str)*/ + /*free port_str*/ return OK; } ``` -Add the str_from_ptrs() utility function. Update utils.h accordingly. +Add the `str_from_ptrs()` utility function. Update `xps_utils.h` accordingly. -- expserver/src/utils/utils.c +- `expserver/src/utils/xps_utils.c` ```c char *str_from_ptrs(const char *start, const char *end) { @@ -798,6 +805,7 @@ int http_process_headers(xps_http_req_t *http_req, xps_buffer_t *buff) { /* Alloc memory for new header*/ /*assign key,val from their corresponding start and end pointers*/ /*push this header into headers list of http_req*/ + /*if error is E_NEXT continue*/ } return OK; } @@ -815,9 +823,9 @@ Serializes the parsed HTTP request (request line and headers) into a buffer. The xps_buffer_t *xps_http_req_serialize(xps_http_req_t *http_req) { assert(http_req != NULL); /* Serialize headers into a buffer headers_str*/ - size_t final_len = strlen(http_req->request_line) + 1 + headers_str->len + 1;// Calculate length for final buffer + size_t final_len = strlen(http_req->request_line) + 1 + headers_str->len + 1; /*Calculate length for final buffer*/ /*Create instance for final buffer*/ - // Copy everything to final buffer + /*Copy everything to final buffer*/ memcpy(buff->pos, http_req->request_line, strlen(http_req->request_line)); buff->pos += strlen(http_req->request_line); /*similarly copy "\n", headers_str, "\n"*/ @@ -858,7 +866,7 @@ Frees the memory and resources allocated for the HTTP request object. void xps_http_req_destroy(xps_core_t *core, xps_http_req_t *http_req) { assert(http_req != NULL); /*Frees memory allocated for various components of the HTTP request line(request line, method,etc)*/ - /* iterate through the headers list of http_req and free the memory*/ + /*iterate through the headers list of http_req and free the memory*/ /*de-intialize the headers list*/ /*free http_req*/ } @@ -931,15 +939,15 @@ void client_sink_handler(void *ptr) { ... if (session->http_req == NULL) { //http requset is not recieved till now// [!code ++] int error;// [!code ++] - /*create http_req for the buff read from pipe and destroy the buff*/// [!code ++] + // create http_req for the buff read from pipe and destroy the buff // [!code ++] if (error == E_FAIL) {// [!code ++] - /*process the session and return*/// [!code ++] + // process the session and return //[!code ++] }// [!code ++] - /*handle E_AGAIN*/// [!code ++] + // handle E_AGAIN // [!code ++] session->http_req = http_req;// [!code ++] - /*serialize http_req into buffer http_req_buff*/// [!code ++] - /*set http_req_buff to from_client_buff and clear the pipe*/// [!code ++] - /*process the session*/ // [!code ++] + // serialize http_req into buffer http_req_buff // [!code ++] + // set http_req_buff to from_client_buff and clear the pipe // [!code ++] + // process the session // [!code ++] } else {// [!code ++] set_from_client_buff(session, buff); xps_pipe_sink_clear(sink, buff->len); diff --git a/docs/roadmap/phase-2/stage-15.md b/docs/roadmap/phase-2/stage-15.md index bb128b8..e99b5ef 100644 --- a/docs/roadmap/phase-2/stage-15.md +++ b/docs/roadmap/phase-2/stage-15.md @@ -197,7 +197,6 @@ A new function named `xps_http_set_header()` is added in this stage. This functi ```c int xps_http_set_header(vec_void_t *headers, const char *key, const char *val) { - // Validate params /* Validate params */ xps_keyval_t *header = /* fill this */; @@ -242,6 +241,7 @@ The `xps_http_res_create()` function is getting called from this function. Based - If the path for the requested file is not found, then HTTP response is created with status code set to `HTTP_NOT_FOUND`. - After creating the http response, headers for content length and type are added using `xps_http_set_header()`. - Further the response is serialized using `xps_http_res_serialize()` and it is then stored into the `to_client_buff` . +- Don't forget to destroy the response object after its use(after serialization). ## Milestone #1