88"""
99
1010try :
11- from typing import Callable , Protocol , Union , List , Tuple , Dict , Iterable
12- from socket import socket
13- from socketpool import SocketPool
11+ from typing import Callable , Union , List , Tuple , Dict , Iterable
1412except ImportError :
1513 pass
1614
2826 ServingFilesDisabledError ,
2927)
3028from .headers import Headers
29+ from .interfaces import _ISocketPool , _ISocket
3130from .methods import GET , HEAD
3231from .request import Request
3332from .response import Response , FileResponse
@@ -54,7 +53,7 @@ class Server: # pylint: disable=too-many-instance-attributes
5453 """Root directory to serve files from. ``None`` if serving files is disabled."""
5554
5655 def __init__ (
57- self , socket_source : Protocol , root_path : str = None , * , debug : bool = False
56+ self , socket_source : _ISocketPool , root_path : str = None , * , debug : bool = False
5857 ) -> None :
5958 """Create a server, and get it ready to run.
6059
@@ -172,7 +171,7 @@ def _verify_can_start(self, host: str, port: int) -> None:
172171 raise RuntimeError (f"Cannot start server on { host } :{ port } " ) from error
173172
174173 def serve_forever (
175- self , host : str , port : int = 80 , * , poll_interval : float = 0.1
174+ self , host : str = "0.0.0.0" , port : int = 5000 , * , poll_interval : float = 0.1
176175 ) -> None :
177176 """
178177 Wait for HTTP requests at the given host and port. Does not return.
@@ -195,15 +194,25 @@ def serve_forever(
195194 except Exception : # pylint: disable=broad-except
196195 pass # Ignore exceptions in handler function
197196
198- def _set_socket_level_to_reuse_address (self ) -> None :
199- """
200- Only for CPython, prevents "Address already in use" error when restarting the server.
201- """
202- self ._sock .setsockopt (
203- self ._socket_source .SOL_SOCKET , self ._socket_source .SO_REUSEADDR , 1
204- )
197+ @staticmethod
198+ def _create_server_socket (
199+ socket_source : _ISocketPool ,
200+ host : str ,
201+ port : int ,
202+ ) -> _ISocket :
203+ sock = socket_source .socket (socket_source .AF_INET , socket_source .SOCK_STREAM )
204+
205+ # TODO: Temporary backwards compatibility, remove after CircuitPython 9.0.0 release
206+ if implementation .version >= (9 ,) or implementation .name != "circuitpython" :
207+ sock .setsockopt (socket_source .SOL_SOCKET , socket_source .SO_REUSEADDR , 1 )
208+
209+ sock .bind ((host , port ))
210+ sock .listen (10 )
211+ sock .setblocking (False ) # Non-blocking socket
212+
213+ return sock
205214
206- def start (self , host : str , port : int = 80 ) -> None :
215+ def start (self , host : str = "0.0.0.0" , port : int = 5000 ) -> None :
207216 """
208217 Start the HTTP server at the given host and port. Requires calling
209218 ``.poll()`` in a while loop to handle incoming requests.
@@ -216,16 +225,7 @@ def start(self, host: str, port: int = 80) -> None:
216225 self .host , self .port = host , port
217226
218227 self .stopped = False
219- self ._sock = self ._socket_source .socket (
220- self ._socket_source .AF_INET , self ._socket_source .SOCK_STREAM
221- )
222-
223- if implementation .name != "circuitpython" :
224- self ._set_socket_level_to_reuse_address ()
225-
226- self ._sock .bind ((host , port ))
227- self ._sock .listen (10 )
228- self ._sock .setblocking (False ) # Non-blocking socket
228+ self ._sock = self ._create_server_socket (self ._socket_source , host , port )
229229
230230 if self .debug :
231231 _debug_started_server (self )
@@ -244,9 +244,7 @@ def stop(self) -> None:
244244 if self .debug :
245245 _debug_stopped_server (self )
246246
247- def _receive_header_bytes (
248- self , sock : Union ["SocketPool.Socket" , "socket.socket" ]
249- ) -> bytes :
247+ def _receive_header_bytes (self , sock : _ISocket ) -> bytes :
250248 """Receive bytes until a empty line is received."""
251249 received_bytes = bytes ()
252250 while b"\r \n \r \n " not in received_bytes :
@@ -263,7 +261,7 @@ def _receive_header_bytes(
263261
264262 def _receive_body_bytes (
265263 self ,
266- sock : Union [ "SocketPool.Socket" , "socket.socket" ] ,
264+ sock : _ISocket ,
267265 received_body_bytes : bytes ,
268266 content_length : int ,
269267 ) -> bytes :
@@ -282,7 +280,7 @@ def _receive_body_bytes(
282280
283281 def _receive_request (
284282 self ,
285- sock : Union [ "SocketPool.Socket" , "socket.socket" ] ,
283+ sock : _ISocket ,
286284 client_address : Tuple [str , int ],
287285 ) -> Request :
288286 """Receive bytes from socket until the whole request is received."""
@@ -530,6 +528,13 @@ def socket_timeout(self, value: int) -> None:
530528 else :
531529 raise ValueError ("Server.socket_timeout must be a positive numeric value." )
532530
531+ def __repr__ (self ) -> str :
532+ host = self .host
533+ port = self .port
534+ root_path = self .root_path
535+
536+ return f"<Server { host = } , { port = } , { root_path = } >"
537+
533538
534539def _debug_warning_exposed_files (root_path : str ):
535540 """Warns about exposing all files on the device."""
0 commit comments