Skip to content

Commit

Permalink
Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
nhairs committed Nov 13, 2024
1 parent 7eeac9b commit 0895a76
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 56 deletions.
9 changes: 3 additions & 6 deletions docs/error-handling.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
# Error Handling

Custom exception handling is handled through the [`ExceptionHandlerMiddleware`][nserver.middleware.ExceptionHandlerMiddleware] and [`RawRecordExceptionHandlerMiddleware`][nserver.middleware.RawRecordExceptionHandlerMiddleware] [Middleware][middleware]. These middleware will catch any `Exception`s raised by their respective middleware stacks.
Custom exception handling is handled through the [`ExceptionHandlerMiddleware`][nserver.middleware.ExceptionHandlerMiddleware] and [`RawExceptionHandlerMiddleware`][nserver.middleware.RawExceptionHandlerMiddleware] [Middleware][middleware]. These middleware will catch any `Exception`s raised by their respective middleware stacks.

!!! note
Error handling requires `nserver>=2.0`

In general you are probably able to use the `ExceptionHandlerMiddleware` as the `RawRecordExceptionHandlerMiddleware` is only needed to catch exceptions resulting from `RawRecordMiddleware` or broken exception handlers in the `ExceptionHandlerMiddleware`. If you only write `QueryMiddleware` and your `ExceptionHandlerMiddleware` handlers never raise exceptions then you'll be good to go with just the `ExceptionHandlerMiddleware`.
In general you are probably able to use the `ExceptionHandlerMiddleware` as the `RawExceptionHandlerMiddleware` is only needed to catch exceptions resulting from `RawMiddleware` or broken exception handlers in the `ExceptionHandlerMiddleware`. If you only write `QueryMiddleware` and your `ExceptionHandlerMiddleware` handlers never raise exceptions then you'll be good to go with just the `ExceptionHandlerMiddleware`.

Both of these middleware have a default exception handler that will be used for anything not matching a registered handler. The default handler can be overwritten by registering a handler for the `Exception` class.

Expand All @@ -16,7 +13,7 @@ Handlers are chosen by finding a handler for the most specific parent class of t

## Registering Exception Handlers

Exception handlers can be registered to `NameServer` and `SubServer` instances using either their `@[raw_]exception_handler` decorators or their `register_[raw_]exception_handler` methods.
Exception handlers can be registered to `NameServer` and `RawNameSeerver` instances using either their `@exception_handler` decorators or their `register_exception_handler` methods.

```python
import dnslib
Expand Down
5 changes: 4 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ NServer has been inspired by easy to use high level frameworks such as [Flask](h

Follow our [Quickstart Guide](quickstart.md).

```python title="TLDR"
```python title="tldr.py"
from nserver import NameServer, Query, A

server = NameServer("example")
Expand All @@ -43,6 +43,9 @@ if __name__ == "__main__":
server.run()
```

```bash
nserver --server tldr.py:server
```

