diff --git a/docs/.vitepress/config.mjs b/docs/.vitepress/config.mjs index 32f970b..ce5f728 100644 --- a/docs/.vitepress/config.mjs +++ b/docs/.vitepress/config.mjs @@ -46,6 +46,10 @@ export default defineConfig({ text: 'Introduction to Linux epoll', link: '/guides/resources/introduction-to-linux-epoll', }, + { + text: 'Blocking & Non-Blocking Sockets', + 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' }, @@ -119,21 +123,25 @@ export default defineConfig({ link: '/roadmap/phase-1/stage-7', }, { - text: 'Stage 8: Pipe Module', + text: 'Stage 8: Non-Blocking Sockets', link: '/roadmap/phase-1/stage-8', }, { - text: 'Stage 9: Upstream Module', + text: 'Stage 9: Pipe Module', link: '/roadmap/phase-1/stage-9', }, { - text: 'Stage 10: File module', + text: 'Stage 10: Upstream Module', link: '/roadmap/phase-1/stage-10', }, { - text: 'Stage 11: Session Module', + text: 'Stage 11: File module', link: '/roadmap/phase-1/stage-11', }, + { + text: 'Stage 12: Session Module', + link: '/roadmap/phase-1/stage-12', + }, ], }, { @@ -145,21 +153,21 @@ export default defineConfig({ link: '/roadmap/phase-2/', }, { - text: 'Stage 12: HTTP Request Module', - link: '/roadmap/phase-2/stage-12', - }, - { - text: 'Stage 13: HTTP Response Module', + text: 'Stage 13: HTTP Request Module', link: '/roadmap/phase-2/stage-13', }, { - text: 'Stage 14: Config Module', + text: 'Stage 14: HTTP Response Module', link: '/roadmap/phase-2/stage-14', }, { - text: 'Stage 15: HTTP Specification', + text: 'Stage 15: Config Module', link: '/roadmap/phase-2/stage-15', }, + { + text: 'Stage 16: HTTP Specification', + link: '/roadmap/phase-2/stage-16', + }, ], }, { @@ -171,25 +179,25 @@ export default defineConfig({ link: '/roadmap/phase-3/', }, { - text: 'Stage 16: Directory Browsing', - link: '/roadmap/phase-3/stage-16', - }, - { - text: 'Stage 17: IP Whitelist/Blacklist', + text: 'Stage 17: Directory Browsing', link: '/roadmap/phase-3/stage-17', }, { - text: 'Stage 18: Gzip Compression', + text: 'Stage 18: IP Whitelist/Blacklist', link: '/roadmap/phase-3/stage-18', }, { - text: 'Stage 19: Load Balancing', + text: 'Stage 19: Gzip Compression', link: '/roadmap/phase-3/stage-19', }, { - text: 'Stage 20: Request timeouts', + text: 'Stage 20: Load Balancing', link: '/roadmap/phase-3/stage-20', }, + { + text: 'Stage 21: Request timeouts', + link: '/roadmap/phase-3/stage-21', + }, ], }, { @@ -201,17 +209,17 @@ export default defineConfig({ link: '/roadmap/phase-4/', }, { - text: 'Stage 21: Metrics', - link: '/roadmap/phase-4/stage-21', - }, - { - text: 'Stage 22: Multiprocess', + text: 'Stage 22: Metrics', link: '/roadmap/phase-4/stage-22', }, { - text: 'Stage 23: Transport Layer Security (TLS)', + text: 'Stage 23: Multiprocess', link: '/roadmap/phase-4/stage-23', }, + { + text: 'Stage 24: Transport Layer Security (TLS)', + link: '/roadmap/phase-4/stage-24', + }, ], }, ], diff --git a/docs/guides/index.md b/docs/guides/index.md index 4023303..cfe2113 100644 --- a/docs/guides/index.md +++ b/docs/guides/index.md @@ -16,6 +16,7 @@ The guides feature supplementary documentation intended for your reference as yo - ✅ [TCP](/guides/resources/tcp) - ✅ [Sockets](/guides/resources/sockets) - ✅ [Linux epoll](/guides/resources/introduction-to-linux-epoll) +- [Blocking & Non-Blocking Sockets](/guides/resources/blocking-and-non-blocking-sockets) - [HTTP](/guides/resources/http) ## References diff --git a/docs/guides/resources/blocking-and-non-blocking-sockets.md b/docs/guides/resources/blocking-and-non-blocking-sockets.md new file mode 100644 index 0000000..da729fd --- /dev/null +++ b/docs/guides/resources/blocking-and-non-blocking-sockets.md @@ -0,0 +1 @@ +# Blocking & Non-Blocking Sockets diff --git a/docs/roadmap/index.md b/docs/roadmap/index.md index 11df220..ea8c5da 100644 --- a/docs/roadmap/index.md +++ b/docs/roadmap/index.md @@ -34,31 +34,32 @@ The eXpServer project comprises 24 stages, organized into 5 phases. Prior to the - 🟡 [Overview](phase-1/) - 🟡 [Stage 6: Listener & Connection Modules](phase-1/stage-6) - 🟡 [Stage 7: Core & Loop Modules](phase-1/stage-7) -- [Stage 8: Pipe Module](phase-1/stage-8) -- [Stage 9: Upstream Module](phase-1/stage-9) -- [Stage 10: File Module](phase-1/stage-10) -- [Stage 11: Session Module](phase-1/stage-11) +- 🟡 [Stage 8: Non-Blocking Sockets](phase-1/stage-8) +- [Stage 9: Pipe Module](phase-1/stage-9) +- [Stage 10: Upstream Module](phase-1/stage-10) +- [Stage 11: File Module](phase-1/stage-11) +- [Stage 12: Session Module](phase-1/stage-12) ### Phase 2: Implementing HTTP support - [Overview](phase-2/) -- [Stage 12: HTTP Request Module](phase-2/stage-12) -- [Stage 13: HTTP Response Module](phase-2/stage-13) -- [Stage 14: Config Module](phase-2/stage-14) -- [Stage 15: HTTP Specification](phase-2/stage-15) +- [Stage 13: HTTP Request Module](phase-2/stage-13) +- [Stage 14: HTTP Response Module](phase-2/stage-14) +- [Stage 15: Config Module](phase-2/stage-15) +- [Stage 16: HTTP Specification](phase-2/stage-16) ### Phase 3: Adding features to eXpServer - [Overview](phase-3/) -- [Stage 16: Directory Browsing](phase-3/stage-16) -- [Stage 17: IP Whitelist/Blacklist](phase-3/stage-17) -- [Stage 18: Gzip Compression](phase-3/stage-18) -- [Stage 19: Load Balancing](phase-3/stage-19) -- [Stage 20: Request timeouts](phase-3/stage-20) +- [Stage 17: Directory Browsing](phase-3/stage-17) +- [Stage 18: IP Whitelist/Blacklist](phase-3/stage-18) +- [Stage 19: Gzip Compression](phase-3/stage-19) +- [Stage 20: Load Balancing](phase-3/stage-20) +- [Stage 21: Request timeouts](phase-3/stage-21) ### Phase 4: Advanced features and multiprocess architecture - [Overview](phase-4/) -- [Stage 21: Metrics](phase-4/stage-22) -- [Stage 22: Multiprocess](phase-4/stage-22) -- [Stage 23: Transport Layer Security (TLS)](phase-4/stage-23) +- [Stage 22: Metrics](phase-4/stage-22) +- [Stage 23: Multiprocess](phase-4/stage-23) +- [Stage 24: Transport Layer Security (TLS)](phase-4/stage-24) diff --git a/docs/roadmap/phase-1/stage-10.md b/docs/roadmap/phase-1/stage-10.md index b080418..30c3187 100644 --- a/docs/roadmap/phase-1/stage-10.md +++ b/docs/roadmap/phase-1/stage-10.md @@ -1 +1 @@ -# Stage 10: File Module +# Stage 10: Upstream Module diff --git a/docs/roadmap/phase-1/stage-11.md b/docs/roadmap/phase-1/stage-11.md index 911e361..ab250bb 100644 --- a/docs/roadmap/phase-1/stage-11.md +++ b/docs/roadmap/phase-1/stage-11.md @@ -1 +1 @@ -# Stage 11: Session Module +# Stage 11: File Module diff --git a/docs/roadmap/phase-1/stage-12.md b/docs/roadmap/phase-1/stage-12.md new file mode 100644 index 0000000..da7102f --- /dev/null +++ b/docs/roadmap/phase-1/stage-12.md @@ -0,0 +1 @@ +# Stage 12: Session Module diff --git a/docs/roadmap/phase-1/stage-8.md b/docs/roadmap/phase-1/stage-8.md index 7559bfa..db63805 100644 --- a/docs/roadmap/phase-1/stage-8.md +++ b/docs/roadmap/phase-1/stage-8.md @@ -1 +1 @@ -# Stage 8: Pipe Module +# Stage 8: Non-Blocking Sockets diff --git a/docs/roadmap/phase-1/stage-9.md b/docs/roadmap/phase-1/stage-9.md index 2fc6925..a125303 100644 --- a/docs/roadmap/phase-1/stage-9.md +++ b/docs/roadmap/phase-1/stage-9.md @@ -1 +1 @@ -# Stage 9: Upstream Module +# Stage 9: Pipe Module diff --git a/docs/roadmap/phase-2/stage-12.md b/docs/roadmap/phase-2/stage-12.md deleted file mode 100644 index 377d9e4..0000000 --- a/docs/roadmap/phase-2/stage-12.md +++ /dev/null @@ -1,184 +0,0 @@ -# Stage 12: HTTP Request Module - -## Recap - -## Introduction - -You’ve made it to Phase 2! Till now, we only focused on TCP. But this phase will focus on HTTP connections. All the client requests and server responses will be HTTP messages. - -But what is HTTP you may ask. We have made a short and concise description for the same [here](https://www.notion.so/HTTP-e93e4b23676d4d5c9e939e7ae835237a?pvs=21). It is imperative that you have a clear understanding of the structure of HTTP messages as this stage will completely focus on writing a HTTP parser from the ground up! - -For eXpServer, we would only need to parse HTTP request messages. - -As eXpServer IS THE server, it would be the one to generate the HTTP response messages, not read them. But what about the cases when the server acts as a proxy? We will discuss about this in the next stage. - -Let us take an example HTTP request message and see how the parser will work. - -``` -GET https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages/httpmsg2.png HTTP/1.1 -Host: developer.mozilla.org -User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0 -Accept: image/avif,image/webp,*/* -Accept-Language: en-US,en;q=0.5 -Accept-Encoding: gzip, deflate, br -Referer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages -``` - -We will split the parser into 2: - -1. Request line parser -2. Request headers parser - -To assist us with the parsing process, we will use `struct *xps_http_req_s`.\* - -### Request line parser - -The general structure of the an HTTP request line is the following: - -- Request method - GET -- Space -- Request URI - https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages/httpmsg2.png -- Space -- Request HTTP version - HTTP/1.1 - -The request URI can be of different types (refer the HTTP section). And each request URI has multiple subparts within it such as: - -- Schema - https -- Host - developer.mozilla.org -- Port - 443 (since its https, and a specific port is not mentioned in the URI) -- Path - /en-US/docs/Web/HTTP/Messages/httpmsg2.png -- Pathname - -The job of the parser is to take _any_ HTTP request message and be able to: - -1. Check if the HTTP request message is a valid -2. Split the message into different parts as mentioned above - -### Request headers parser - -The structure of a request header is the following: - -- Header key -- Semicolon -- Space -- Header value - -Similar to the request line parser, the request header parser should split each header into key-value pairs. We will store all the key-value pairs in a `vec_void_t headers`, which is part of `struct *xps_http_req_s`.\* - -## Implementation - -Like we mentioned before, we’ll split the HTTP request parser into two parts: - -1. `xps_http_parse_request_line` - to parse the request line -2. `xps_http_parse_header_line` - to parse the each header line - -```c -u_int xps_http_parse_request_line(xps_http_req_t *http_req, xps_buffer_t *buff) { - ... -} - -u_int xps_http_parse_header_line(xps_http_req_t *http_req, xps_buffer_t *buff) { - ... -} -``` - -`xps_buffer_t *buff` contains the HTTP request message that we need to parse. `xps_http_req_t *http_req` is an instance of the struct that we will use for parsing the message. - -If you look into `xps_http_req_t`, we can see that it mostly contains u_char\* pointers. The idea is simple: - -Iterate through the characters one by one. We are aware of the syntax of a standard HTTP request line. We use that to figure out where we are in the iteration step. - -Suppose we are parsing the request line, and the first character the first character. We know that this will be start of the request line, so we set a pointer there. - -```c -http_req->request_line_start = buff[0]; -``` - -We also know that the request line starts with the request method (GET, POST, PUT etc.). Therefore we can also set the request method start to the same position. - -```c -http_req->method_start = buff[0]; -``` - -We also know that the character HAS TO BE in the range(A to Z). We can use rules like these to validate and verify if the request message that we are receiving is of the correct format. - -> ::: tip -> Make use of parser_state (part of `http_req`) defined in `xps_http_parser.h` to keep track of where you are in the parsing process. -> ::: - -Here is some starting code that you can use to write the function. - -```c -#define LF (u_char)'\n' -#define CR (u_char)'\r' -#define CRLF "\r\n" - -u_int xps_http_parse_request_line(xps_http_req_t *http_req, xps_buffer_t *buff) { - u_char *p_ch; - u_int parser_state = http_req->parser_state; - - p_ch = buff->pos; - - for (u_int i = 0; i < buff->len; i++) { - if (i != 0) - p_ch += 1; - u_char ch = *p_ch; - - if (parser_state == RL_START) { - http_req->request_line_start = p_ch; - if (ch == CR || ch == LF) - continue; - if (ch < 'A' || ch > 'Z') - return XPS_HTTP_PARSE_INVALID_METHOD; - - http_req->method_start = p_ch; - parser_state = RL_METHOD; - continue; - } - - else if (parser_state == RL_METHOD) { - ... - } - - ... - - else if (parser_state == RL_LF) { - buff->pos = p_ch + 1; - if (http_req->request_line_end == NULL) - http_req->request_line_end = p_ch; - - http_req->parser_state = RL_START; - buff->pos = p_ch; - return OK; - } - } -} -``` - -You are not restricted to use the above mentioned approach. Coming up with your own type of implementation is well appreciated. - ---- - -### Milestone #1 - -Half way there! We can check if what we wrote can actually parse any HTTP request line. - ---- - -Similarly, we can now write the parser for the HTTP request header line. - -```c -u_int xps_http_parse_header_line(xps_http_req_t *http_req, xps_buffer_t *buff) { - ... -} -``` - -This should be smaller than the other function, and should take very less time. - ---- - -### Milestone #2 - -Let’s check this with some test cases. - -## Conclusion diff --git a/docs/roadmap/phase-2/stage-13.md b/docs/roadmap/phase-2/stage-13.md index ea6adcf..6547679 100644 --- a/docs/roadmap/phase-2/stage-13.md +++ b/docs/roadmap/phase-2/stage-13.md @@ -1 +1,184 @@ -# Stage 13: HTTP Response Module +# Stage 13: HTTP Request Module + +## Recap + +## Introduction + +You’ve made it to Phase 2! Till now, we only focused on TCP. But this phase will focus on HTTP connections. All the client requests and server responses will be HTTP messages. + +But what is HTTP you may ask. We have made a short and concise description for the same [here](https://www.notion.so/HTTP-e93e4b23676d4d5c9e939e7ae835237a?pvs=21). It is imperative that you have a clear understanding of the structure of HTTP messages as this stage will completely focus on writing a HTTP parser from the ground up! + +For eXpServer, we would only need to parse HTTP request messages. + +As eXpServer IS THE server, it would be the one to generate the HTTP response messages, not read them. But what about the cases when the server acts as a proxy? We will discuss about this in the next stage. + +Let us take an example HTTP request message and see how the parser will work. + +``` +GET https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages/httpmsg2.png HTTP/1.1 +Host: developer.mozilla.org +User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0 +Accept: image/avif,image/webp,*/* +Accept-Language: en-US,en;q=0.5 +Accept-Encoding: gzip, deflate, br +Referer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages +``` + +We will split the parser into 2: + +1. Request line parser +2. Request headers parser + +To assist us with the parsing process, we will use `struct *xps_http_req_s`.\* + +### Request line parser + +The general structure of the an HTTP request line is the following: + +- Request method - GET +- Space +- Request URI - https://developer.mozilla.org/en-US/docs/Web/HTTP/Messages/httpmsg2.png +- Space +- Request HTTP version - HTTP/1.1 + +The request URI can be of different types (refer the HTTP section). And each request URI has multiple subparts within it such as: + +- Schema - https +- Host - developer.mozilla.org +- Port - 443 (since its https, and a specific port is not mentioned in the URI) +- Path - /en-US/docs/Web/HTTP/Messages/httpmsg2.png +- Pathname + +The job of the parser is to take _any_ HTTP request message and be able to: + +1. Check if the HTTP request message is a valid +2. Split the message into different parts as mentioned above + +### Request headers parser + +The structure of a request header is the following: + +- Header key +- Semicolon +- Space +- Header value + +Similar to the request line parser, the request header parser should split each header into key-value pairs. We will store all the key-value pairs in a `vec_void_t headers`, which is part of `struct *xps_http_req_s`.\* + +## Implementation + +Like we mentioned before, we’ll split the HTTP request parser into two parts: + +1. `xps_http_parse_request_line` - to parse the request line +2. `xps_http_parse_header_line` - to parse the each header line + +```c +u_int xps_http_parse_request_line(xps_http_req_t *http_req, xps_buffer_t *buff) { + ... +} + +u_int xps_http_parse_header_line(xps_http_req_t *http_req, xps_buffer_t *buff) { + ... +} +``` + +`xps_buffer_t *buff` contains the HTTP request message that we need to parse. `xps_http_req_t *http_req` is an instance of the struct that we will use for parsing the message. + +If you look into `xps_http_req_t`, we can see that it mostly contains u_char\* pointers. The idea is simple: + +Iterate through the characters one by one. We are aware of the syntax of a standard HTTP request line. We use that to figure out where we are in the iteration step. + +Suppose we are parsing the request line, and the first character the first character. We know that this will be start of the request line, so we set a pointer there. + +```c +http_req->request_line_start = buff[0]; +``` + +We also know that the request line starts with the request method (GET, POST, PUT etc.). Therefore we can also set the request method start to the same position. + +```c +http_req->method_start = buff[0]; +``` + +We also know that the character HAS TO BE in the range(A to Z). We can use rules like these to validate and verify if the request message that we are receiving is of the correct format. + +> ::: tip +> Make use of parser_state (part of `http_req`) defined in `xps_http_parser.h` to keep track of where you are in the parsing process. +> ::: + +Here is some starting code that you can use to write the function. + +```c +#define LF (u_char)'\n' +#define CR (u_char)'\r' +#define CRLF "\r\n" + +u_int xps_http_parse_request_line(xps_http_req_t *http_req, xps_buffer_t *buff) { + u_char *p_ch; + u_int parser_state = http_req->parser_state; + + p_ch = buff->pos; + + for (u_int i = 0; i < buff->len; i++) { + if (i != 0) + p_ch += 1; + u_char ch = *p_ch; + + if (parser_state == RL_START) { + http_req->request_line_start = p_ch; + if (ch == CR || ch == LF) + continue; + if (ch < 'A' || ch > 'Z') + return XPS_HTTP_PARSE_INVALID_METHOD; + + http_req->method_start = p_ch; + parser_state = RL_METHOD; + continue; + } + + else if (parser_state == RL_METHOD) { + ... + } + + ... + + else if (parser_state == RL_LF) { + buff->pos = p_ch + 1; + if (http_req->request_line_end == NULL) + http_req->request_line_end = p_ch; + + http_req->parser_state = RL_START; + buff->pos = p_ch; + return OK; + } + } +} +``` + +You are not restricted to use the above mentioned approach. Coming up with your own type of implementation is well appreciated. + +--- + +### Milestone #1 + +Half way there! We can check if what we wrote can actually parse any HTTP request line. + +--- + +Similarly, we can now write the parser for the HTTP request header line. + +```c +u_int xps_http_parse_header_line(xps_http_req_t *http_req, xps_buffer_t *buff) { + ... +} +``` + +This should be smaller than the other function, and should take very less time. + +--- + +### Milestone #2 + +Let’s check this with some test cases. + +## Conclusion diff --git a/docs/roadmap/phase-2/stage-14.md b/docs/roadmap/phase-2/stage-14.md index 8a8f6f8..6eb67dc 100644 --- a/docs/roadmap/phase-2/stage-14.md +++ b/docs/roadmap/phase-2/stage-14.md @@ -1 +1 @@ -# Stage 14: Config Module +# Stage 14: HTTP Response Module diff --git a/docs/roadmap/phase-2/stage-15.md b/docs/roadmap/phase-2/stage-15.md index a04e6ad..b31beb0 100644 --- a/docs/roadmap/phase-2/stage-15.md +++ b/docs/roadmap/phase-2/stage-15.md @@ -1 +1 @@ -# Stage 15: HTTP Specification +# Stage 15: Config Module diff --git a/docs/roadmap/phase-2/stage-16.md b/docs/roadmap/phase-2/stage-16.md new file mode 100644 index 0000000..eb2f87f --- /dev/null +++ b/docs/roadmap/phase-2/stage-16.md @@ -0,0 +1 @@ +# Stage 16: HTTP Specification diff --git a/docs/roadmap/phase-3/stage-16.md b/docs/roadmap/phase-3/stage-16.md deleted file mode 100644 index 89579a1..0000000 --- a/docs/roadmap/phase-3/stage-16.md +++ /dev/null @@ -1 +0,0 @@ -# Stage 16: Directory Browsing diff --git a/docs/roadmap/phase-3/stage-17.md b/docs/roadmap/phase-3/stage-17.md index 1150e47..2ab8576 100644 --- a/docs/roadmap/phase-3/stage-17.md +++ b/docs/roadmap/phase-3/stage-17.md @@ -1 +1 @@ -# Stage 17: IP Whitelist/Blacklist +# Stage 17: Directory Browsing diff --git a/docs/roadmap/phase-3/stage-18.md b/docs/roadmap/phase-3/stage-18.md index 9b141cb..16fdd2e 100644 --- a/docs/roadmap/phase-3/stage-18.md +++ b/docs/roadmap/phase-3/stage-18.md @@ -1 +1 @@ -# Stage 18: Gzip Compression +# Stage 18: IP Whitelist/Blacklist diff --git a/docs/roadmap/phase-3/stage-19.md b/docs/roadmap/phase-3/stage-19.md index 1eae99b..f159712 100644 --- a/docs/roadmap/phase-3/stage-19.md +++ b/docs/roadmap/phase-3/stage-19.md @@ -1 +1 @@ -# Stage 19: Load Balancing +# Stage 19: Gzip Compression diff --git a/docs/roadmap/phase-3/stage-20.md b/docs/roadmap/phase-3/stage-20.md index 28f0d35..612e716 100644 --- a/docs/roadmap/phase-3/stage-20.md +++ b/docs/roadmap/phase-3/stage-20.md @@ -1 +1 @@ -# Stage 20: Request timeouts +# Stage 20: Load Balancing diff --git a/docs/roadmap/phase-3/stage-21.md b/docs/roadmap/phase-3/stage-21.md new file mode 100644 index 0000000..1ef92eb --- /dev/null +++ b/docs/roadmap/phase-3/stage-21.md @@ -0,0 +1 @@ +# Stage 21: Request timeouts diff --git a/docs/roadmap/phase-4/stage-21.md b/docs/roadmap/phase-4/stage-21.md deleted file mode 100644 index c4e6d20..0000000 --- a/docs/roadmap/phase-4/stage-21.md +++ /dev/null @@ -1 +0,0 @@ -# Stage 21: Metrics diff --git a/docs/roadmap/phase-4/stage-22.md b/docs/roadmap/phase-4/stage-22.md index 453930a..9e4ede9 100644 --- a/docs/roadmap/phase-4/stage-22.md +++ b/docs/roadmap/phase-4/stage-22.md @@ -1 +1 @@ -# Stage 22: Multiprocess +# Stage 22: Metrics diff --git a/docs/roadmap/phase-4/stage-23.md b/docs/roadmap/phase-4/stage-23.md index 1e21132..ea9cd91 100644 --- a/docs/roadmap/phase-4/stage-23.md +++ b/docs/roadmap/phase-4/stage-23.md @@ -1 +1 @@ -# Stage 23: Transport Layer Security (TLS) +# Stage 23: Multiprocess diff --git a/docs/roadmap/phase-4/stage-24.md b/docs/roadmap/phase-4/stage-24.md new file mode 100644 index 0000000..6961c2e --- /dev/null +++ b/docs/roadmap/phase-4/stage-24.md @@ -0,0 +1 @@ +# Stage 24: Transport Layer Security (TLS)