From 0ea21320db3669c4d0e4fc5934f202cd47711f77 Mon Sep 17 00:00:00 2001 From: Shibin-Ez Date: Mon, 12 Jan 2026 08:17:53 +0000 Subject: [PATCH 1/5] immediate change to epoll doc --- docs/guides/resources/linux-epoll.md | 72 +++++++++++++--------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/docs/guides/resources/linux-epoll.md b/docs/guides/resources/linux-epoll.md index d269b68..2c71d02 100644 --- a/docs/guides/resources/linux-epoll.md +++ b/docs/guides/resources/linux-epoll.md @@ -9,15 +9,20 @@ Internally, epoll revolves around a kernel object called `eventpoll`. ### Key Components of `eventpoll` Object #### 1. The Wait Queue (`wq`) + This queue holds the list of processes (threads) that are currently blocked in `epoll_wait()`, waiting for an event to occur. When an event happens, the kernel wakes up the processes in this queue. #### 2. The Ready List (`rdlist`) + This is a **doubly linked list** that stores the file descriptors (FDs) that are currently "ready" (i.e., have data to read or space to write). + - When an event occurs on a monitored FD, it is added to this list. - `epoll_wait()` simply checks this list. If it is not empty, it returns the events to the user. #### 3. The Red-Black Tree (`rbr`) + This is a **Red-Black Tree** that stores all the file descriptors currently being monitored by this epoll instance. + - It allows for efficient **insertion, deletion, and search** of file descriptors (O(log n)). - When you call `epoll_ctl()` to add or remove an FD, the kernel modifies this tree. @@ -87,6 +92,7 @@ epoll_create1() removes the obsolete size parameter, and allows FD_CLOEXEC to be #include int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); ``` + **Arguments:** - `epfd`: The file descriptor of the epoll instance as returned by `epoll_create1()`. @@ -158,8 +164,8 @@ Every file descriptor in Linux (sockets, pipes, character devices, etc.) exposes - Once a file descriptor is registered with epoll, the kernel associates it with an internal callback. When the state of that file descriptor changes—for example, when new data arrives (`POLLIN`) or buffer space becomes available (`POLLOUT`)—the kernel invokes its own internal epoll callback function, `ep_poll_callback()`. - This callback: - 1. Enqueues the `epitem` into the **ready list** (`rdlist`) of the `eventpoll`. - 2. Wakes up any thread currently sleeping in `epoll_wait()` on this epoll instance. + 1. Enqueues the `epitem` into the **ready list** (`rdlist`) of the `eventpoll`. + 2. Wakes up any thread currently sleeping in `epoll_wait()` on this epoll instance. ### Waiting for Events: `epoll_wait()` @@ -186,6 +192,7 @@ SYSCALL_DEFINE4(epoll_wait, int, epfd, struct epoll_event __user *, events, int, #### Internally: 1. **Kernel acquires lock and checks ready list** + - If the `rdlist` (ready list) is non-empty, events are immediately returned. - Otherwise, the process goes to sleep in the `wait queue` (`ep->wq`). @@ -199,7 +206,6 @@ When `epoll_wait()` wakes up, it iterates over the `rdlist`, copies the correspo In level-triggered mode, epoll reports an event as long as any of the file descriptors in the `rdlist` of epoll is ready. For readable events `(EPOLLIN)`, if a socket has unread data in its receive buffer, every call to `epoll_wait()` will continue to return it until the data is consumed. For writable events `(EPOLLOUT)`, `epoll_wait()` will continue to return the descriptor as long as there is available space in the send buffer. - This mode is easier to use and ensures events are not missed, but may generate repeated notifications if the application does not fully drain the file descriptor. ## Edge triggered mode @@ -208,7 +214,7 @@ In edge-triggered mode, epoll reports events only when the readiness state chang Because **ET does not repeat events**, the application must read or write until the operation returns `EAGAIN`; otherwise, data may remain unread with no further notifications. -ET reduces unnecessary wakeups and is useful for high-performance servers, but requires more careful programming. This mode is enabled by passing the `EPOLLET` flag when registering the file descriptor with `epoll_ctl()`. +ET reduces unnecessary wakeups and is useful for high-performance servers, but requires more careful programming. This mode is enabled by passing the `EPOLLET` flag when registering the file descriptor with `epoll_ctl()`. ::: tip NOTE `EAGAIN` is a common error code returned by non-blocking I/O operations (e.g., `read`, `write`, `recv`, `send`) when the operation cannot be completed immediately without blocking the calling process. In the context of `epoll` with non-blocking sockets, especially in Edge-Triggered mode, receiving `EAGAIN` indicates that there is no more data to read or the write buffer is full, and you should stop attempting the operation until a new event is reported by `epoll_wait()`. @@ -218,11 +224,11 @@ ET reduces unnecessary wakeups and is useful for high-performance servers, but r Each `epitem` (monitored FD) transitions through three stages: -| Stage | Description | -| :---- | :---- | -| **Registered** | In red-black tree, not ready yet | -| **Ready** | Added to ready list after kernel callback | -| **Delivered** | Returned by `epoll_wait()`, removed or re-queued | +| Stage | Description | +| :------------- | :----------------------------------------------- | +| **Registered** | In red-black tree, not ready yet | +| **Ready** | Added to ready list after kernel callback | +| **Delivered** | Returned by `epoll_wait()`, removed or re-queued | ## Epoll’s Red-Black Tree (Interest List) @@ -249,20 +255,6 @@ wake_up_interruptible(&ep->wq); This triggers a scheduler wakeup for any sleeping threads, causing them to resume execution and return ready events. -## Recursive Epoll (Nested Instances) - -Linux allows **epoll of epoll** (monitoring another epoll FD). -The kernel prevents deadlocks and loops by marking epoll files with flags (`EPOLLWAKEUP`, `EPOLLEXCLUSIVE`) and limiting recursion depth. - -This is handled carefully in `fs/eventpoll.c` using checks like: - -```c -if (is_file_epoll(file)) - error = -EINVAL; -``` - -unless explicit recursion is enabled. - ## Performance and Locking Epoll uses **fine-grained spinlocks** (`ep->lock`) around its lists and trees. @@ -276,14 +268,14 @@ This design ensures: ## Important Kernel Functions (for reference) -| Function | Purpose | -| :---- | :---- | -| `do_epoll_create()` | Allocates and initializes `eventpoll`. | -| `ep_insert()` | Inserts new `epitem` into red-black tree. | -| `ep_remove()` | Removes an `epitem` on `EPOLL_CTL_DEL`. | -| `ep_poll_callback()` | Called by kernel when FD becomes ready. | -| `ep_send_events()` | Copies ready events to user space. | -| `ep_eventpoll_release()` | Cleans up on `close(epfd)` or `exit()`. | +| Function | Purpose | +| :----------------------- | :---------------------------------------- | +| `do_epoll_create()` | Allocates and initializes `eventpoll`. | +| `ep_insert()` | Inserts new `epitem` into red-black tree. | +| `ep_remove()` | Removes an `epitem` on `EPOLL_CTL_DEL`. | +| `ep_poll_callback()` | Called by kernel when FD becomes ready. | +| `ep_send_events()` | Copies ready events to user space. | +| `ep_eventpoll_release()` | Cleans up on `close(epfd)` or `exit()`. | All defined in `fs/eventpoll.c` (Linux source). @@ -310,17 +302,17 @@ When the application closes a monitored FD: ## Summary Table -| Component | Data Structure | Purpose | -| :---- | :---- | :---- | -| Interest list | Red-black tree (`rbr`) | Store registered FDs | -| Ready list | Linked list (`rdlist`) | Store active events | -| Wait queue | `wait_queue_head_t` | Put sleeping processes | -| FD node | `epitem` | Connects file to eventpoll | -| Callback | `ep_poll_callback()` | Moves item to ready list + wakeup | +| Component | Data Structure | Purpose | +| :------------ | :--------------------- | :-------------------------------- | +| Interest list | Red-black tree (`rbr`) | Store registered FDs | +| Ready list | Linked list (`rdlist`) | Store active events | +| Wait queue | `wait_queue_head_t` | Put sleeping processes | +| FD node | `epitem` | Connects file to eventpoll | +| Callback | `ep_poll_callback()` | Moves item to ready list + wakeup | ### Summary of chain of events: -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. +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. Workflow of `epoll_wait()` : -![epoll_wait.png](/assets/resources/linux-epoll-wait.png) \ No newline at end of file +![epoll_wait.png](/assets/resources/linux-epoll-wait.png) From 71702c45eb73ad73180de869a524b108da5dccd5 Mon Sep 17 00:00:00 2001 From: fayisRahman Date: Thu, 15 Jan 2026 08:36:19 +0000 Subject: [PATCH 2/5] added minor chnages --- docs/.vitepress/config.mjs | 2 +- docs/assets/stage-17/directory.png | Bin 0 -> 30327 bytes docs/guides/resources/linux-epoll.md | 40 ++-- docs/roadmap/phase-2/stage-16.md | 19 +- docs/roadmap/phase-2/stage-17.md | 276 +++++++++++++++++++++++++++ 5 files changed, 306 insertions(+), 31 deletions(-) create mode 100644 docs/assets/stage-17/directory.png diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs index 4056a45..cb92e65 100644 --- a/docs/.vitepress/config.mjs +++ b/docs/.vitepress/config.mjs @@ -187,7 +187,7 @@ export default defineConfig({ }, { text: 'Stage 17: Directory Browsing', - link: '/roadmap/phase-3/stage-17', + link: '/roadmap/phase-2/stage-17', }, ], diff --git a/docs/assets/stage-17/directory.png b/docs/assets/stage-17/directory.png new file mode 100644 index 0000000000000000000000000000000000000000..f31e69fa28ef75cfb7f881fd8d8dc450d9978dc4 GIT binary patch literal 30327 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 LRkrxHap3<0PEthN literal 0 HcmV?d00001 diff --git a/docs/guides/resources/linux-epoll.md b/docs/guides/resources/linux-epoll.md index 0712c4a..e81a41c 100644 --- a/docs/guides/resources/linux-epoll.md +++ b/docs/guides/resources/linux-epoll.md @@ -299,12 +299,8 @@ Workflow of `epoll_wait()` : ![epoll_wait.png](/assets/resources/linux-epoll-wait.png) - - - - -# Glossary -## Callback +## Glossary +### Callback **Definition** A _callback_ is a kernel-registered function that the kernel invokes automatically when a specific internal event occurs. @@ -336,7 +332,7 @@ A _callback_ is a kernel-registered function that the kernel invokes automatical - Must not sleep. -## Poll Table +### Poll Table **Definition** A _poll table_ is a kernel data structure used to register interest in future readiness changes of a file descriptor. @@ -365,7 +361,7 @@ A _poll table_ is a kernel data structure used to register interest in future re ---------- -## Spin Lock +### Spin Lock **Definition** A _spin lock_ is a low-level kernel lock that causes the CPU to repeatedly check a lock variable until it becomes available. @@ -399,15 +395,15 @@ A _spin lock_ is a low-level kernel lock that causes the CPU to repeatedly check - Holding a spin lock for too long wastes CPU time. -## Interrupt Context (Hard Interrupt Context) +### Interrupt Context (Hard Interrupt Context) -### Definition +#### Definition **Interrupt context** is a kernel execution context entered when the CPU receives a hardware interrupt signal. Code running in interrupt context executes immediately, preempting the currently running task, and is subject to strict execution constraints. ---------- -### How interrupt context is entered (step by step) +#### How interrupt context is entered (step by step) 1. The CPU is executing instructions (either user-space code or kernel code). @@ -424,7 +420,7 @@ A _spin lock_ is a low-level kernel lock that causes the CPU to repeatedly check 4. The interrupt handler begins executing in **interrupt context**. - ### What runs in interrupt context + #### What runs in interrupt context - Hardware interrupt handlers @@ -437,7 +433,7 @@ A _spin lock_ is a low-level kernel lock that causes the CPU to repeatedly check Interrupt context code executes **without association to any user process or thread**. -### Execution rules in interrupt context +#### Execution rules in interrupt context The following rules are **strictly enforced**: @@ -458,7 +454,7 @@ Allowed operations include: - Scheduling deferred work -### Why sleeping is forbidden +#### Why sleeping is forbidden Sleeping would require the kernel scheduler to: @@ -476,7 +472,7 @@ However, interrupt context: Sleeping in interrupt context would lead to kernel corruption or deadlock. -### Relation to epoll +#### Relation to epoll When a device event occurs (e.g., network packet arrival): @@ -488,15 +484,15 @@ When a device event occurs (e.g., network packet arrival): - Epoll callbacks are never allowed to sleep because they may be triggered from interrupt-related paths -## Softirq Context +### Softirq Context -### Definition +#### Definition **Softirq context** is a kernel execution context used to perform deferred work that was triggered by a hardware interrupt but could not be safely or efficiently completed inside the interrupt handler itself. ---------- -### Why softirq exists +#### Why softirq exists Hardware interrupt handlers must execute **very quickly**. However, many kernel subsystems (especially networking) require additional processing that: @@ -510,7 +506,7 @@ However, many kernel subsystems (especially networking) require additional proce Softirqs provide a controlled way to defer this work. -### How softirq context is entered +#### How softirq context is entered 1. A hardware interrupt occurs. 2. The interrupt handler: @@ -541,7 +537,7 @@ Softirqs may execute: ---------- -### What runs in softirq context +#### What runs in softirq context - Network packet processing @@ -556,7 +552,7 @@ Softirqs may execute: Like interrupt context, softirq context is **not associated with a user process**. -### Execution rules in softirq context +#### Execution rules in softirq context The rules are similar to interrupt context: @@ -576,7 +572,7 @@ Allowed operations: - Updating kernel state - Waking sleeping processes -### Relation to epoll +#### Relation to epoll In the networking stack: diff --git a/docs/roadmap/phase-2/stage-16.md b/docs/roadmap/phase-2/stage-16.md index b5edfc9..a1bca68 100644 --- a/docs/roadmap/phase-2/stage-16.md +++ b/docs/roadmap/phase-2/stage-16.md @@ -369,7 +369,7 @@ if (lookup->type == REQ_FILE_SERVE) { /*implementation already given*/ } else if (lookup->type == REQ_REVERSE_PROXY) { - + ... sscanf(lookup->upstream, "%[^:]:%u", host, &port); /*create upstream connection*/ /*handle errors*/ @@ -584,34 +584,37 @@ void sigint_handler(int signum) { First we have to run the server by giving the JSON file containing the configuration information as command line argument. ![exp1-img1.png](/assets/stage-16/exp1-img1.png) + Three port would be created as given: ![exp1-img2.png](/assets/stage-16/exp1-img2.png) -- First, we would be verifying the file server functionality. An `index.html` file as mentioned in the xps_config.json is created in the public folder. Add some standard html code in this file. The contents can be viewed on `[localhost:8001](http://localhost:8001)` on the browser. +- First, we would be verifying the file server functionality. An `index.html` file as mentioned in the xps_config.json is created in the public folder. Add some standard html code in this file. The contents can be viewed on [localhost:8001](http://localhost:8001) on the browser. Try by replacing files of different format. You have to update JSON configuration file accordingly. - Now, run a python file server as done earlier in Phase 0. - ![exp1-img3.png](/assets/stage-16/exp1-img3.png) - - This will create the upstream server mentioned in xps_config.json. + ![exp1-img3.png](/assets/stage-16/exp1-img3.png) + + This will create the upstream server mentioned in xps_config.json. - The files in the directory in which the python server is running can be viewed from the urls [`localhost:8001/hello`] and [`localhost:8002`] . In this case, the url is redirected to the second one. +The files in the directory in which the python server is running can be viewed from the urls [`localhost:8001/hello`](http://localhost:800/hello) and [`localhost:8002`](http://localhost:8002) . In this case, the url is redirected to the second one. - `localhost:8003` would be redirecting to the `redirect_url` mentioned in the JSON configuration file. Try adding more ports and redirect to different urls. -## Experiment #1 +## Experiments + +### Experiment #1 eXpServer uses a **Longest Prefix Match** strategy to decide which route to use when multiple routes match a request path. To see this in action, open your `xps_config.json` and add two overlapping routes for the same server: Route A with `req_path: "/"` (Type: `file_serve`) and Route B with `req_path: "/api"` (Type: `redirect` to `http://localhost:8002`). Now, try requesting `http://localhost:8001/api/data`. You will notice that Route B is chosen because `/api` is a longer match than `/`. If you request `/ap`, where do you think it will be routed to? -## Experiment #2 +### Experiment #2 In this experiment, we will explore how the server behaves when an index file is missing. Open `xps_config.json` and change the `dir_path` for the root route of port `8001` from `../public/` to `../../` (your project root). diff --git a/docs/roadmap/phase-2/stage-17.md b/docs/roadmap/phase-2/stage-17.md index 2ab8576..2b4607a 100644 --- a/docs/roadmap/phase-2/stage-17.md +++ b/docs/roadmap/phase-2/stage-17.md @@ -1 +1,277 @@ # Stage 17: Directory Browsing + +## Recap + +In the previous stages, we updated the server to handle HTTP messages. Stage 14 focused on implementing HTTP request parsing, while Stage 15 covered the construction of HTTP response messages. And in stage 16 we have implemented multiple cores, used a JSON file to set-up server configuration and implemented reverse proxy and URL redirecting in our webserver. + +## Learning Objectives + +- Implement directory browsing to display folder contents +- Create a new `xps_directory` module to handle directory listing + +## Introduction + +Directory browsing is a feature in web servers that allows us to see the files and folders in a directory when a default index file (typically, index.html) is missing or not present in the directory. The web server dynamically generates an index.html file with a list of files and subdirectories within the request directory and serves it. To achieve directory browsing, we will use inbuilt functions like `opendir(),readdir(),closedir()` but remember that directory browsing can expose sensitive files and directories that were not intended to be publicly accessible. This can include configuration files, source code, database backups, and other sensitive data, so be careful when you configure the path to be displayed under directory browsing + +## Implementation + +Recall, during config lookup, when we were unable to find the index file, we set the `dir_path` to resource path. + +```c +lookup_object->dir_path = resource_path; +``` + +If this is the case, we initiate the directory browsing function which takes in the `dir_path` and generates an `index.html` file. So when `lookup->type==REQ_FILE_SEREVE` and if `lookup->dir_path` is not `NULL`, `xps_directory_browsing()` will generate HTML file then set the body of `http_res` + +To open a directory stream we use `opendir()` with `dir_path` and assigned to `DIR` data type + +:::tip NOTE +Before going to the implementation, we have to look into some system calls that will help you to implement directory browsing from **``** + +- [`opendir(dir_path)`](https://man7.org/linux/man-pages/man3/opendir.3.html) function opens a directory stream corresponding to the directory name, and returns a pointer to the directory stream. The stream is positioned at the first entry in the directory.on success, it will return a pointer to the a directory stream on error will return `NULL` pointer +- [`readdir(dirp)`](https://man7.org/linux/man-pages/man3/readdir.3.html) function returns a pointer to a `dirent` structure representing the next directory entry in the directory stream pointed to by `dirp`. It returns `NULL` on reaching the end of the directory stream or if an error occurred. +In the `glibc` implementation, the `dirent` structure is defined as follows: + +```c +struct dirent{ + ino_t d_ino; /* Inode number */ + off_t d_off; /* Not an offset; see below */ + unsigned short d_reclen; /* Length of this record */ + unsigned char d_type; /* Type of file; not supported + by all filesystem types */ + char d_name[256]; /* Null-terminated filename */ +}; +``` + +The only fields in the `dirent` structure that are mandated by POSIX.1 are `d_name` and `d_ino` . + +The other fields are unstandardized and not present on all systems. + +::: + +### **xps_directory** + +#### `xps_directory.h` + +The code below has the contents of the header file for `xps_directory.h`. Have a look at it and make a copy of it in your codebase + +```c +#ifndef XPS_DIRECTORY +#define XPS_DIRECTORY + +#include "../xps.h" + +/** + * @brief Generates a directory listing HTML page for directory browsing. + * + * This function generates an HTML page displaying the contents of a directory + * for browsing. + * + * @param dir_path The path to the directory to be browsed. + * @param pathname The pathname to be displayed in the HTML page. + * @return An xps_buffer_t pointer containing the HTML page, or NULL on failure. + */ +xps_buffer_t *xps_directory_browsing(const char *dir_path, const char *pathname); + +#endif +``` + +#### `xps_directory.c` + +When the `xps_directory_browsing()` called from the `sesssion_process_request()` a buffer will be created with HTML tags and the heading of the page, then all the data will be added in the formatted pattern to the buffer + +:::tip NOTE +Before proceeding to `xps_session`, please go through some system calls and functions that will help us to implement directory browsing + +- [`stat`](https://man7.org/linux/man-pages/man3/stat.3type.html) file status - Describe the information about a file +- [`fstat`](https://man7.org/linux/man-pages/man2/stat.2.html) retrieve the information about the file pointed to the pathname +- [`S_ISREG, S_ISDIR`](https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html) for testing the type of a file +::: + +:::details expserver/src/disk/xps_directory.c + +```c +#include "../xps.h" + +xps_buffer_t *xps_directory_browsing(const char *dir_path,const char *pathname){ + /* validate parameters */ + + // Buffer for HTTP message + char *buff= /* fill this */ + if (buff == NULL) { + logger(LOG_ERROR, "xps_directory_browsing()", "malloc() failed for 'buff'"); + return + } + + //Here we will append the html file into this buff starting from the heading and basic styling + spintf(buff,"Directory: " + "%s

