Skip to content

Commit f62899c

Browse files
committed
Added Server.headers and updated docs for it
1 parent 06dcf7a commit f62899c

File tree

4 files changed

+49
-4
lines changed

4 files changed

+49
-4
lines changed

adafruit_httpserver/server.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"""
99

1010
try:
11-
from typing import Callable, Protocol, Union, List, Set, Tuple
11+
from typing import Callable, Protocol, Union, List, Set, Tuple, Dict
1212
from socket import socket
1313
from socketpool import SocketPool
1414
except ImportError:
@@ -25,14 +25,15 @@
2525
InvalidPathError,
2626
ServingFilesDisabledError,
2727
)
28+
from .headers import Headers
2829
from .methods import GET, HEAD
2930
from .request import Request
3031
from .response import Response, FileResponse
3132
from .route import _Routes, _Route
3233
from .status import BAD_REQUEST_400, UNAUTHORIZED_401, FORBIDDEN_403, NOT_FOUND_404
3334

3435

35-
class Server:
36+
class Server: # pylint: disable=too-many-instance-attributes
3637
"""A basic socket-based HTTP server."""
3738

3839
host: str = None
@@ -57,6 +58,7 @@ def __init__(
5758
self._routes = _Routes()
5859
self._socket_source = socket_source
5960
self._sock = None
61+
self.headers = Headers()
6062
self.root_path = root_path
6163
if root_path in ["", "/"] and debug:
6264
_debug_warning_exposed_files(root_path)
@@ -306,6 +308,12 @@ def _handle_request(
306308
status=NOT_FOUND_404,
307309
)
308310

311+
def _set_default_server_headers(self, response: Response) -> None:
312+
for name, value in self.headers.items():
313+
response._headers.setdefault( # pylint: disable=protected-access
314+
name, value
315+
)
316+
309317
def poll(self):
310318
"""
311319
Call this method inside your main loop to get the server to check for new incoming client
@@ -334,6 +342,8 @@ def poll(self):
334342
if response is None:
335343
return
336344

345+
self._set_default_server_headers(response)
346+
337347
# Send the response
338348
response._send() # pylint: disable=protected-access
339349

@@ -366,6 +376,27 @@ def require_authentication(self, auths: List[Union[Basic, Bearer]]) -> None:
366376
"""
367377
self._auths = auths
368378

379+
@property
380+
def headers(self) -> Headers:
381+
"""
382+
Headers to be sent with every response, without the need to specify them in each handler.
383+
384+
If a header is specified in both the handler and the server, the handler's header will be
385+
used.
386+
387+
Example::
388+
389+
server = Server(pool, "/static")
390+
server.headers = {
391+
"Access-Control-Allow-Origin": "*",
392+
}
393+
"""
394+
return self._headers
395+
396+
@headers.setter
397+
def headers(self, value: Union[Headers, Dict[str, str]]) -> None:
398+
self._headers = value.copy() if isinstance(value, Headers) else Headers(value)
399+
369400
@property
370401
def request_buffer_size(self) -> int:
371402
"""

docs/examples.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,12 @@ Get CPU information
9797
You can return data from sensors or any computed value as JSON.
9898
That makes it easy to use the data in other applications.
9999

100+
If you want to use the data in a web browser, it might be necessary to enable CORS.
101+
More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
102+
100103
.. literalinclude:: ../examples/httpserver_cpu_information.py
101104
:caption: examples/httpserver_cpu_information.py
102-
:emphasize-lines: 9,27
105+
:emphasize-lines: 9,14-17,32
103106
:linenos:
104107

105108
Handling different methods
@@ -233,12 +236,15 @@ Using ``.serve_forever()`` for this is not possible because of it's blocking beh
233236

234237
Each server **must have a different port number**.
235238

239+
In order to distinguish between responses from different servers a 'X-Server' header is added to each response.
240+
**This is an optional step**, both servers will work without it.
241+
236242
In combination with separate authentication and diffrent ``root_path`` this allows creating moderately complex setups.
237243
You can share same handler functions between servers or use different ones for each server.
238244

239245
.. literalinclude:: ../examples/httpserver_multiple_servers.py
240246
:caption: examples/httpserver_multiple_servers.py
241-
:emphasize-lines: 13-14,17,25,33-34,45-46,51-52
247+
:emphasize-lines: 13-14,16-17,20,28,36-37,48-49,54-55
242248
:linenos:
243249

244250
Debug mode

examples/httpserver_cpu_information.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
pool = socketpool.SocketPool(wifi.radio)
1212
server = Server(pool, debug=True)
1313

14+
# (Optional) Allow cross-origin requests.
15+
server.headers = {
16+
"Access-Control-Allow-Origin": "*",
17+
}
18+
1419

1520
@server.route("/cpu-information", append_slash=True)
1621
def cpu_information_handler(request: Request):

examples/httpserver_multiple_servers.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@
1111
pool = socketpool.SocketPool(wifi.radio)
1212

1313
bedroom_server = Server(pool, "/bedroom", debug=True)
14+
bedroom_server.headers["X-Server"] = "Bedroom"
15+
1416
office_server = Server(pool, "/office", debug=True)
17+
office_server.headers["X-Server"] = "Office"
1518

1619

1720
@bedroom_server.route("/bedroom")

0 commit comments

Comments
 (0)