## Bugs, Feature Requests etc
Please [submit an issue on github](https://github.com/nhairs/nserver/issues).
Expand Down
37 changes: 18 additions & 19 deletions docs/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
Middleware can be used to modify the behaviour of a server seperate to the individual rules that are registered to the server. Middleware is run on all requests and can modify both the input and response of a request.

!!! note
Middleware requires `nserver>=2.0`
Middleware requires `nserver>=3.0`

## Middleware Stacks

Middleware operates in a stack with each middleware calling the middleware below it until one returns and the result is propagated back up the chain. NServer uses two stacks, the outmost stack deals with raw DNS records (`RawRecordMiddleware`), which will eventually convert the record to a `Query` which will then be passed to the main `QueryMiddleware` stack.
Middleware operates in a stack with each middleware calling the middleware below it until one returns and the result is propagated back up the chain. NServer uses two stacks, the outmost stack deals with raw DNS records (`RawMiddleware`), which will eventually convert the record to a `Query` which will then be passed to the main `QueryMiddleware` stack.

Middleware can be added to the application until it is run. Once the server begins running the middleware cannot be modified. The ordering of middleware is kept in the order in which it is added to the server; that is the first middleware registered will be called before the second and so on.

Expand Down Expand Up @@ -65,33 +65,34 @@ Once processed the `QueryMiddleware` stack will look as follows:
- `<registered middleware>`
- [`HookMiddleware`][nserver.middleware.HookMiddleware]
- Runs hooks registered to the server. This can be considered a simplified version of middleware.
- [`RuleProcessor`][nserver.middleware.RuleProcessor]
- The entry point into our rule processing.


## `RawRecordMiddleware`
## `RawMiddleware`

[`RawRecordMiddleware`][nserver.middleware.RawRecordMiddleware] allows for modifying the raw `dnslib.DNSRecord`s that are recevied and sent by the server.
[`RawMiddleware`][nserver.middleware.RawMiddleware] allows for modifying the raw `dnslib.DNSRecord`s that are recevied and sent by the server.

### Registering `RawRecordMiddleware`
### Registering `RawMiddleware`

`RawRecordMiddleware` can be registered to `NameServer` instances using their `register_raw_middleware` method.
`RawMiddleware` can be registered to `RawNameServer` instances using their `register_middleware` method.

```python
# ...
from nserver.middleware import RawRecordMiddleware
from nserver import RawNameServer
from nserver.middleware import RawMiddleware

server.register_raw_middleware(RawRecordMiddleware())
raw_server = RawNameServer(server)

server.register_middleware(RawMiddleware())
```

### Creating your own `RawRecordMiddleware`
### Creating your own `RawMiddleware`

Using an unmodified `RawRecordMiddleware` isn't very interesting as it just passes the request onto the next middleware. To add your own middleware you should subclass `RawRecordMiddleware` and override the `process_record` method.
Using an unmodified `RawMiddleware` isn't very interesting as it just passes the request onto the next middleware. To add your own middleware you should subclass `RawMiddleware` and override the `process_record` method.

```python
# ...

class SizeLimiterMiddleware(RawRecordMiddleware):
class SizeLimiterMiddleware(RawMiddleware):
def __init__(self, max_size: int):
super().__init__()
self.max_size = max_size
Expand All @@ -114,15 +115,13 @@ class SizeLimiterMiddleware(RawRecordMiddleware):

return response

server.register_raw_middleware(SizeLimiterMiddleware(1400))
server.register_middleware(SizeLimiterMiddleware(1400))
```

### Default `RawRecordMiddleware` stack
### Default `RawMiddleware` stack

Once processed the `RawRecordMiddleware` stack will look as follows:
Once processed the `RawMiddleware` stack will look as follows:

- [`RawRecordExceptionHandlerMiddleware`][nserver.middleware.RawRecordExceptionHandlerMiddleware]
- [`RawExceptionHandlerMiddleware`][nserver.middleware.RawExceptionHandlerMiddleware]
- Customisable error handler for `Exception`s originating from within the stack.
- `<registered raw middleware>`
- [`QueryMiddlewareProcessor`][nserver.middleware.QueryMiddlewareProcessor]
- entry point into the `QueryMiddleware` stack.
20 changes: 7 additions & 13 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ server = NameServer("example")
@server.rule("example.com", ["A"])
def example_a_records(query: Query):
return A(query.name, "1.2.3.4")

if __name__ == "__main__":
server.run()
```

Here's what this code does:
Expand All @@ -37,28 +34,25 @@ Here's what this code does:

4. When triggered our function will then return a single `A` record as a response.

5. Finally we add code so that we can run our server.

### Running our server

With our server written we can now run it:
With our server written we can now run it using the `nserver` CLI:

```shell
python3 example_server.py
```bash
nserver --server path/to/minimal_server.py
```

```{.none .no-copy}
[INFO] Starting UDPv4Transport(address='localhost', port=9953)
[INFO] Starting UDPv4Transport(address='localhost', port=5300)
```

We can access it using `dig`.

```shell
dig -p 9953 @localhost A example.com
dig -p 5300 @localhost A example.com
```

```{.none .no-copy}
; <<>> DiG 9.18.12-0ubuntu0.22.04.3-Ubuntu <<>> -p 9953 @localhost A example.com
; <<>> DiG 9.18.12-0ubuntu0.22.04.3-Ubuntu <<>> -p 5300 @localhost A example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
Expand All @@ -72,7 +66,7 @@ dig -p 9953 @localhost A example.com
example.com. 300 IN A 1.2.3.4
;; Query time: 324 msec
;; SERVER: 127.0.0.1#9953(localhost) (UDP)
;; SERVER: 127.0.0.1#5300(localhost) (UDP)
;; WHEN: Thu Nov 02 21:27:12 AEDT 2023
;; MSG SIZE rcvd: 45
```
Expand Down
30 changes: 13 additions & 17 deletions docs/subserver-blueprint.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

## Sub-Servers

[`SubServer`][nserver.server.SubServer] provides a way for you to compose your application. They support most of the same functionality as a `NameServer`.
To allow for composing an application into different parts, a [`NameServer`][nserver.server.NameServer] can be included in another `NameServer`.

Use cases:

Expand All @@ -12,15 +12,15 @@ Use cases:
- Allow custom packages to define their own rules that you can add to your own server.

!!! note
SubServers requires `nserver>=2.0`
Adding a `NameServer` to another requires `nserver>=3.0`

### Using Sub-Servers

```python
from nserver import SubServer, NameServer, ZoneRule, ALL_CTYPES, A, TXT
from nserver import NameServer, ZoneRule, ALL_CTYPES, A, TXT

# First SubServer
mysite = SubServer("mysite")
# First child NameServer
mysite = NameServer("mysite")

@mysite.rule("nicholashairs.com", ["A"])
@mysite.rule("www.nicholashairs.com", ["A"])
Expand All @@ -32,37 +32,33 @@ def nicholashairs_catchall(query: Query) -> None:
# Return empty response for all other queries
return None

# Second SubServer
en_subserver = SubServer("english-speaking-blueprint")
# Second child NameServer
en_subserver = NameServer("english-speaking-blueprint")

@en_subserver.rule("hello.{base_domain}", ["TXT"])
def en_hello(query: Query) -> TXT:
return TXT(query.name, "Hello There!")

# Register to NameServer
# Register to main NameServer
server = NameServer("server")
server.register_subserver(mysite, ZoneRule, "nicholashairs.com", ALL_CTYPES)
server.register_subserver(en_subserver, ZoneRule, "au", ALL_CTYPES)
server.register_subserver(en_subserver, ZoneRule, "nz", ALL_CTYPES)
server.register_subserver(en_subserver, ZoneRule, "uk", ALL_CTYPES)
```

#### Middleware, Hooks, and Error Handling
#### Middleware, Hooks, and Exception Handling

Sub-Servers maintain their own `QueryMiddleware` stack which will run before any rule function is run. Included in this stack is the `HookMiddleware` and `ExceptionHandlerMiddleware`.
Don't forget that each `NameServer` maintains it's own middleware stack, exception handlers, and hooks.

### Key differences with `NameServer`

- Does not use settings (`Setting`).
- Does not have a `Transport`.
- Does not have a `RawRecordMiddleware` stack.
In particular errors will not propagate up from a child server to it's parent as the child's exception handler will catch any exception and return a response.

## Blueprints

[`Blueprint`][nserver.server.Blueprint]s act as a container for rules. They are an efficient way to compose your application if you do not want or need to use functionality provided by a `QueryMiddleware` stack.

!!! note
Blueprints require `nserver>=2.0`
Blueprints require `nserver>=3.0`

### Using Blueprints

Expand All @@ -88,7 +84,7 @@ en_subserver.register_rule(no_email_blueprint)
mysite.rules.insert(0, no_email_blueprint)
```

### Key differences with `NameServer` and `SubServer`
### Key differences with `NameServer`

- Only provides the `@rule` decorator and `register_rule` method.
- It does not have a `QueryMiddleware` stack which means it does not support hooks or error-handling.
Expand Down

0 comments on commit 0895a76

Please sign in to comment.