Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
aadhavanpl committed Apr 28, 2024
2 parents 6a96922 + 12b02f5 commit 10ff221
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 36 deletions.
7 changes: 2 additions & 5 deletions docs/.vitepress/config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default defineConfig({
collapsed: false,
items: [
{ text: 'Architecture', link: '/guides/resources/architecture' },
{ text: 'Coding Conventions', link: '/guides/resources/coding-conventions' },
{
text: 'TCP/IP Model',
link: '/guides/resources/tcp-ip-model',
Expand Down Expand Up @@ -208,13 +209,9 @@ export default defineConfig({
link: '/roadmap/phase-4/stage-22',
},
{
text: 'Stage 23: Caching',
text: 'Stage 23: Transport Layer Security (TLS)',
link: '/roadmap/phase-4/stage-23',
},
{
text: 'Stage 24: Transport Layer Security (TLS)',
link: '/roadmap/phase-4/stage-24',
},
],
},
],
Expand Down
Binary file modified docs/assets/phase-1-overview/filestructure.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/resources/logs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/guides/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The guides feature supplementary documentation intended for your reference as yo
## Resources

- [Architecture](/guides/resources/architecture)
- [Coding Conventions](/guides/resources/coding-conventions)
- [TCP/IP Model](/guides/resources/tcp-ip-model)
- [TCP](/guides/resources/tcp)
- [Sockets](/guides/resources/sockets)
Expand Down
123 changes: 123 additions & 0 deletions docs/guides/resources/coding-conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Coding Conventions

In order to maintain consistency while developing eXpServer, we will be following a set of [coding conventions](https://en.wikipedia.org/wiki/Coding_conventions). Even though it is possible to develop eXpServer without following these exact conventions, the roadmap has been laid out with the expectation that these conventions will be followed.

## Modules

eXpServer is developed in the form of modules. A module will comprise of a `.h` and `.c` file, eg: `xps_listener.h` and `xps_listener.c`. The header files will contain struct definitions, [function prototypes](https://en.wikipedia.org/wiki/Function_prototype) and other declarations. The C files will contain definitions for the functions.

### **Create and Destroy Functions**

A typical pattern that we will be seeing in modules is the _create_ and _destroy_ functions. For example, the `xps_listener` module has `xps_listener_t *xps_listener_create(...)` and `void xps_listener_destory(xps_listener_t *listener)` functions. These functions are responsible for the following:

- The _create_ functions will allocate memory and initialize the struct for the respective modules, `xps_listener_t` in this example. On success it will return a pointer to the instance and on error it will return `NULL` . Hence the calling function can detect an error by checking if the returned value is `NULL` or not.
- The _destroy_ functions will accept a pointer to an instance of the module struct, `xps_listener_t` in this case, to release memory and properly de-initialize any associated values. Since the destroy functions are always expected to succeed, it will not return any errors.

### **Other Module Functions**

Apart from the create and destroy functions, there could be other functions associated with the module, that can be called from anywhere in the code. Thus they are also declared in the modules’ header file, and its definition in the C file. The function names are prefixed with `xps_`.

### Helper Functions

Additionally, helper functions can be defined and used within the modules. They will be declared at the top of the C file and not in the header file. The function name should be prefixed with the module name without `xps_`, for eg: `listener_connection_handler()`. The function name starts with the module name `xps_istener` without `xps_` as it will be used within the `xps_listener.c` file only.

## Memory Management

As a web server is a software that is expected to run for long intervals of time without shutting down, [memory leaks](https://en.wikipedia.org/wiki/Memory_leak) in the code can lead to huge consumption of system resources which can lead to the OS killing the process. Hence, if memory is allocated, it should be de-allocated after its use.

### Modules

When writing a _create_ function for a module which allocates memory, an accompanying _destroy_ function should be written which will free the memory.

### Strings

- In case of functions that accept strings (character arrays) as parameters, if the function does not modify the string it should be passed as `const char *`.
- eg: `int count_a(const char *str)`. Here the function returns the no. of letter ‘a’ in the string `str`. Since it won’t modify the string `str` it is passed as `const char *`.
- If a function returns a string that should not be modified, then its should be returned as a `const char *`.
- eg: `const char *find_a(const char *str)`. Here the function finds the first occurrence of letter ‘a’ in the string `str`. Since it is a pointer to a byte in the string that should not be modified, it is returned as a `const char *`.
- If a function returns `char *`, that signifies the function has internally allocated memory using `malloc()`. It is the responsibility of the calling function to free that memory.
- eg: `char * str_dup(const char *str)`. Here the function takes in a string and duplicates it. Internally `str_dup()` will allocate memory and copy over the contents from `str` and return a `char *` pointer to the newly allocated string.

> ::: info NOTE
> Memory leaks are hard to detect. Small leaks will only make an observable impact when the program is run for a long time. Hence the use of third party tools like [Valgrind](https://valgrind.org/) will be helpful in debugging memory issues in our code.
> :::
## Error Handling

Unlike more modern languages, error handling is not a built in feature of the C programming language. Hence, there are a set of conventions followed in-order to indicate a function call has resulted in an error. Let us see what they are.

### System Calls

System calls such as `socket()` , `listen()` etc. which are part of [libc](https://en.wikipedia.org/wiki/C_standard_library) usually return `-1` on error and sets the [errno](https://en.wikipedia.org/wiki/Errno.h). We can use the `perror()` function to print the message associated with the last set _errno_. To see the exact return values of function look up their [man pages](https://man7.org/).

### Pointer Return

In eXpServer, if a function has a pointer return type it should return a valid pointer on success and `NULL` on error. eg: `xps_listener_t *xps_listener_create()`.

### Error Code Return

If a function is intended to perform a task that could fail, it should return an integer type with a valid error code. Valid error codes like `OK`, `E_FAIL`, `E_NOTFOUND`, `E_AGAIN`, etc., are defined in the `xps.h` file, seen in the first stage of Phase 1. For example, in the function signature `int xps_loop_attach(...)`, if the call is successful, it will return `OK`; otherwise, it will return `E_FAIL`.

### Pointer Return with Error Code

When a function needs to return a pointer to an instance and report an error code, it's not possible in C to return more than one value directly. In such cases, the return type of the function will be a pointer, and a reference to an integer, `int *error`, is passed to the function by the calling function. On error, NULL is returned, and the error integer is set to a valid error code.

For example: `xps_file_t xps_file_create(..., int *error)`. On successful creation of the file instance, a valid pointer will be returned, and `*error` will be set to `OK`. In case of an error, `NULL` is returned, and `*error` is set to a valid error code such as `E_NOTFOUND`.

### Validating Function Params

When writing functions, start by validating the params first. You can use the [`assert()` macro](https://www.gnu.org/software/gawk/manual/html_node/Assert-Function.html) for this. Assert will stop the code execution and print the error if the expression is false. For example

```c
// Check if str has n 'a' in it
int has_n_a(const char *str, int n) {
assert(str != NULL);
assert(n >= 0);

...
}
```
### Early Return Pattern
The early return pattern for errors is a programming practice where a function exits prematurely upon encountering an error condition, rather than continuing execution. This approach enhances code readability and performance by reducing nested conditionals and maintaining a clear flow of logic. When an error is detected, the function immediately returns by freeing up any allocated memory, closing any opened FDs, destroying and created instances etc. up till that point. For example
```c
// NOTE: This is an example and not the actual function definition
xps_listener_t *xps_listner_create(...) {
// Validate params
int sock_fd = socket(...);
if(sock_fd == -1)
return NULL;
xps_listener_t *listener = malloc(...);
if(listener == NULL) {
close(sock_fd);
return NULL;
}
int error == bind(...);
if(error == -1) {
close(sock_fd);
free(listener);
return NULL;
}
...
return listener; // Success
}
```

## Logging

To help with debugging and understanding the order of function invocations, we will be logging messages throughout the code using the provided `xps_logger` utility. There are primarily 4 levels of logging, `LOG_INFO`, `LOG_WARNING`,`LOG_ERROR` and `LOG_DEBUG` .

![Screenshot 2024-04-27 at 15.36.17.png](/assets/resources/logs.png)

Read more about and get the source code for `xps_logger` [here](https://www.notion.so/xps_logger-9c8f4eb874ff4b0db31d2783197a7708?pvs=21).

## Naming Convention

[Snake case](https://en.wikipedia.org/wiki/Snake_case) convention is used to name all identifiers, i.e. small letters with underscores in place of spaces. For example `my_server`, `xps_buffer.c` etc. File names, function names and type names that are used across multiple files are prefixed with `xps_` , eg: `xps_buffer_create()` .
5 changes: 2 additions & 3 deletions docs/roadmap/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Each stage builds upon the previous one, ensuring a systematic approach to the p

2. **Informative links**: Resources under these links are for more information about a particular concept. The information may not be necessary at that point and you may proceed with the roadmap without visiting these links.

The eXpServer project comprises 22 stages, organized into 5 phases. Prior to the commencement of each phase, participants receive an overview detailing what to anticipate in that phase and summarizing their progress up to that point.
The eXpServer project comprises 24 stages, organized into 5 phases. Prior to the commencement of each phase, participants receive an overview detailing what to anticipate in that phase and summarizing their progress up to that point.

## Stages

Expand Down Expand Up @@ -58,5 +58,4 @@ The eXpServer project comprises 22 stages, organized into 5 phases. Prior to the
- [Overview](phase-4/)
- [Stage 21: Metrics](phase-4/stage-22)
- [Stage 22: Multiprocess](phase-4/stage-22)
- [Stage 23: Caching](phase-4/stage-23)
- [Stage 24: Transport Layer Security (TLS)](phase-4/stage-24)
- [Stage 23: Transport Layer Security (TLS)](phase-4/stage-23)
34 changes: 8 additions & 26 deletions docs/roadmap/phase-1/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ Phase 0 gave us an introduction to Linux socket programming and an understanding
Now is a good time to understand the capabilities and architecture of eXpServer. Read the following [Architecture](/guides/resources/architecture) document before proceeding further.
:::

::: tip PRE-REQUISITE READING
Read the following document about the [Coding Conventions](/guides/resources/coding-conventions) we will be using to implement eXpServer
:::

There will be an apparent jump in the complexity of the code that we will be writing which is normal and expected. We will have to spend a bit more time than Phase 0 for this. Rest assured, this documentation will guide us throughout the process.

## File Structure
Expand All @@ -28,30 +32,8 @@ In order to maintain consistency, the documentation will be providing the expect

### Phase 1 Initial File Structure

![filestructure.png](/assets/phase-1-overview/filestructure.png)

- We will be using a library called `vec` that provides dynamic array implementation. Read about and get the source code for `vec` [here](/guides/references/vec).
- `xps_buffer` is a module used to create instances of data buffers. Read about and get the source code for `xps_buffer` [here](/guides/references/xps_buffer).
- `xps_logger` is a module that provides a function to print log messages for debugging another purposes. Read about and get the source code for `xps_logger` [here](/guides/references/xps_logger).

## Memory Management, Error Handling & Logging

In Phase 0, we did not look into error handling and memory management. We will address these going further.

As a web server is a software that is expected to run for long intervals of time without shutting down, memory leaks in the code can lead to huge consumption of system resources, which is inefficient and can lead to the OS killing the process.

As a web server deals with a lot of asynchronous system calls and network communications, it can lead to a variety of errors and unexpected failures. If these errors are not properly handled, it can lead to the process exiting itself.

Thus, from this phase onwards,

- If memory is allocated, it has to be freed after its use.
- Errors from any function calls should be handled properly
- To help with debugging and understanding the order of function invocations, we will be logging messages throughout the code using the provided `xps_logger` utility. Read about it and get the source code for `xps_logger` [here](/guides/references/xps_logger).

::: tip
Third-party tools such as [Valgrind](https://en.wikipedia.org/wiki/Valgrind) can help to find any memory leaks in the application we write.
:::

## Naming convention
![phase-1-overview-filestructure.png](/assets/phase-1-overview/filestructure.png)

[Snake case](https://en.wikipedia.org/wiki/Snake_case) convention is used to name all identifiers, ie. small letters with underscores in place of spaces. eg: `my_server` , `xps_buffer.c` etc. File names, function names and type names are prefixed with `xps_`
- We will be using a library called `vec` that provides a dynamic array implementation. Read about and get the source code for `vec` [here](/guides/references/vec).
- `xps_logger` module provides a function to print log messages for debugging and other purposes. Read about and get the source code for `xps_logger` [here](/guides/references/xps_logger).
- From this point onward, the code will be independent of what was done in the previous stage. Begin by creating a folder named `src` within `expserver`.
2 changes: 1 addition & 1 deletion docs/roadmap/phase-4/stage-23.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# Stage 23: Caching
# Stage 23: Transport Layer Security (TLS)
1 change: 0 additions & 1 deletion docs/roadmap/phase-4/stage-24.md

This file was deleted.

0 comments on commit 10ff221

Please sign in to comment.