22import threading
33import json
44from collections import defaultdict
5+ from enum import Enum
56from typing import Callable , Mapping , Optional , Union
67from ssl import SSLContext
78
@@ -296,6 +297,12 @@ def match(self, request: Request) -> RequestHandler:
296297 return None
297298
298299
300+ class HandlerType (Enum ):
301+ PERMANENT = 'permanent'
302+ ONESHOT = 'oneshot'
303+ ORDERED = 'ordered'
304+
305+
299306class HTTPServer : # pylint: disable=too-many-instance-attributes
300307 """
301308 Server instance which manages handlers to serve pre-defined requests.
@@ -398,29 +405,30 @@ def create_matcher(self, *args, **kwargs) -> RequestMatcher:
398405
399406 return RequestMatcher (* args , ** kwargs )
400407
401- def expect_oneshot_request (
408+ def expect_request (
402409 self ,
403410 uri : str ,
404411 method : str = METHOD_ALL ,
405412 data : Union [str , bytes , None ] = None ,
406413 data_encoding : str = "utf-8" ,
407414 headers : Optional [Mapping [str , str ]] = None ,
408415 query_string : Optional [str ] = None ,
409- * ,
410- ordered = False ,
411- header_value_matcher : Optional [HeaderValueMatcher ] = None ) -> RequestHandler :
416+ header_value_matcher : Optional [HeaderValueMatcher ] = None ,
417+ handler_type : HandlerType = HandlerType .PERMANENT ) -> RequestHandler :
412418 """
413- Create and register a oneshot request handler.
419+ Create and register a request handler.
414420
415- This handler can be only used once. Once the server serves a response for this handler,
416- the handler will be dropped.
421+ If `handler_type` is `HandlerType.PERMANENT` a permanent request handler is created. This handler can be used as
422+ many times as the request matches it, but ordered handlers have higher priority so if there's one or more
423+ ordered handler registered, those must be used first.
417424
418- Ordered handler (when `ordered` parameter is `True`) also determines the
419- order of the requests to be served. For example if there are two ordered handlers
420- registered, the first request must hit the first handler, and the second request must hit the
421- second one, and not vica versa.
425+ If `handler_type` is `HandlerType.ONESHOT` a oneshot request handler is created. This handler can be only used
426+ once. Once the server serves a response for this handler, the handler will be dropped.
422427
423- If one or more ordered handler defined, those must be exhausted first.
428+ If `handler_type` is `HandlerType.ORDERED` an ordered request handler is created. Comparing to oneshot handler,
429+ ordered handler also determines the order of the requests to be served. For example if there are two ordered
430+ handlers registered, the first request must hit the first handler, and the second request must hit the second
431+ one, and not vice versa. If one or more ordered handler defined, those must be exhausted first.
424432
425433 :param uri: URI of the request. This must be an absolute path starting with ``/``.
426434 :param method: HTTP method of the request. If not specified (or `METHOD_ALL`
@@ -430,8 +438,8 @@ def expect_oneshot_request(
430438 :param data_encoding: the encoding used for data parameter if data is a string.
431439 :param headers: dictionary of the headers of the request to be matched
432440 :param query_string: the http query string starting with ``?``, such as ``?username=user``
433- :param ordered: specifies whether to create an ordered handler or not. See above for details.
434441 :param header_value_matcher: :py:class:`HeaderValueMatcher` that matches values of headers.
442+ :param handler_type: type of handler
435443
436444 :return: Created and register :py:class:`RequestHandler`.
437445 """
@@ -446,14 +454,15 @@ def expect_oneshot_request(
446454 header_value_matcher = header_value_matcher ,
447455 )
448456 request_handler = RequestHandler (matcher )
449- if ordered :
450- self .ordered_handlers .append (request_handler )
451- else :
457+ if handler_type == HandlerType . PERMANENT :
458+ self .handlers .append (request_handler )
459+ elif handler_type == HandlerType . ONESHOT :
452460 self .oneshot_handlers .append (request_handler )
453-
461+ elif handler_type == HandlerType .ORDERED :
462+ self .ordered_handlers .append (request_handler )
454463 return request_handler
455464
456- def expect_request (
465+ def expect_oneshot_request (
457466 self ,
458467 uri : str ,
459468 method : str = METHOD_ALL ,
@@ -463,10 +472,9 @@ def expect_request(
463472 query_string : Optional [str ] = None ,
464473 header_value_matcher : Optional [HeaderValueMatcher ] = None ) -> RequestHandler :
465474 """
466- Create and register a permanent request handler.
475+ Create and register a oneshot request handler.
467476
468- This handler can be used as many times as the request matches it, but ordered handlers
469- have higher priority so if there's one or more ordered handler registered, those must be used first.
477+ This is a method for convenience. See :py:meth:`expect_request` for documentation.
470478
471479 :param uri: URI of the request. This must be an absolute path starting with ``/``.
472480 :param method: HTTP method of the request. If not specified (or `METHOD_ALL`
@@ -475,24 +483,60 @@ def expect_request(
475483 by default, see `data_encoding`) or a bytes object.
476484 :param data_encoding: the encoding used for data parameter if data is a string.
477485 :param headers: dictionary of the headers of the request to be matched
478- :param ordered: specifies whether to create an ordered handler or not. See above for details.
486+ :param query_string: the http query string starting with ``?``, such as ``?username=user``
479487 :param header_value_matcher: :py:class:`HeaderValueMatcher` that matches values of headers.
480488
481489 :return: Created and register :py:class:`RequestHandler`.
482490 """
483491
484- matcher = self .create_matcher (
485- uri ,
492+ return self .expect_request (
493+ uri = uri ,
486494 method = method ,
487495 data = data ,
488496 data_encoding = data_encoding ,
489497 headers = headers ,
490498 query_string = query_string ,
491499 header_value_matcher = header_value_matcher ,
500+ handler_type = HandlerType .ONESHOT ,
501+ )
502+
503+ def expect_ordered_request (
504+ self ,
505+ uri : str ,
506+ method : str = METHOD_ALL ,
507+ data : Union [str , bytes , None ] = None ,
508+ data_encoding : str = "utf-8" ,
509+ headers : Optional [Mapping [str , str ]] = None ,
510+ query_string : Optional [str ] = None ,
511+ header_value_matcher : Optional [HeaderValueMatcher ] = None ) -> RequestHandler :
512+ """
513+ Create and register a ordered request handler.
514+
515+ This is a method for convenience. See :py:meth:`expect_request` for documentation.
516+
517+ :param uri: URI of the request. This must be an absolute path starting with ``/``.
518+ :param method: HTTP method of the request. If not specified (or `METHOD_ALL`
519+ specified), all HTTP requests will match.
520+ :param data: payload of the HTTP request. This could be a string (utf-8 encoded
521+ by default, see `data_encoding`) or a bytes object.
522+ :param data_encoding: the encoding used for data parameter if data is a string.
523+ :param headers: dictionary of the headers of the request to be matched
524+ :param query_string: the http query string starting with ``?``, such as ``?username=user``
525+ :param header_value_matcher: :py:class:`HeaderValueMatcher` that matches values of headers.
526+
527+ :return: Created and register :py:class:`RequestHandler`.
528+ """
529+
530+ return self .expect_request (
531+ uri = uri ,
532+ method = method ,
533+ data = data ,
534+ data_encoding = data_encoding ,
535+ headers = headers ,
536+ query_string = query_string ,
537+ header_value_matcher = header_value_matcher ,
538+ handler_type = HandlerType .ORDERED ,
492539 )
493- request_handler = RequestHandler (matcher )
494- self .handlers .append (request_handler )
495- return request_handler
496540
497541 def thread_target (self ):
498542 """
0 commit comments