eXpServer

Index " + "of %s


", + pathname, pathname); + + // Open a directory stream using opendir function + DIR *dir = opendir(dir_path); + if(dir == NULL){ + logger(LOG_ERROR, "xps_directory_browsing()", "opendir() failed"); + free(buff); + return NULL; + } + + struct dirent *dir_entry; + + while((dir_entry == readdir(dir)) != NULL){ + //skip the first two entries such as . and .. in list directory from dir_entry->d_dname + if (strcmp(dir_entry->d_name, ".") == || strcmp(dir_entry->d_name, "..") == 0)) + continue; + + char full_path[1024]; + /* copy the dir_path and dir_entry->d_name with formatted with a / into full_path */ + //HINT: you can use snprintf + + //open the file in the full_path + int file_fd = /*fill this*/ + if(file_fd == -1){ + logger(LOG_ERROR, "xps_directory_browsing()", "failed to open file"); + continue; + } + + struct stat file_stat; + + //get information about the file + if(fstat(file_fd, &file_stat) ==-1){ + logger(LOG_ERROR, "xps_directory_browsing()", "fstat()"); + close(file_fd); + continue; + } + //check if file is regular file or if the file is a direcory + if(S_ISREG(file_stat.st_mode) || S_ISDIR(file_stat.st_mode)){ + char *is_dir = S_ISDIR(file_stat.st_mode) ? "/" : ""; + + char *temp_pathname = str_create(pathname); + if (temp_pathname[strlen(temp_pathname) - 1] == '/') + temp_pathname[strlen(temp_pathname) - 1] = '\0'; + + // char time_buff[20]; + // strftime(time_buff, sizeof(time_buff), "%Y-%m-%d %H:%M:%S", localtime(&file_stat.st_mtime)); + + if (S_ISREG(file_stat.st_mode)) // IS_FILE + sprintf(buff + strlen(buff), + "\n", + temp_pathname, dir_entry->d_name, dir_entry->d_name, is_dir,); + else + sprintf( + buff + strlen(buff), + "\n", + temp_pathname, dir_entry->d_name, dir_entry->d_name, is_dir); + free(temp_pathname); + } + + /*close the file*/ + + } + + closedir(file_fd); + sprintf(buff + strlen(buff), "
%s%s
%s%s
"); + xps_buffer_t *directory_browsing = /*fill this*/ + + if (directory_browsing == NULL) { + logger(LOG_ERROR, "xps_directory_browsing()","xps_buffer_create() returned NULL"); + free(buff); + return NULL; + } + + return directory_browsing; + +} + +``` +::: + +### `xps_config.c` + +In this part we have to set `lookup->dir_path = resource_path` if the index file not found , Remember that in the last stage, we iterate through the route index and set the `index_file_flound=true` if the `index_fille_path` is a file + +```c +xps_config_lookup_t *xps_config_lookup(xps_config_t *config, xps_http_req_t *http_req, xps_connection_t *client, int *error) { + //* no change from the last stage + + if(is_file(resource_path)){ + lookup->file_path = resource_path; + }else if( is_dir(resource_path)){ + //from stage 16 + + //if the index file not found set the dir_path in lookup to resource_path + }else{ + free(resource_path); + } + + //* no change from the last stage + +} +``` + +### `xps_session.c` + +So far, when the lookup type is `REQ_FILE_SERVE`, we only handled file serving if the `file_path` in the `lookup` is not empty.Now onwards, we are going first to check if `dir_path` in the lookup is there; then it will go for directory browsing by creating an HTML file using the `xps_directory_browsing` function and add the HTML file in the HTTP response body + +```c +void session_process_request(xps_session_t *session) { + + /* no change from the last stage*/ + + if(lookup->type == REQ_FILE_SERVE){ + + xps_buffer_t *dir_html= /*generate the html content*/ + /*verify the dir_html is not NULL*/ + + /*no change from the last stage*/ + + /*create http_res with status code HTTP_OK dir html is not NULL or HTTP_INTERNAL_SERVER_ERROR*/ + + /*if dir_html is not null set the body and header of http_res*/ + + xps_buffer_t *http_res_buff= /*fill this*/ + + /*serialize and set to_client_buff*/ + /*destroy response object after use*/ + return; + } + +} +``` + +## Milestone #1 + +Build the server and run it with `xps_config.json`. Ensure that the `dir_path` is set to a directory without an index file (as configured in Stage 16's Experiment #2). Navigate to [localhost:8001](http://localhost:8001) in your browser. If the implementation is correct, you should see an HTML page listing all files and subdirectories in the configured directory. + +## Experiments + +### Experiment #1 + +For all directories and files, display the last modified date in the following format: + +```c +"%Y-%m-%d %H:%M:%S" +``` +Extend the directory listing to display file sizes in KB for each file. You can retrieve the file size in **bytes** using `file_stat.st_size` and convert it to kilobytes. + +![directory image](/assets/stage-17/directory.png) + +After completing this experiment, your directory listing should display the last modified date and file size for each entry, as shown above. + +## Conclusion + +In this stage, we implemented the `xps_directory` module to handle directory browsing. When a requested directory lacks an index file, the server now dynamically generates an HTML page listing all files and subdirectories, solving the 404 issue we encountered in Stage 16. + +This also marks the end of Phase 2. Throughout this phase, we transformed eXpServer from a basic TCP server into a fully functional HTTP server with dynamic configuration, multiple worker cores, file serving, reverse proxying, and URL redirection. + +However, our server currently accepts connections from any client on the network. What if you want to restrict access to only trusted IP addresses within your organization or block known malicious IPs? Think about how you might solve this problem. \ No newline at end of file From 316569a279840e9cb210e165dfcfd3ffcb0208e8 Mon Sep 17 00:00:00 2001 From: fayisRahman Date: Thu, 15 Jan 2026 10:21:10 +0000 Subject: [PATCH 3/5] minor fixes --- docs/assets/stage-12/mime_types.txt | 928 +++++++++++++++++++++++++++ docs/guides/resources/linux-epoll.md | 10 +- docs/roadmap/phase-1/stage-12.md | 9 +- 3 files changed, 939 insertions(+), 8 deletions(-) create mode 100644 docs/assets/stage-12/mime_types.txt diff --git a/docs/assets/stage-12/mime_types.txt b/docs/assets/stage-12/mime_types.txt new file mode 100644 index 0000000..a868e3e --- /dev/null +++ b/docs/assets/stage-12/mime_types.txt @@ -0,0 +1,928 @@ +{".123", "application/vnd.lotus-1-2-3"}, +{".3dml", "text/vnd.in3d.3dml"}, +{".3g2", "video/3gpp2"}, +{".avif", "image/avif"}, +{".avifs", "image/avif"}, +{".kra", "application/x-krita"}, +{".krz", "application/x-krita"}, +{".heif", "image/heic"}, +{".heic", "image/heic"}, +{".3gp", "video/3gpp"}, +{".3g2", "audio/3gpp2"}, +{".3gp", "audio/3gpp"}, +{".7z", "application/x-7z-compressed"}, +{".a", "application/octet-stream"}, +{".bin", "application/octet-stream"}, +{".bpk", "application/octet-stream"}, +{".deploy", "application/octet-stream"}, +{".dist", "application/octet-stream"}, +{".distz", "application/octet-stream"}, +{".dmg", "application/octet-stream"}, +{".dms", "application/octet-stream"}, +{".dump", "application/octet-stream"}, +{".elc", "application/octet-stream"}, +{".iso", "application/octet-stream"}, +{".lha", "application/octet-stream"}, +{".lrf", "application/octet-stream"}, +{".lzh", "application/octet-stream"}, +{".o", "application/octet-stream"}, +{".obj", "application/octet-stream"}, +{".pkg", "application/octet-stream"}, +{".so", "application/octet-stream"}, +{".aab", "application/x-authorware-bin"}, +{".u32", "application/x-authorware-bin"}, +{".vox", "application/x-authorware-bin"}, +{".x32", "application/x-authorware-bin"}, +{".icns", "image/x-icns"}, +{".aac", "audio/aac"}, +{".mp4", "audio/aac"}, +{".m4a", "audio/aac"}, +{".aam", "application/x-authorware-map"}, +{".aas", "application/x-authorware-seg"}, +{".abw", "application/x-abiword"}, +{".acc", "application/vnd.americandynamics.acc"}, +{".ace", "application/x-ace-compressed"}, +{".acu", "application/vnd.acucobol"}, +{".acutc", "application/vnd.acucorp"}, +{".atc", "application/vnd.acucorp"}, +{".adp", "audio/adpcm"}, +{".aep", "application/vnd.audiograph"}, +{".afm", "application/x-font-type1"}, +{".pfa", "application/x-font-type1"}, +{".pfb", "application/x-font-type1"}, +{".pfm", "application/x-font-type1"}, +{".afp", "application/vnd.ibm.modcap"}, +{".list3820", "application/vnd.ibm.modcap"}, +{".listafp", "application/vnd.ibm.modcap"}, +{".ai", "application/postscript"}, +{".eps", "application/postscript"}, +{".ps", "application/postscript"}, +{".aif", "audio/x-aiff"}, +{".aifc", "audio/x-aiff"}, +{".aiff", "audio/x-aiff"}, +{".air", "application/vnd.adobe.air-application-installer-package+zip"}, +{".ami", "application/vnd.amiga.ami"}, +{".apk", "application/vnd.android.package-archive"}, +{".application", "application/x-ms-application"}, +{".apr", "application/vnd.lotus-approach"}, +{".asc", "application/pgp-signature"}, +{".sig", "application/pgp-signature"}, +{".asf", "video/x-ms-asf"}, +{".asx", "video/x-ms-asf"}, +{".asm", "text/x-asm"}, +{".s", "text/x-asm"}, +{".aso", "application/vnd.accpac.simply.aso"}, +{".atom", "application/atom+xml"}, +{".atomcat", "application/atomcat+xml"}, +{".atomsvc", "application/atomsvc+xml"}, +{".atx", "application/vnd.antix.game-component"}, +{".au", "audio/basic"}, +{".snd", "audio/basic"}, +{".avi", "video/x-msvideo"}, +{".aw", "application/applixware"}, +{".azf", "application/vnd.airzip.filesecure.azf"}, +{".azs", "application/vnd.airzip.filesecure.azs"}, +{".azw", "application/vnd.amazon.ebook"}, +{".bat", "application/x-msdownload"}, +{".com", "application/x-msdownload"}, +{".dll", "application/x-msdownload"}, +{".exe", "application/x-msdownload"}, +{".msi", "application/x-msdownload"}, +{".bcpio", "application/x-bcpio"}, +{".bdf", "application/x-font-bdf"}, +{".bdm", "application/vnd.syncml.dm+wbxml"}, +{".bh2", "application/vnd.fujitsu.oasysprs"}, +{".bmi", "application/vnd.bmi"}, +{".bmp", "image/bmp"}, +{".book", "application/vnd.framemaker"}, +{".fm", "application/vnd.framemaker"}, +{".frame", "application/vnd.framemaker"}, +{".maker", "application/vnd.framemaker"}, +{".box", "application/vnd.previewsystems.box"}, +{".boz", "application/x-bzip2"}, +{".bz2", "application/x-bzip2"}, +{".btif", "image/prs.btif"}, +{".bz", "application/x-bzip"}, +{".c", "text/x-c"}, +{".cc", "text/x-c"}, +{".cpp", "text/x-c"}, +{".cxx", "text/x-c"}, +{".dic", "text/x-c"}, +{".h", "text/x-c"}, +{".hh", "text/x-c"}, +{".c4d", "application/vnd.clonk.c4group"}, +{".c4f", "application/vnd.clonk.c4group"}, +{".c4g", "application/vnd.clonk.c4group"}, +{".c4p", "application/vnd.clonk.c4group"}, +{".c4u", "application/vnd.clonk.c4group"}, +{".cab", "application/vnd.ms-cab-compressed"}, +{".car", "application/vnd.curl.car"}, +{".cat", "application/vnd.ms-pki.seccat"}, +{".cct", "application/x-director"}, +{".cst", "application/x-director"}, +{".cxt", "application/x-director"}, +{".dcr", "application/x-director"}, +{".dir", "application/x-director"}, +{".dxr", "application/x-director"}, +{".fgd", "application/x-director"}, +{".swa", "application/x-director"}, +{".w3d", "application/x-director"}, +{".ccxml", "application/ccxml+xml"}, +{".cdbcmsg", "application/vnd.contact.cmsg"}, +{".cdf", "application/x-netcdf"}, +{".nc", "application/x-netcdf"}, +{".cdkey", "application/vnd.mediastation.cdkey"}, +{".cdx", "chemical/x-cdx"}, +{".cdxml", "application/vnd.chemdraw+xml"}, +{".cdy", "application/vnd.cinderella"}, +{".cer", "application/pkix-cert"}, +{".cgm", "image/cgm"}, +{".chat", "application/x-chat"}, +{".chm", "application/vnd.ms-htmlhelp"}, +{".chrt", "application/vnd.kde.kchart"}, +{".cif", "chemical/x-cif"}, +{".cii", "application/vnd.anser-web-certificate-issue-initiation"}, +{".cil", "application/vnd.ms-artgalry"}, +{".cla", "application/vnd.claymore"}, +{".class", "application/java-vm"}, +{".clkk", "application/vnd.crick.clicker.keyboard"}, +{".clkp", "application/vnd.crick.clicker.palette"}, +{".clkt", "application/vnd.crick.clicker.template"}, +{".clkw", "application/vnd.crick.clicker.wordbank"}, +{".clkx", "application/vnd.crick.clicker"}, +{".clp", "application/x-msclip"}, +{".cmc", "application/vnd.cosmocaller"}, +{".cmdf", "chemical/x-cmdf"}, +{".cml", "chemical/x-cml"}, +{".cmp", "application/vnd.yellowriver-custom-menu"}, +{".cmx", "image/x-cmx"}, +{".cod", "application/vnd.rim.cod"}, +{".conf", "text/plain"}, +{".def", "text/plain"}, +{".diff", "text/plain"}, +{".in", "text/plain"}, +{".ksh", "text/plain"}, +{".list", "text/plain"}, +{".log", "text/plain"}, +{".pl", "text/plain"}, +{".text", "text/plain"}, +{".txt", "text/plain"}, +{".deb", "application/vnd.debian.binary-package"}, +{".udeb", "application/vnd.debian.binary-package"}, +{".md", "text/markdown"}, +{".markdown", "text/markdown"}, +{".mdown", "text/markdown"}, +{".markdn", "text/markdown"}, +{".md", "text/x-markdown"}, +{".markdown", "text/x-markdown"}, +{".mdown", "text/x-markdown"}, +{".markdn", "text/x-markdown"}, +{".wasm", "application/wasm"}, +{".cpio", "application/x-cpio"}, +{".cpt", "application/mac-compactpro"}, +{".crd", "application/x-mscardfile"}, +{".crl", "application/pkix-crl"}, +{".crt", "application/x-x509-ca-cert"}, +{".der", "application/x-x509-ca-cert"}, +{".csh", "application/x-csh"}, +{".csml", "chemical/x-csml"}, +{".csp", "application/vnd.commonspace"}, +{".css", "text/css"}, +{".csv", "text/csv"}, +{".cu", "application/cu-seeme"}, +{".curl", "text/vnd.curl"}, +{".cww", "application/prs.cww"}, +{".daf", "application/vnd.mobius.daf"}, +{".dataless", "application/vnd.fdsn.seed"}, +{".seed", "application/vnd.fdsn.seed"}, +{".davmount", "application/davmount+xml"}, +{".dcurl", "text/vnd.curl.dcurl"}, +{".dd2", "application/vnd.oma.dd2+xml"}, +{".ddd", "application/vnd.fujixerox.ddd"}, +{".deb", "application/x-debian-package"}, +{".udeb", "application/x-debian-package"}, +{".dfac", "application/vnd.dreamfactory"}, +{".dis", "application/vnd.mobius.dis"}, +{".djv", "image/vnd.djvu"}, +{".djvu", "image/vnd.djvu"}, +{".dna", "application/vnd.dna"}, +{".doc", "application/msword"}, +{".dot", "application/msword"}, +{".wiz", "application/msword"}, +{".docm", "application/vnd.ms-word.document.macroenabled.12"}, +{".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, +{".dotm", "application/vnd.ms-word.template.macroenabled.12"}, +{".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, +{".dp", "application/vnd.osgi.dp"}, +{".dpg", "application/vnd.dpgraph"}, +{".dsc", "text/prs.lines.tag"}, +{".dtb", "application/x-dtbook+xml"}, +{".dtd", "application/xml-dtd"}, +{".dts", "audio/vnd.dts"}, +{".dtshd", "audio/vnd.dts.hd"}, +{".dvi", "application/x-dvi"}, +{".dwf", "model/vnd.dwf"}, +{".dwg", "image/vnd.dwg"}, +{".dxf", "image/vnd.dxf"}, +{".dxp", "application/vnd.spotfire.dxp"}, +{".ecelp4800", "audio/vnd.nuera.ecelp4800"}, +{".ecelp7470", "audio/vnd.nuera.ecelp7470"}, +{".ecelp9600", "audio/vnd.nuera.ecelp9600"}, +{".ecma", "application/ecmascript"}, +{".edm", "application/vnd.novadigm.edm"}, +{".edx", "application/vnd.novadigm.edx"}, +{".efif", "application/vnd.picsel"}, +{".ei6", "application/vnd.pg.osasli"}, +{".eml", "message/rfc822"}, +{".mht", "message/rfc822"}, +{".mhtml", "message/rfc822"}, +{".mime", "message/rfc822"}, +{".nws", "message/rfc822"}, +{".emma", "application/emma+xml"}, +{".eol", "audio/vnd.digital-winds"}, +{".eot", "application/vnd.ms-fontobject"}, +{".epub", "application/epub+zip"}, +{".es3", "application/vnd.eszigno3+xml"}, +{".et3", "application/vnd.eszigno3+xml"}, +{".esf", "application/vnd.epson.esf"}, +{".etx", "text/x-setext"}, +{".ext", "application/vnd.novadigm.ext"}, +{".ez", "application/andrew-inset"}, +{".ez2", "application/vnd.ezpix-album"}, +{".ez3", "application/vnd.ezpix-package"}, +{".f", "text/x-fortran"}, +{".f77", "text/x-fortran"}, +{".f90", "text/x-fortran"}, +{".for", "text/x-fortran"}, +{".f4v", "video/x-f4v"}, +{".fbs", "image/vnd.fastbidsheet"}, +{".fdf", "application/vnd.fdf"}, +{".fe_launch", "application/vnd.denovo.fcselayout-link"}, +{".fg5", "application/vnd.fujitsu.oasysgp"}, +{".fh", "image/x-freehand"}, +{".fh4", "image/x-freehand"}, +{".fh5", "image/x-freehand"}, +{".fh7", "image/x-freehand"}, +{".fhc", "image/x-freehand"}, +{".fig", "application/x-xfig"}, +{".fli", "video/x-fli"}, +{".flo", "application/vnd.micrografx.flo"}, +{".flv", "video/x-flv"}, +{".flw", "application/vnd.kde.kivio"}, +{".flx", "text/vnd.fmi.flexstor"}, +{".fly", "text/vnd.fly"}, +{".fnc", "application/vnd.frogans.fnc"}, +{".fpx", "image/vnd.fpx"}, +{".fsc", "application/vnd.fsc.weblaunch"}, +{".fst", "image/vnd.fst"}, +{".ftc", "application/vnd.fluxtime.clip"}, +{".fti", "application/vnd.anser-web-funds-transfer-initiation"}, +{".fvt", "video/vnd.fvt"}, +{".fzs", "application/vnd.fuzzysheet"}, +{".g3", "image/g3fax"}, +{".gac", "application/vnd.groove-account"}, +{".gdl", "model/vnd.gdl"}, +{".geo", "application/vnd.dynageo"}, +{".gex", "application/vnd.geometry-explorer"}, +{".gre", "application/vnd.geometry-explorer"}, +{".ggb", "application/vnd.geogebra.file"}, +{".ggt", "application/vnd.geogebra.tool"}, +{".ghf", "application/vnd.groove-help"}, +{".gif", "image/gif"}, +{".gim", "application/vnd.groove-identity-message"}, +{".gmx", "application/vnd.gmx"}, +{".gnumeric", "application/x-gnumeric"}, +{".gph", "application/vnd.flographit"}, +{".gqf", "application/vnd.grafeq"}, +{".gqs", "application/vnd.grafeq"}, +{".gram", "application/srgs"}, +{".grv", "application/vnd.groove-injector"}, +{".grxml", "application/srgs+xml"}, +{".gsf", "application/x-font-ghostscript"}, +{".gtar", "application/x-gtar"}, +{".gtm", "application/vnd.groove-tool-message"}, +{".gtw", "model/vnd.gtw"}, +{".gv", "text/vnd.graphviz"}, +{".gz", "application/x-gzip"}, +{".tgz", "application/x-gzip"}, +{".gz", "application/gzip"}, +{".tgz", "application/gzip"}, +{".h261", "video/h261"}, +{".gcode", "gcode"}, +{".h263", "video/h263"}, +{".h264", "video/h264"}, +{".hbci", "application/vnd.hbci"}, +{".gbr", "application/vnd.gerber"}, +{".hdf", "application/x-hdf"}, +{".hlp", "application/winhlp"}, +{".hpgl", "application/vnd.hp-hpgl"}, +{".hpid", "application/vnd.hp-hpid"}, +{".hps", "application/vnd.hp-hps"}, +{".hqx", "application/mac-binhex40"}, +{".htke", "application/vnd.kenameaapp"}, +{".htm", "text/html"}, +{".html", "text/html"}, +{".hvd", "application/vnd.yamaha.hv-dic"}, +{".hvp", "application/vnd.yamaha.hv-voice"}, +{".hvs", "application/vnd.yamaha.hv-script"}, +{".icc", "application/vnd.iccprofile"}, +{".icm", "application/vnd.iccprofile"}, +{".ice", "x-conference/x-cooltalk"}, +{".ico", "image/x-icon"}, +{".ics", "text/calendar"}, +{".ifb", "text/calendar"}, +{".ief", "image/ief"}, +{".ifm", "application/vnd.shana.informed.formdata"}, +{".iges", "model/iges"}, +{".igs", "model/iges"}, +{".igl", "application/vnd.igloader"}, +{".igx", "application/vnd.micrografx.igx"}, +{".iif", "application/vnd.shana.informed.interchange"}, +{".imp", "application/vnd.accpac.simply.imp"}, +{".ims", "application/vnd.ms-ims"}, +{".ipk", "application/vnd.shana.informed.package"}, +{".irm", "application/vnd.ibm.rights-management"}, +{".irp", "application/vnd.irepository.package+xml"}, +{".itp", "application/vnd.shana.informed.formtemplate"}, +{".ivp", "application/vnd.immervision-ivp"}, +{".ivu", "application/vnd.immervision-ivu"}, +{".jad", "text/vnd.sun.j2me.app-descriptor"}, +{".jam", "application/vnd.jam"}, +{".jar", "application/java-archive"}, +{".java", "text/x-java-source"}, +{".jisp", "application/vnd.jisp"}, +{".jlt", "application/vnd.hp-jlyt"}, +{".jnlp", "application/x-java-jnlp-file"}, +{".joda", "application/vnd.joost.joda-archive"}, +{".jpe", "image/jpeg"}, +{".jpeg", "image/jpeg"}, +{".jpg", "image/jpeg"}, +{".pjpg", "image/jpeg"}, +{".jfif", "image/jpeg"}, +{".jfif-tbnl", "image/jpeg"}, +{".jif", "image/jpeg"}, +{".jpe", "image/pjpeg"}, +{".jpeg", "image/pjpeg"}, +{".jpg", "image/pjpeg"}, +{".pjpg", "image/pjpeg"}, +{".jfi", "image/pjpeg"}, +{".jfif", "image/pjpeg"}, +{".jfif-tbnl", "image/pjpeg"}, +{".jif", "image/pjpeg"}, +{".jpgm", "video/jpm"}, +{".jpm", "video/jpm"}, +{".jpgv", "video/jpeg"}, +{".sh", "application/x-shellscript"}, +{".js", "text/javascript"}, +{".json", "application/json"}, +{".json", "text/json"}, +{".kar", "audio/midi"}, +{".mid", "audio/midi"}, +{".midi", "audio/midi"}, +{".rmi", "audio/midi"}, +{".aiff", "audio/aiff"}, +{".aif", "audio/aiff"}, +{".aff", "audio/aiff"}, +{".opus", "audio/opus"}, +{".karbon", "application/vnd.kde.karbon"}, +{".kfo", "application/vnd.kde.kformula"}, +{".kia", "application/vnd.kidspiration"}, +{".kil", "application/x-killustrator"}, +{".kml", "application/vnd.google-earth.kml+xml"}, +{".kmz", "application/vnd.google-earth.kmz"}, +{".kne", "application/vnd.kinar"}, +{".knp", "application/vnd.kinar"}, +{".kon", "application/vnd.kde.kontour"}, +{".kpr", "application/vnd.kde.kpresenter"}, +{".kpt", "application/vnd.kde.kpresenter"}, +{".ksp", "application/vnd.kde.kspread"}, +{".ktr", "application/vnd.kahootz"}, +{".ktz", "application/vnd.kahootz"}, +{".kwd", "application/vnd.kde.kword"}, +{".kwt", "application/vnd.kde.kword"}, +{".latex", "application/x-latex"}, +{".lbd", "application/vnd.llamagraphics.life-balance.desktop"}, +{".lbe", "application/vnd.llamagraphics.life-balance.exchange+xml"}, +{".les", "application/vnd.hhe.lesson-player"}, +{".link66", "application/vnd.route66.link66+xml"}, +{".lostxml", "application/lost+xml"}, +{".lrm", "application/vnd.ms-lrm"}, +{".ltf", "application/vnd.frogans.ltf"}, +{".lvp", "audio/vnd.lucent.voice"}, +{".lwp", "application/vnd.lotus-wordpro"}, +{".m13", "application/x-msmediaview"}, +{".m14", "application/x-msmediaview"}, +{".mvb", "application/x-msmediaview"}, +{".m1v", "video/mpeg"}, +{".m2v", "video/mpeg"}, +{".mpa", "video/mpeg"}, +{".mpe", "video/mpeg"}, +{".mpeg", "video/mpeg"}, +{".mpg", "video/mpeg"}, +{".m2a", "audio/mpeg"}, +{".m3a", "audio/mpeg"}, +{".mp2", "audio/mpeg"}, +{".mp2a", "audio/mpeg"}, +{".mp3", "audio/mpeg"}, +{".mpga", "audio/mpeg"}, +{".m3u", "audio/x-mpegurl"}, +{".m4u", "video/vnd.mpegurl"}, +{".mxu", "video/vnd.mpegurl"}, +{".m4v", "video/x-m4v"}, +{".ma", "application/mathematica"}, +{".mb", "application/mathematica"}, +{".nb", "application/mathematica"}, +{".mag", "application/vnd.ecowin.chart"}, +{".man", "text/troff"}, +{".me", "text/troff"}, +{".ms", "text/troff"}, +{".roff", "text/troff"}, +{".t", "text/troff"}, +{".tr", "text/troff"}, +{".mathml", "application/mathml+xml"}, +{".mml", "application/mathml+xml"}, +{".mathml", "text/mathml"}, +{".mml", "text/mathml"}, +{".db", "application/vnd.sqlite3"}, +{".sqlite", "application/vnd.sqlite3"}, +{".sqlite3", "application/vnd.sqlite3"}, +{".db-wal", "application/vnd.sqlite3"}, +{".sqlite-wal", "application/vnd.sqlite3"}, +{".db-shm", "application/vnd.sqlite3"}, +{".sqlite-shm", "application/vnd.sqlite3"}, +{".db", "application/x-sqlite3"}, +{".sqlite", "application/x-sqlite3"}, +{".sqlite3", "application/x-sqlite3"}, +{".db-wal", "application/x-sqlite3"}, +{".sqlite-wal", "application/x-sqlite3"}, +{".db-shm", "application/x-sqlite3"}, +{".sqlite-shm", "application/x-sqlite3"}, +{".mbk", "application/vnd.mobius.mbk"}, +{".mbox", "application/mbox"}, +{".mc1", "application/vnd.medcalcdata"}, +{".mcd", "application/vnd.mcd"}, +{".mcurl", "text/vnd.curl.mcurl"}, +{".mdb", "application/x-msaccess"}, +{".mdi", "image/vnd.ms-modi"}, +{".mesh", "model/mesh"}, +{".msh", "model/mesh"}, +{".silo", "model/mesh"}, +{".mfm", "application/vnd.mfmp"}, +{".mgz", "application/vnd.proteus.magazine"}, +{".mif", "application/vnd.mif"}, +{".mj2", "video/mj2"}, +{".mjp2", "video/mj2"}, +{".mlp", "application/vnd.dolby.mlp"}, +{".mmd", "application/vnd.chipnuts.karaoke-mmd"}, +{".mmf", "application/vnd.smaf"}, +{".mmr", "image/vnd.fujixerox.edmics-mmr"}, +{".mny", "application/x-msmoney"}, +{".mobi", "application/x-mobipocket-ebook"}, +{".prc", "application/x-mobipocket-ebook"}, +{".mov", "video/quicktime"}, +{".qt", "video/quicktime"}, +{".movie", "video/x-sgi-movie"}, +{".mp4", "video/mp4"}, +{".mp4v", "video/mp4"}, +{".mpg4", "video/mp4"}, +{".mp4a", "audio/mp4"}, +{".yaml", "application/yaml"}, +{".yml", "application/yaml"}, +{".mp4s", "application/mp4"}, +{".mpc", "application/vnd.mophun.certificate"}, +{".mpkg", "application/vnd.apple.installer+xml"}, +{".mpm", "application/vnd.blueice.multipass"}, +{".mpn", "application/vnd.mophun.application"}, +{".mpp", "application/vnd.ms-project"}, +{".mpt", "application/vnd.ms-project"}, +{".mpy", "application/vnd.ibm.minipay"}, +{".mqy", "application/vnd.mobius.mqy"}, +{".mrc", "application/marc"}, +{".mscml", "application/mediaservercontrol+xml"}, +{".mseed", "application/vnd.fdsn.mseed"}, +{".mseq", "application/vnd.mseq"}, +{".msf", "application/vnd.epson.msf"}, +{".msl", "application/vnd.mobius.msl"}, +{".msty", "application/vnd.muvee.style"}, +{".mts", "model/vnd.mts"}, +{".mus", "application/vnd.musician"}, +{".musicxml", "application/vnd.recordare.musicxml+xml"}, +{".mwf", "application/vnd.mfer"}, +{".mxf", "application/mxf"}, +{".mxl", "application/vnd.recordare.musicxml"}, +{".mxml", "application/xv+xml"}, +{".xhvml", "application/xv+xml"}, +{".xvm", "application/xv+xml"}, +{".xvml", "application/xv+xml"}, +{".mxs", "application/vnd.triscape.mxs"}, +{".n-gage", "application/vnd.nokia.n-gage.symbian.install"}, +{".ncx", "application/x-dtbncx+xml"}, +{".ngdat", "application/vnd.nokia.n-gage.data"}, +{".nlu", "application/vnd.neurolanguage.nlu"}, +{".nml", "application/vnd.enliven"}, +{".nnd", "application/vnd.noblenet-directory"}, +{".nns", "application/vnd.noblenet-sealer"}, +{".nnw", "application/vnd.noblenet-web"}, +{".npx", "image/vnd.net-fpx"}, +{".nsf", "application/vnd.lotus-notes"}, +{".oa2", "application/vnd.fujitsu.oasys2"}, +{".oa3", "application/vnd.fujitsu.oasys3"}, +{".oas", "application/vnd.fujitsu.oasys"}, +{".obd", "application/x-msbinder"}, +{".oda", "application/oda"}, +{".odb", "application/vnd.oasis.opendocument.database"}, +{".odc", "application/vnd.oasis.opendocument.chart"}, +{".odf", "application/vnd.oasis.opendocument.formula"}, +{".odft", "application/vnd.oasis.opendocument.formula-template"}, +{".odg", "application/vnd.oasis.opendocument.graphics"}, +{".odi", "application/vnd.oasis.opendocument.image"}, +{".odp", "application/vnd.oasis.opendocument.presentation"}, +{".ods", "application/vnd.oasis.opendocument.spreadsheet"}, +{".odt", "application/vnd.oasis.opendocument.text"}, +{".oga", "audio/ogg"}, +{".ogg", "audio/ogg"}, +{".spx", "audio/ogg"}, +{".mkv", "video/x-matroska"}, +{".mka", "audio/x-matroska"}, +{".ogv", "video/ogg"}, +{".ogx", "application/ogg"}, +{".onepkg", "application/onenote"}, +{".onetmp", "application/onenote"}, +{".onetoc", "application/onenote"}, +{".onetoc2", "application/onenote"}, +{".opf", "application/oebps-package+xml"}, +{".oprc", "application/vnd.palm"}, +{".pdb", "application/vnd.palm"}, +{".pqa", "application/vnd.palm"}, +{".org", "application/vnd.lotus-organizer"}, +{".osf", "application/vnd.yamaha.openscoreformat"}, +{".osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml"}, +{".otc", "application/vnd.oasis.opendocument.chart-template"}, +{".woff", "font/woff"}, +{".woff2", "font/woff2"}, +{".rpa", "application/x-redhat-package-manager"}, +{".pm", "application/x-perl"}, +{".pl", "application/x-perl"}, +{".weba", "audio/webm"}, +{".webm", "video/webm"}, +{".webp", "image/webp"}, +{".otf", "application/x-font-otf"}, +{".otf", "font/otf"}, +{".otg", "application/vnd.oasis.opendocument.graphics-template"}, +{".oth", "application/vnd.oasis.opendocument.text-web"}, +{".oti", "application/vnd.oasis.opendocument.image-template"}, +{".otm", "application/vnd.oasis.opendocument.text-master"}, +{".otp", "application/vnd.oasis.opendocument.presentation-template"}, +{".ots", "application/vnd.oasis.opendocument.spreadsheet-template"}, +{".ott", "application/vnd.oasis.opendocument.text-template"}, +{".oxt", "application/vnd.openofficeorg.extension"}, +{".p", "text/x-pascal"}, +{".pas", "text/x-pascal"}, +{".pp", "text/x-pascal"}, +{".inc", "text/x-pascal"}, +{".p10", "application/pkcs10"}, +{".p12", "application/x-pkcs12"}, +{".pfx", "application/x-pkcs12"}, +{".p7b", "application/x-pkcs7-certificates"}, +{".spc", "application/x-pkcs7-certificates"}, +{".p7c", "application/pkcs7-mime"}, +{".p7m", "application/pkcs7-mime"}, +{".p7r", "application/x-pkcs7-certreqresp"}, +{".p7s", "application/pkcs7-signature"}, +{".pbd", "application/vnd.powerbuilder6"}, +{".pbm", "image/x-portable-bitmap"}, +{".pcf", "application/x-font-pcf"}, +{".pcl", "application/vnd.hp-pcl"}, +{".pclxl", "application/vnd.hp-pclxl"}, +{".pct", "image/x-pict"}, +{".pic", "image/x-pict"}, +{".pcurl", "application/vnd.curl.pcurl"}, +{".pcx", "image/x-pcx"}, +{".pdf", "application/pdf"}, +{".pfr", "application/font-tdpfr"}, +{".pgm", "image/x-portable-graymap"}, +{".pgn", "application/x-chess-pgn"}, +{".pgp", "application/pgp-encrypted"}, +{".pki", "application/pkixcmp"}, +{".pkipath", "application/pkix-pkipath"}, +{".plb", "application/vnd.3gpp.pic-bw-large"}, +{".plc", "application/vnd.mobius.plc"}, +{".plf", "application/vnd.pocketlearn"}, +{".pls", "application/pls+xml"}, +{".pml", "application/vnd.ctc-posml"}, +{".png", "image/png"}, +{".png", "image/x-png"}, +{".png", "image/vnd.mozilla.apng"}, +{".pnm", "image/x-portable-anymap"}, +{".portpkg", "application/vnd.macports.portpkg"}, +{".pot", "application/vnd.ms-powerpoint"}, +{".ppa", "application/vnd.ms-powerpoint"}, +{".pps", "application/vnd.ms-powerpoint"}, +{".ppt", "application/vnd.ms-powerpoint"}, +{".pwz", "application/vnd.ms-powerpoint"}, +{".potm", "application/vnd.ms-powerpoint.template.macroenabled.12"}, +{".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"}, +{".ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12"}, +{".ppd", "application/vnd.cups-ppd"}, +{".ppm", "image/x-portable-pixmap"}, +{".ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12"}, +{".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, +{".pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12"}, +{".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, +{".pre", "application/vnd.lotus-freelance"}, +{".prf", "application/pics-rules"}, +{".prql", "application/prql"}, +{".psb", "application/vnd.3gpp.pic-bw-small"}, +{".psd", "image/vnd.adobe.photoshop"}, +{".psf", "application/x-font-linux-psf"}, +{".ptid", "application/vnd.pvi.ptid1"}, +{".pub", "application/x-mspublisher"}, +{".pvb", "application/vnd.3gpp.pic-bw-var"}, +{".pwn", "application/vnd.3m.post-it-notes"}, +{".py", "text/x-python"}, +{".pya", "audio/vnd.ms-playready.media.pya"}, +{".pyc", "application/x-python-code"}, +{".pyo", "application/x-python-code"}, +{".pyv", "video/vnd.ms-playready.media.pyv"}, +{".qam", "application/vnd.epson.quickanime"}, +{".qbo", "application/vnd.intu.qbo"}, +{".qfx", "application/vnd.intu.qfx"}, +{".qps", "application/vnd.publishare-delta-tree"}, +{".qwd", "application/vnd.quark.quarkxpress"}, +{".qwt", "application/vnd.quark.quarkxpress"}, +{".qxb", "application/vnd.quark.quarkxpress"}, +{".qxd", "application/vnd.quark.quarkxpress"}, +{".qxl", "application/vnd.quark.quarkxpress"}, +{".qxt", "application/vnd.quark.quarkxpress"}, +{".ra", "audio/x-pn-realaudio"}, +{".ram", "audio/x-pn-realaudio"}, +{".rar", "application/vnd.rar"}, +{".rar", "application/x-rar-compressed"}, +{".ras", "image/x-cmu-raster"}, +{".rcprofile", "application/vnd.ipunplugged.rcprofile"}, +{".rdf", "application/rdf+xml"}, +{".rdz", "application/vnd.data-vision.rdz"}, +{".rep", "application/vnd.businessobjects"}, +{".res", "application/x-dtbresource+xml"}, +{".rgb", "image/x-rgb"}, +{".rif", "application/reginfo+xml"}, +{".rl", "application/resource-lists+xml"}, +{".rlc", "image/vnd.fujixerox.edmics-rlc"}, +{".rld", "application/resource-lists-diff+xml"}, +{".rm", "application/vnd.rn-realmedia"}, +{".rmp", "audio/x-pn-realaudio-plugin"}, +{".rms", "application/vnd.jcp.javame.midlet-rms"}, +{".rnc", "application/relax-ng-compact-syntax"}, +{".rpm", "application/x-rpm"}, +{".rpss", "application/vnd.nokia.radio-presets"}, +{".rpst", "application/vnd.nokia.radio-preset"}, +{".rq", "application/sparql-query"}, +{".rs", "application/rls-services+xml"}, +{".rsd", "application/rsd+xml"}, +{".rss", "application/rss+xml"}, +{".xml", "application/rss+xml"}, +{".rtf", "application/rtf"}, +{".rtx", "text/richtext"}, +{".saf", "application/vnd.yamaha.smaf-audio"}, +{".sbml", "application/sbml+xml"}, +{".sc", "application/vnd.ibm.secure-container"}, +{".scd", "application/x-msschedule"}, +{".scm", "application/vnd.lotus-screencam"}, +{".scq", "application/scvp-cv-request"}, +{".scs", "application/scvp-cv-response"}, +{".scurl", "text/vnd.curl.scurl"}, +{".sda", "application/vnd.stardivision.draw"}, +{".sdc", "application/vnd.stardivision.calc"}, +{".sdd", "application/vnd.stardivision.impress"}, +{".sdkd", "application/vnd.solent.sdkm+xml"}, +{".sdkm", "application/vnd.solent.sdkm+xml"}, +{".sdp", "application/sdp"}, +{".sdw", "application/vnd.stardivision.writer"}, +{".vor", "application/vnd.stardivision.writer"}, +{".see", "application/vnd.seemail"}, +{".sema", "application/vnd.sema"}, +{".semd", "application/vnd.semd"}, +{".semf", "application/vnd.semf"}, +{".ser", "application/java-serialized-object"}, +{".setpay", "application/set-payment-initiation"}, +{".setreg", "application/set-registration-initiation"}, +{".sfd-hdstx", "application/vnd.hydrostatix.sof-data"}, +{".sfs", "application/vnd.spotfire.sfs"}, +{".sgl", "application/vnd.stardivision.writer-global"}, +{".sgm", "text/sgml"}, +{".sgml", "text/sgml"}, +{".sh", "application/x-sh"}, +{".shar", "application/x-shar"}, +{".shf", "application/shf+xml"}, +{".si", "text/vnd.wap.si"}, +{".sic", "application/vnd.wap.sic"}, +{".sis", "application/vnd.symbian.install"}, +{".sisx", "application/vnd.symbian.install"}, +{".sit", "application/x-stuffit"}, +{".sitx", "application/x-stuffitx"}, +{".skd", "application/vnd.koan"}, +{".skm", "application/vnd.koan"}, +{".skp", "application/vnd.koan"}, +{".skt", "application/vnd.koan"}, +{".sl", "text/vnd.wap.sl"}, +{".slc", "application/vnd.wap.slc"}, +{".sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12"}, +{".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"}, +{".slt", "application/vnd.epson.salt"}, +{".smf", "application/vnd.stardivision.math"}, +{".smi", "application/smil+xml"}, +{".smil", "application/smil+xml"}, +{".snf", "application/x-font-snf"}, +{".spf", "application/vnd.yamaha.smaf-phrase"}, +{".spl", "application/x-futuresplash"}, +{".spot", "text/vnd.in3d.spot"}, +{".spp", "application/scvp-vp-response"}, +{".spq", "application/scvp-vp-request"}, +{".src", "application/x-wais-source"}, +{".srx", "application/sparql-results+xml"}, +{".sse", "application/vnd.kodak-descriptor"}, +{".ssf", "application/vnd.epson.ssf"}, +{".ssml", "application/ssml+xml"}, +{".stc", "application/vnd.sun.xml.calc.template"}, +{".std", "application/vnd.sun.xml.draw.template"}, +{".stf", "application/vnd.wt.stf"}, +{".sti", "application/vnd.sun.xml.impress.template"}, +{".stk", "application/hyperstudio"}, +{".stl", "application/vnd.ms-pki.stl"}, +{".str", "application/vnd.pg.format"}, +{".stw", "application/vnd.sun.xml.writer.template"}, +{".sus", "application/vnd.sus-calendar"}, +{".susp", "application/vnd.sus-calendar"}, +{".sv4cpio", "application/x-sv4cpio"}, +{".sv4crc", "application/x-sv4crc"}, +{".svd", "application/vnd.svd"}, +{".svg", "image/svg+xml"}, +{".svgz", "image/svg+xml"}, +{".swf", "application/x-shockwave-flash"}, +{".swi", "application/vnd.arastra.swi"}, +{".sxc", "application/vnd.sun.xml.calc"}, +{".sxd", "application/vnd.sun.xml.draw"}, +{".sxg", "application/vnd.sun.xml.writer.global"}, +{".sxi", "application/vnd.sun.xml.impress"}, +{".sxm", "application/vnd.sun.xml.math"}, +{".sxw", "application/vnd.sun.xml.writer"}, +{".tao", "application/vnd.tao.intent-module-archive"}, +{".tar", "application/x-tar"}, +{".tcap", "application/vnd.3gpp2.tcap"}, +{".tcl", "application/x-tcl"}, +{".teacher", "application/vnd.smart.teacher"}, +{".tex", "application/x-tex"}, +{".texi", "application/x-texinfo"}, +{".texinfo", "application/x-texinfo"}, +{".tfm", "application/x-tex-tfm"}, +{".tif", "image/tiff"}, +{".tiff", "image/tiff"}, +{".tmo", "application/vnd.tmobile-livetv"}, +{".torrent", "application/x-bittorrent"}, +{".tpl", "application/vnd.groove-tool-template"}, +{".tpt", "application/vnd.trid.tpt"}, +{".tra", "application/vnd.trueapp"}, +{".trm", "application/x-msterminal"}, +{".tsv", "text/tab-separated-values"}, +{".ttc", "application/x-font-ttf"}, +{".ttf", "application/x-font-ttf"}, +{".twd", "application/vnd.simtech-mindmapper"}, +{".twds", "application/vnd.simtech-mindmapper"}, +{".txd", "application/vnd.genomatix.tuxedo"}, +{".txf", "application/vnd.mobius.txf"}, +{".ufd", "application/vnd.ufdl"}, +{".ufdl", "application/vnd.ufdl"}, +{".umj", "application/vnd.umajin"}, +{".unityweb", "application/vnd.unity"}, +{".uoml", "application/vnd.uoml+xml"}, +{".uri", "text/uri-list"}, +{".uris", "text/uri-list"}, +{".urls", "text/uri-list"}, +{".ustar", "application/x-ustar"}, +{".utz", "application/vnd.uiq.theme"}, +{".uu", "text/x-uuencode"}, +{".vcd", "application/x-cdlink"}, +{".vcf", "text/x-vcard"}, +{".vcg", "application/vnd.groove-vcard"}, +{".vcs", "text/x-vcalendar"}, +{".vcx", "application/vnd.vcx"}, +{".vis", "application/vnd.visionary"}, +{".viv", "video/vnd.vivo"}, +{".vrml", "model/vrml"}, +{".wrl", "model/vrml"}, +{".vsd", "application/vnd.visio"}, +{".vss", "application/vnd.visio"}, +{".vst", "application/vnd.visio"}, +{".vsw", "application/vnd.visio"}, +{".vsdx", "application/vnd.visio"}, +{".vssx", "application/vnd.visio"}, +{".vstx", "application/vnd.visio"}, +{".vssm", "application/vnd.visio"}, +{".vstm", "application/vnd.visio"}, +{".vsf", "application/vnd.vsf"}, +{".vtu", "model/vnd.vtu"}, +{".vxml", "application/voicexml+xml"}, +{".wad", "application/x-doom"}, +{".ts", "video/mp2t"}, +{".wav", "audio/wav"}, +{".wax", "audio/x-ms-wax"}, +{".wbmp", "image/vnd.wap.wbmp"}, +{".wbs", "application/vnd.criticaltools.wbs+xml"}, +{".wbxml", "application/vnd.wap.wbxml"}, +{".wcm", "application/vnd.ms-works"}, +{".wdb", "application/vnd.ms-works"}, +{".wks", "application/vnd.ms-works"}, +{".wps", "application/vnd.ms-works"}, +{".wm", "video/x-ms-wm"}, +{".wma", "audio/x-ms-wma"}, +{".wmd", "application/x-ms-wmd"}, +{".wmf", "application/x-msmetafile"}, +{".wml", "text/vnd.wap.wml"}, +{".wmlc", "application/vnd.wap.wmlc"}, +{".wmls", "text/vnd.wap.wmlscript"}, +{".wmlsc", "application/vnd.wap.wmlscriptc"}, +{".wmv", "video/x-ms-wmv"}, +{".wmx", "video/x-ms-wmx"}, +{".wmz", "application/x-ms-wmz"}, +{".wpd", "application/vnd.wordperfect"}, +{".wpl", "application/vnd.ms-wpl"}, +{".wqd", "application/vnd.wqd"}, +{".wri", "application/x-mswrite"}, +{".wsdl", "application/wsdl+xml"}, +{".wspolicy", "application/wspolicy+xml"}, +{".wtb", "application/vnd.webturbo"}, +{".wvx", "video/x-ms-wvx"}, +{".x3d", "application/vnd.hzn-3d-crossword"}, +{".xap", "application/x-silverlight-app"}, +{".xar", "application/vnd.xara"}, +{".xbap", "application/x-ms-xbap"}, +{".xbd", "application/vnd.fujixerox.docuworks.binder"}, +{".xbm", "image/x-xbitmap"}, +{".xdm", "application/vnd.syncml.dm+xml"}, +{".xdp", "application/vnd.adobe.xdp+xml"}, +{".xdw", "application/vnd.fujixerox.docuworks"}, +{".xenc", "application/xenc+xml"}, +{".xer", "application/patch-ops-error+xml"}, +{".xfdf", "application/vnd.adobe.xfdf"}, +{".xfdl", "application/vnd.xfdl"}, +{".xht", "application/xhtml+xml"}, +{".xhtml", "application/xhtml+xml"}, +{".xif", "image/vnd.xiff"}, +{".xla", "application/vnd.ms-excel"}, +{".xlb", "application/vnd.ms-excel"}, +{".xlc", "application/vnd.ms-excel"}, +{".xlm", "application/vnd.ms-excel"}, +{".xls", "application/vnd.ms-excel"}, +{".xlt", "application/vnd.ms-excel"}, +{".xlw", "application/vnd.ms-excel"}, +{".xlam", "application/vnd.ms-excel.addin.macroenabled.12"}, +{".xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12"}, +{".xlsm", "application/vnd.ms-excel.sheet.macroenabled.12"}, +{".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, +{".xltm", "application/vnd.ms-excel.template.macroenabled.12"}, +{".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"}, +{".xml", "application/xml"}, +{".xpdl", "application/xml"}, +{".xsl", "application/xml"}, +{".xo", "application/vnd.olpc-sugar"}, +{".xop", "application/xop+xml"}, +{".xpi", "application/x-xpinstall"}, +{".xpm", "image/x-xpixmap"}, +{".xpr", "application/vnd.is-xpr"}, +{".xps", "application/vnd.ms-xpsdocument"}, +{".xpw", "application/vnd.intercon.formnet"}, +{".xpx", "application/vnd.intercon.formnet"}, +{".xslt", "application/xslt+xml"}, +{".xsm", "application/vnd.syncml+xml"}, +{".xspf", "application/xspf+xml"}, +{".xul", "application/vnd.mozilla.xul+xml"}, +{".xwd", "image/x-xwindowdump"}, +{".xyz", "chemical/x-xyz"}, +{".zaz", "application/vnd.zzazz.deck+xml"}, +{".zip", "application/zip"}, +{".zip", "application/x-zip-compressed"}, +{".zip", "application/zip-compressed"}, +{".zir", "application/vnd.zul"}, +{".zirz", "application/vnd.zul"}, +{".zmm", "application/vnd.handheld-entertainment+xml"}, +{".dng", "image/x-adobe-dng"}, +{".arw", "image/x-sony-arw"}, +{".cr2", "image/x-canon-cr2"}, +{".crw", "image/x-canon-crw"}, +{".dcr", "image/x-kodak-dcr"}, +{".erf", "image/x-epson-erf"}, +{".k25", "image/x-kodak-k25"}, +{".kdc", "image/x-kodak-kdc"}, +{".mrw", "image/x-minolta-mrw"}, +{".nef", "image/x-nikon-nef"}, +{".orf", "image/x-olympus-orf"}, +{".pef", "image/x-pentax-pef"}, +{".ptx", "image/x-pentax-pef"}, +{".raf", "image/x-fuji-raf"}, +{".raw", "image/x-panasonic-raw"}, +{".rw2", "image/x-panasonic-raw"}, +{".rwl", "image/x-panasonic-raw"}, +{".flac", "audio/flac"}, +{".sr2", "image/x-sony-sr2"}, +{".srf", "image/x-sony-srf"}, +{".x3f", "image/x-sigma-x3f"} \ No newline at end of file diff --git a/docs/guides/resources/linux-epoll.md b/docs/guides/resources/linux-epoll.md index 1204eb3..35da17c 100644 --- a/docs/guides/resources/linux-epoll.md +++ b/docs/guides/resources/linux-epoll.md @@ -407,10 +407,10 @@ Workflow of `epoll_wait()` : ![epoll_wait.png](/assets/resources/linux-epoll-wait.png) -# Glossary +## Glossary -## Callback +### Callback A callback is a function inside the kernel that is registered to be run automatically when a certain kernel condition occurs. In epoll, the callback is used to inform the epoll subsystem that the state of a monitored file descriptor has changed. @@ -418,7 +418,7 @@ When a file descriptor is added to epoll using epoll_ctl(EPOLL_CTL_ADD), epoll r The callback runs entirely inside the kernel and is not called by user code. Its job is only to record that the file descriptor is ready and to wake up any threads waiting in epoll_wait(). It does not decide which events occurred and does not return data to user space. Because it may run in interrupt or softirq context, the callback is not allowed to sleep. -## Spinlock +### Spinlock A spinlock is a low-level kernel synchronization primitive used to protect shared data structures from concurrent access. When a CPU attempts to acquire a spinlock, it repeatedly checks the lock until it becomes available, without putting the current execution context to sleep. @@ -426,7 +426,7 @@ Spinlocks are used in epoll because parts of the epoll subsystem, including call -### Interrupt Context (Hard Interrupt Context) +#### Interrupt Context (Hard Interrupt Context) Interrupt context is a kernel execution context entered when the CPU receives a hardware interrupt from a device such as a network card, disk controller, or timer. When an interrupt occurs, the CPU immediately suspends the currently running code, switches to kernel mode if necessary, and begins executing the registered interrupt handler. @@ -436,7 +436,7 @@ In the context of epoll, interrupt handlers do not directly invoke epoll logic. ---------- -### Softirq Context +#### Softirq Context Softirq context is a kernel execution context used to perform deferred work that was triggered by a hardware interrupt but could not be completed safely or efficiently within the interrupt handler itself. Softirqs allow the kernel to defer processing while still executing soon after the interrupt and without sleeping. diff --git a/docs/roadmap/phase-1/stage-12.md b/docs/roadmap/phase-1/stage-12.md index 1b42057..43b523a 100644 --- a/docs/roadmap/phase-1/stage-12.md +++ b/docs/roadmap/phase-1/stage-12.md @@ -58,6 +58,7 @@ The function `xps_get_mime()` returns the MIME type of a file based on its exten #include "../xps.h" //here, some extension-mime types pairs are given, you can add as required xps_keyval_t mime_types[] = { + {".h", "text/x-c"}, {".c", "text/x-c"}, {".cc", "text/x-c"}, {".cpp", "text/x-c"}, @@ -86,7 +87,11 @@ const char *xps_get_mime(const char *file_path) { return NULL; } ``` -::: +::: + +:::tip NOTE +If you want to add more **MIME** types, you can refer to this [link](/assets/stage-12/mime_types.txt). +::: As we are mapping the MIME type based on the file extension, a function for finding the file extension is added in `xps_utils.h`. Add the below given function in `xps_utils.c` @@ -386,8 +391,6 @@ In the expserver folder, create a new folder public. Inside this create a file s Now start the server as mentioned in previous stages. Connect a client on port 8002 using `netcat localhost 8002`. Verify that the contents in file sample.txt is received by the client in terminal. Thus we have successfully implemented a basic file server which can send files in local disk to the client. - - ## Conclusion Now all the clients connected to port 8002, would be served the specified file. Thus we have implemented a basic file server. The file server is implemented along with the pipe mechanism itself with the only difference being source reading the file attached to its `ptr` field and writing it to pipe. \ No newline at end of file From adabb0af8ce33a73105e4b7b2bb86cd8e9290a8f Mon Sep 17 00:00:00 2001 From: fayisRahman Date: Thu, 15 Jan 2026 10:24:09 +0000 Subject: [PATCH 4/5] minor fixes --- docs/roadmap/phase-2/stage-17.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/roadmap/phase-2/stage-17.md b/docs/roadmap/phase-2/stage-17.md index 2b4607a..33c85c0 100644 --- a/docs/roadmap/phase-2/stage-17.md +++ b/docs/roadmap/phase-2/stage-17.md @@ -6,7 +6,7 @@ In the previous stages, we updated the server to handle HTTP messages. Stage 14 ## Learning Objectives -- Implement directory browsing to display folder contents +- Implement directory browsing as a fallback when no index file is present - Create a new `xps_directory` module to handle directory listing ## Introduction From 94009a44ff1b4faa12fbafc5409beec2f373f3a5 Mon Sep 17 00:00:00 2001 From: fayisRahman Date: Sat, 17 Jan 2026 07:55:59 +0000 Subject: [PATCH 5/5] fixed some minor issues --- docs/.vitepress/config.mjs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs index cb92e65..a94bed2 100644 --- a/docs/.vitepress/config.mjs +++ b/docs/.vitepress/config.mjs @@ -25,7 +25,6 @@ export default defineConfig({ { text: 'Roadmap', link: '/roadmap/' }, { text: 'Guides', link: '/guides/' }, { text: 'About', link: '/about' }, - // { text: 'Feedback', link: '/feedback' }, ], sidebar: { @@ -49,10 +48,7 @@ export default defineConfig({ text: 'Introduction to Linux epoll', link: '/guides/resources/introduction-to-linux-epoll', }, - // { - // text: 'Linux epoll tutorial', - // link: '/guides/resources/linux-epoll-tutorial', - // }, + { text: 'Linux epoll', link: '/guides/resources/linux-epoll', @@ -62,7 +58,7 @@ export default defineConfig({ link: '/guides/resources/blocking-and-non-blocking-sockets', }, { text: 'HTTP', link: '/guides/resources/http' }, - + // { text: 'Internet Protocol (IP)', link: '/guides/resources/ip' }, // { text: 'File descriptors', link: '/guides/resources/file-descriptors' }, @@ -111,7 +107,7 @@ export default defineConfig({ { text: 'Stage 4: Linux epoll', link: '/roadmap/phase-0/stage-4', - + }, { text: 'Stage 5 a): TCP Proxy', @@ -189,7 +185,7 @@ export default defineConfig({ text: 'Stage 17: Directory Browsing', link: '/roadmap/phase-2/stage-17', }, - + ], }, { @@ -239,9 +235,10 @@ export default defineConfig({ ], }, ], + }, - socialLinks: [{ icon: 'github', link: 'https://github.com/eXpServer' }], + socialLinks: [{ icon: 'github', link: 'https://github.com/eXpServer' }], }, sitemap: { hostname: 'https://expserver.github.io',