2018-09-20 06:28:18 +00:00
# Copyright 2018 New Vector Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
2018-06-27 09:37:24 +00:00
import re
2022-05-11 11:24:48 +00:00
from http import HTTPStatus
2022-06-28 12:12:17 +00:00
from typing import Awaitable , Callable , Dict , NoReturn , Optional , Tuple
2018-09-20 06:28:18 +00:00
2018-06-27 09:37:24 +00:00
from twisted . internet . defer import Deferred
2018-09-20 06:28:18 +00:00
from twisted . web . resource import Resource
2018-06-27 09:37:24 +00:00
2020-01-15 15:58:55 +00:00
from synapse . api . errors import Codes , RedirectException , SynapseError
2020-06-16 11:44:07 +00:00
from synapse . config . server import parse_listener_def
2022-05-11 11:24:48 +00:00
from synapse . http . server import (
DirectServeHtmlResource ,
DirectServeJsonResource ,
JsonResource ,
OptionsResource ,
)
from synapse . http . site import SynapseRequest , SynapseSite
2019-07-03 14:07:04 +00:00
from synapse . logging . context import make_deferred_yieldable
2022-05-11 11:24:48 +00:00
from synapse . types import JsonDict
2018-07-09 06:09:20 +00:00
from synapse . util import Clock
2022-08-31 11:16:05 +00:00
from synapse . util . cancellation import cancellable
2018-07-09 06:09:20 +00:00
2018-06-27 09:37:24 +00:00
from tests import unittest
2022-06-07 17:17:32 +00:00
from tests . http . server . _base import test_disconnect
2019-01-24 10:31:54 +00:00
from tests . server import (
2022-06-28 12:12:17 +00:00
FakeChannel ,
2020-11-13 22:39:09 +00:00
FakeSite ,
2019-01-24 10:31:54 +00:00
ThreadedMemoryReactorClock ,
make_request ,
setup_test_homeserver ,
)
2018-06-27 09:37:24 +00:00
class JsonResourceTests ( unittest . TestCase ) :
2022-06-28 12:12:17 +00:00
def setUp ( self ) - > None :
2019-01-24 10:31:54 +00:00
self . reactor = ThreadedMemoryReactorClock ( )
2018-06-27 09:37:24 +00:00
self . hs_clock = Clock ( self . reactor )
self . homeserver = setup_test_homeserver (
2020-12-02 16:09:24 +00:00
self . addCleanup ,
federation_http_client = None ,
clock = self . hs_clock ,
reactor = self . reactor ,
2018-06-27 09:37:24 +00:00
)
2022-06-28 12:12:17 +00:00
def test_handler_for_request ( self ) - > None :
2018-06-27 09:37:24 +00:00
"""
JsonResource . handler_for_request gives correctly decoded URL args to
the callback , while Twisted will give the raw bytes of URL query
arguments .
"""
got_kwargs = { }
2022-06-28 12:12:17 +00:00
def _callback (
request : SynapseRequest , * * kwargs : object
) - > Tuple [ int , Dict [ str , object ] ] :
2018-06-27 09:37:24 +00:00
got_kwargs . update ( kwargs )
2019-08-30 15:28:26 +00:00
return 200 , kwargs
2018-06-27 09:37:24 +00:00
res = JsonResource ( self . homeserver )
2018-07-17 10:43:18 +00:00
res . register_paths (
2019-07-24 12:07:35 +00:00
" GET " ,
[ re . compile ( " ^/_matrix/foo/(?P<room_id>[^/]*)$ " ) ] ,
_callback ,
" test_servlet " ,
2018-07-17 10:43:18 +00:00
)
2018-06-27 09:37:24 +00:00
2020-12-15 14:54:41 +00:00
make_request (
2021-09-24 10:01:25 +00:00
self . reactor ,
FakeSite ( res , self . reactor ) ,
b " GET " ,
b " /_matrix/foo/ %E 2 %98% 83?a= %E 2 %98% 83 " ,
2018-11-06 16:00:00 +00:00
)
2018-06-27 09:37:24 +00:00
self . assertEqual ( got_kwargs , { " room_id " : " \N{SNOWMAN} " } )
2022-06-28 12:12:17 +00:00
def test_callback_direct_exception ( self ) - > None :
2018-06-27 09:37:24 +00:00
"""
If the web callback raises an uncaught exception , it will be translated
into a 500.
"""
2022-06-28 12:12:17 +00:00
def _callback ( request : SynapseRequest , * * kwargs : object ) - > NoReturn :
2018-06-27 09:37:24 +00:00
raise Exception ( " boo " )
res = JsonResource ( self . homeserver )
2019-07-24 12:07:35 +00:00
res . register_paths (
" GET " , [ re . compile ( " ^/_matrix/foo$ " ) ] , _callback , " test_servlet "
)
2018-06-27 09:37:24 +00:00
2021-09-24 10:01:25 +00:00
channel = make_request (
self . reactor , FakeSite ( res , self . reactor ) , b " GET " , b " /_matrix/foo "
)
2018-06-27 09:37:24 +00:00
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 500 )
2018-06-27 09:37:24 +00:00
2022-06-28 12:12:17 +00:00
def test_callback_indirect_exception ( self ) - > None :
2018-06-27 09:37:24 +00:00
"""
If the web callback raises an uncaught exception in a Deferred , it will
be translated into a 500.
"""
2022-06-28 12:12:17 +00:00
def _throw ( * args : object ) - > NoReturn :
2018-06-27 09:37:24 +00:00
raise Exception ( " boo " )
2022-06-28 12:12:17 +00:00
def _callback ( request : SynapseRequest , * * kwargs : object ) - > " Deferred[None] " :
d : " Deferred[None] " = Deferred ( )
2018-06-27 09:37:24 +00:00
d . addCallback ( _throw )
2021-09-24 10:01:25 +00:00
self . reactor . callLater ( 0.5 , d . callback , True )
2018-11-15 22:46:51 +00:00
return make_deferred_yieldable ( d )
2018-06-27 09:37:24 +00:00
res = JsonResource ( self . homeserver )
2019-07-24 12:07:35 +00:00
res . register_paths (
" GET " , [ re . compile ( " ^/_matrix/foo$ " ) ] , _callback , " test_servlet "
)
2018-06-27 09:37:24 +00:00
2021-09-24 10:01:25 +00:00
channel = make_request (
self . reactor , FakeSite ( res , self . reactor ) , b " GET " , b " /_matrix/foo "
)
2018-06-27 09:37:24 +00:00
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 500 )
2018-06-27 09:37:24 +00:00
2022-06-28 12:12:17 +00:00
def test_callback_synapseerror ( self ) - > None :
2018-06-27 09:37:24 +00:00
"""
If the web callback raises a SynapseError , it returns the appropriate
status code and message set in it .
"""
2022-06-28 12:12:17 +00:00
def _callback ( request : SynapseRequest , * * kwargs : object ) - > NoReturn :
2018-06-27 09:37:24 +00:00
raise SynapseError ( 403 , " Forbidden!!one! " , Codes . FORBIDDEN )
res = JsonResource ( self . homeserver )
2019-07-24 12:07:35 +00:00
res . register_paths (
" GET " , [ re . compile ( " ^/_matrix/foo$ " ) ] , _callback , " test_servlet "
)
2018-06-27 09:37:24 +00:00
2021-09-24 10:01:25 +00:00
channel = make_request (
self . reactor , FakeSite ( res , self . reactor ) , b " GET " , b " /_matrix/foo "
)
2018-06-27 09:37:24 +00:00
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 403 )
2018-08-09 02:22:01 +00:00
self . assertEqual ( channel . json_body [ " error " ] , " Forbidden!!one! " )
self . assertEqual ( channel . json_body [ " errcode " ] , " M_FORBIDDEN " )
2018-06-27 09:37:24 +00:00
2022-06-28 12:12:17 +00:00
def test_no_handler ( self ) - > None :
2018-06-27 09:37:24 +00:00
"""
If there is no handler to process the request , Synapse will return 400.
"""
2022-06-28 12:12:17 +00:00
def _callback ( request : SynapseRequest , * * kwargs : object ) - > None :
2018-06-27 09:37:24 +00:00
"""
Not ever actually called !
"""
self . fail ( " shouldn ' t ever get here " )
res = JsonResource ( self . homeserver )
2019-07-24 12:07:35 +00:00
res . register_paths (
" GET " , [ re . compile ( " ^/_matrix/foo$ " ) ] , _callback , " test_servlet "
)
2018-06-27 09:37:24 +00:00
2021-09-24 10:01:25 +00:00
channel = make_request (
self . reactor , FakeSite ( res , self . reactor ) , b " GET " , b " /_matrix/foobar "
)
2018-06-27 09:37:24 +00:00
2022-12-08 16:37:05 +00:00
self . assertEqual ( channel . code , 404 )
2018-08-09 02:22:01 +00:00
self . assertEqual ( channel . json_body [ " error " ] , " Unrecognized request " )
self . assertEqual ( channel . json_body [ " errcode " ] , " M_UNRECOGNIZED " )
2018-09-20 06:28:18 +00:00
2022-06-28 12:12:17 +00:00
def test_head_request ( self ) - > None :
2020-08-03 12:45:42 +00:00
"""
JsonResource . handler_for_request gives correctly decoded URL args to
the callback , while Twisted will give the raw bytes of URL query
arguments .
"""
2022-06-28 12:12:17 +00:00
def _callback (
request : SynapseRequest , * * kwargs : object
) - > Tuple [ int , Dict [ str , object ] ] :
2020-08-03 12:45:42 +00:00
return 200 , { " result " : True }
res = JsonResource ( self . homeserver )
res . register_paths (
" GET " ,
[ re . compile ( " ^/_matrix/foo$ " ) ] ,
_callback ,
" test_servlet " ,
)
# The path was registered as GET, but this is a HEAD request.
2021-09-24 10:01:25 +00:00
channel = make_request (
self . reactor , FakeSite ( res , self . reactor ) , b " HEAD " , b " /_matrix/foo "
)
2020-08-03 12:45:42 +00:00
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 200 )
2020-08-03 12:45:42 +00:00
self . assertNotIn ( " body " , channel . result )
2018-09-20 06:28:18 +00:00
2020-05-22 13:30:07 +00:00
class OptionsResourceTests ( unittest . TestCase ) :
2022-06-28 12:12:17 +00:00
def setUp ( self ) - > None :
2020-05-22 13:30:07 +00:00
self . reactor = ThreadedMemoryReactorClock ( )
class DummyResource ( Resource ) :
isLeaf = True
2022-06-28 12:12:17 +00:00
def render ( self , request : SynapseRequest ) - > bytes :
# Type-ignore: mypy thinks request.path is Optional[Any], not bytes.
return request . path # type: ignore[return-value]
2020-05-22 13:30:07 +00:00
# Setup a resource with some children.
self . resource = OptionsResource ( )
self . resource . putChild ( b " res " , DummyResource ( ) )
2022-10-18 15:52:25 +00:00
def _make_request (
self , method : bytes , path : bytes , experimental_cors_msc3886 : bool = False
) - > FakeChannel :
2020-05-22 13:30:07 +00:00
""" Create a request from the method/path and return a channel with the response. """
# Create a site and query for the resource.
2020-06-16 11:44:07 +00:00
site = SynapseSite (
" test " ,
" site_tag " ,
2022-10-18 15:52:25 +00:00
parse_listener_def (
0 ,
{
" type " : " http " ,
" port " : 0 ,
" experimental_cors_msc3886 " : experimental_cors_msc3886 ,
} ,
) ,
2020-06-16 11:44:07 +00:00
self . resource ,
" 1.0 " ,
2022-07-19 11:45:17 +00:00
max_request_body_size = 4096 ,
2021-04-23 16:06:47 +00:00
reactor = self . reactor ,
2020-06-16 11:44:07 +00:00
)
2020-11-13 22:39:09 +00:00
2020-11-15 22:49:21 +00:00
# render the request and return the channel
2020-12-15 14:44:04 +00:00
channel = make_request ( self . reactor , site , method , path , shorthand = False )
2020-05-22 13:30:07 +00:00
return channel
2022-10-18 15:52:25 +00:00
def _check_cors_standard_headers ( self , channel : FakeChannel ) - > None :
# Ensure the correct CORS headers have been added
# as per https://spec.matrix.org/v1.4/client-server-api/#web-browser-clients
self . assertEqual (
channel . headers . getRawHeaders ( b " Access-Control-Allow-Origin " ) ,
[ b " * " ] ,
" has correct CORS Origin header " ,
)
self . assertEqual (
channel . headers . getRawHeaders ( b " Access-Control-Allow-Methods " ) ,
[ b " GET, HEAD, POST, PUT, DELETE, OPTIONS " ] , # HEAD isn't in the spec
" has correct CORS Methods header " ,
)
self . assertEqual (
channel . headers . getRawHeaders ( b " Access-Control-Allow-Headers " ) ,
[ b " X-Requested-With, Content-Type, Authorization, Date " ] ,
" has correct CORS Headers header " ,
)
2023-03-20 18:14:05 +00:00
self . assertEqual (
channel . headers . getRawHeaders ( b " Access-Control-Expose-Headers " ) ,
[ b " Synapse-Trace-Id " ] ,
)
2022-10-18 15:52:25 +00:00
def _check_cors_msc3886_headers ( self , channel : FakeChannel ) - > None :
# Ensure the correct CORS headers have been added
# as per https://github.com/matrix-org/matrix-spec-proposals/blob/hughns/simple-rendezvous-capability/proposals/3886-simple-rendezvous-capability.md#cors
self . assertEqual (
channel . headers . getRawHeaders ( b " Access-Control-Allow-Origin " ) ,
[ b " * " ] ,
" has correct CORS Origin header " ,
)
self . assertEqual (
channel . headers . getRawHeaders ( b " Access-Control-Allow-Methods " ) ,
[ b " GET, HEAD, POST, PUT, DELETE, OPTIONS " ] , # HEAD isn't in the spec
" has correct CORS Methods header " ,
)
self . assertEqual (
channel . headers . getRawHeaders ( b " Access-Control-Allow-Headers " ) ,
[
b " X-Requested-With, Content-Type, Authorization, Date, If-Match, If-None-Match "
] ,
" has correct CORS Headers header " ,
)
self . assertEqual (
channel . headers . getRawHeaders ( b " Access-Control-Expose-Headers " ) ,
[ b " ETag, Location, X-Max-Bytes " ] ,
" has correct CORS Expose Headers header " ,
)
2022-06-28 12:12:17 +00:00
def test_unknown_options_request ( self ) - > None :
2020-07-24 11:08:07 +00:00
""" An OPTIONS requests to an unknown URL still returns 204 No Content. """
2020-05-22 13:30:07 +00:00
channel = self . _make_request ( b " OPTIONS " , b " /foo/ " )
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 204 )
2020-07-24 11:08:07 +00:00
self . assertNotIn ( " body " , channel . result )
2020-05-22 13:30:07 +00:00
2022-10-18 15:52:25 +00:00
self . _check_cors_standard_headers ( channel )
2020-05-22 16:42:39 +00:00
2022-06-28 12:12:17 +00:00
def test_known_options_request ( self ) - > None :
2020-07-24 11:08:07 +00:00
""" An OPTIONS requests to an known URL still returns 204 No Content. """
2020-05-22 13:30:07 +00:00
channel = self . _make_request ( b " OPTIONS " , b " /res/ " )
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 204 )
2020-07-24 11:08:07 +00:00
self . assertNotIn ( " body " , channel . result )
2020-05-22 13:30:07 +00:00
2022-10-18 15:52:25 +00:00
self . _check_cors_standard_headers ( channel )
def test_known_options_request_msc3886 ( self ) - > None :
""" An OPTIONS requests to an known URL still returns 204 No Content. """
channel = self . _make_request (
b " OPTIONS " , b " /res/ " , experimental_cors_msc3886 = True
2020-05-22 16:42:39 +00:00
)
2022-10-18 15:52:25 +00:00
self . assertEqual ( channel . code , 204 )
self . assertNotIn ( " body " , channel . result )
self . _check_cors_msc3886_headers ( channel )
2020-05-22 16:42:39 +00:00
2022-06-28 12:12:17 +00:00
def test_unknown_request ( self ) - > None :
2020-05-22 13:30:07 +00:00
""" A non-OPTIONS request to an unknown URL should 404. """
channel = self . _make_request ( b " GET " , b " /foo/ " )
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 404 )
2020-05-22 13:30:07 +00:00
2022-06-28 12:12:17 +00:00
def test_known_request ( self ) - > None :
2020-05-22 13:30:07 +00:00
""" A non-OPTIONS request to an known URL should query the proper resource. """
channel = self . _make_request ( b " GET " , b " /res/ " )
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 200 )
2020-05-22 13:30:07 +00:00
self . assertEqual ( channel . result [ " body " ] , b " /res/ " )
2020-01-15 15:58:55 +00:00
class WrapHtmlRequestHandlerTests ( unittest . TestCase ) :
2020-07-03 18:02:19 +00:00
class TestResource ( DirectServeHtmlResource ) :
2022-06-28 12:12:17 +00:00
callback : Optional [ Callable [ . . . , Awaitable [ None ] ] ]
2020-01-15 15:58:55 +00:00
2022-06-28 12:12:17 +00:00
async def _async_render_GET ( self , request : SynapseRequest ) - > None :
assert self . callback is not None
2020-07-03 18:02:19 +00:00
await self . callback ( request )
2020-01-15 15:58:55 +00:00
2022-06-28 12:12:17 +00:00
def setUp ( self ) - > None :
2020-01-15 15:58:55 +00:00
self . reactor = ThreadedMemoryReactorClock ( )
2022-06-28 12:12:17 +00:00
def test_good_response ( self ) - > None :
async def callback ( request : SynapseRequest ) - > None :
2020-01-15 15:58:55 +00:00
request . write ( b " response " )
request . finish ( )
res = WrapHtmlRequestHandlerTests . TestResource ( )
res . callback = callback
2021-09-24 10:01:25 +00:00
channel = make_request (
self . reactor , FakeSite ( res , self . reactor ) , b " GET " , b " /path "
)
2020-01-15 15:58:55 +00:00
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 200 )
2020-01-15 15:58:55 +00:00
body = channel . result [ " body " ]
self . assertEqual ( body , b " response " )
2022-06-28 12:12:17 +00:00
def test_redirect_exception ( self ) - > None :
2020-01-15 15:58:55 +00:00
"""
If the callback raises a RedirectException , it is turned into a 30 x
with the right location .
"""
2022-06-28 12:12:17 +00:00
async def callback ( request : SynapseRequest , * * kwargs : object ) - > None :
2020-01-15 15:58:55 +00:00
raise RedirectException ( b " /look/an/eagle " , 301 )
res = WrapHtmlRequestHandlerTests . TestResource ( )
res . callback = callback
2021-09-24 10:01:25 +00:00
channel = make_request (
self . reactor , FakeSite ( res , self . reactor ) , b " GET " , b " /path "
)
2020-01-15 15:58:55 +00:00
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 301 )
2020-01-15 15:58:55 +00:00
headers = channel . result [ " headers " ]
location_headers = [ v for k , v in headers if k == b " Location " ]
self . assertEqual ( location_headers , [ b " /look/an/eagle " ] )
2022-06-28 12:12:17 +00:00
def test_redirect_exception_with_cookie ( self ) - > None :
2020-01-15 15:58:55 +00:00
"""
If the callback raises a RedirectException which sets a cookie , that is
returned too
"""
2022-06-28 12:12:17 +00:00
async def callback ( request : SynapseRequest , * * kwargs : object ) - > NoReturn :
2020-01-15 15:58:55 +00:00
e = RedirectException ( b " /no/over/there " , 304 )
e . cookies . append ( b " session=yespls " )
raise e
res = WrapHtmlRequestHandlerTests . TestResource ( )
res . callback = callback
2021-09-24 10:01:25 +00:00
channel = make_request (
self . reactor , FakeSite ( res , self . reactor ) , b " GET " , b " /path "
)
2020-01-15 15:58:55 +00:00
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 304 )
2020-01-15 15:58:55 +00:00
headers = channel . result [ " headers " ]
location_headers = [ v for k , v in headers if k == b " Location " ]
self . assertEqual ( location_headers , [ b " /no/over/there " ] )
cookies_headers = [ v for k , v in headers if k == b " Set-Cookie " ]
self . assertEqual ( cookies_headers , [ b " session=yespls " ] )
2020-08-03 12:45:42 +00:00
2022-06-28 12:12:17 +00:00
def test_head_request ( self ) - > None :
2020-08-03 12:45:42 +00:00
""" A head request should work by being turned into a GET request. """
2022-06-28 12:12:17 +00:00
async def callback ( request : SynapseRequest ) - > None :
2020-08-03 12:45:42 +00:00
request . write ( b " response " )
request . finish ( )
res = WrapHtmlRequestHandlerTests . TestResource ( )
res . callback = callback
2021-09-24 10:01:25 +00:00
channel = make_request (
self . reactor , FakeSite ( res , self . reactor ) , b " HEAD " , b " /path "
)
2020-08-03 12:45:42 +00:00
2022-08-05 14:59:09 +00:00
self . assertEqual ( channel . code , 200 )
2020-08-03 12:45:42 +00:00
self . assertNotIn ( " body " , channel . result )
2022-05-11 11:24:48 +00:00
class CancellableDirectServeJsonResource ( DirectServeJsonResource ) :
def __init__ ( self , clock : Clock ) :
super ( ) . __init__ ( )
self . clock = clock
@cancellable
async def _async_render_GET ( self , request : SynapseRequest ) - > Tuple [ int , JsonDict ] :
await self . clock . sleep ( 1.0 )
return HTTPStatus . OK , { " result " : True }
async def _async_render_POST ( self , request : SynapseRequest ) - > Tuple [ int , JsonDict ] :
await self . clock . sleep ( 1.0 )
return HTTPStatus . OK , { " result " : True }
class CancellableDirectServeHtmlResource ( DirectServeHtmlResource ) :
ERROR_TEMPLATE = " {code} {msg} "
def __init__ ( self , clock : Clock ) :
super ( ) . __init__ ( )
self . clock = clock
@cancellable
async def _async_render_GET ( self , request : SynapseRequest ) - > Tuple [ int , bytes ] :
await self . clock . sleep ( 1.0 )
return HTTPStatus . OK , b " ok "
async def _async_render_POST ( self , request : SynapseRequest ) - > Tuple [ int , bytes ] :
await self . clock . sleep ( 1.0 )
return HTTPStatus . OK , b " ok "
2022-06-07 17:17:32 +00:00
class DirectServeJsonResourceCancellationTests ( unittest . TestCase ) :
2022-05-11 11:24:48 +00:00
""" Tests for `DirectServeJsonResource` cancellation. """
2022-06-28 12:12:17 +00:00
def setUp ( self ) - > None :
2022-05-11 11:24:48 +00:00
self . reactor = ThreadedMemoryReactorClock ( )
self . clock = Clock ( self . reactor )
self . resource = CancellableDirectServeJsonResource ( self . clock )
self . site = FakeSite ( self . resource , self . reactor )
def test_cancellable_disconnect ( self ) - > None :
""" Test that handlers with the `@cancellable` flag can be cancelled. """
channel = make_request (
self . reactor , self . site , " GET " , " /sleep " , await_result = False
)
2022-06-07 17:17:32 +00:00
test_disconnect (
2022-05-11 11:24:48 +00:00
self . reactor ,
channel ,
expect_cancellation = True ,
expected_body = { " error " : " Request cancelled " , " errcode " : Codes . UNKNOWN } ,
)
def test_uncancellable_disconnect ( self ) - > None :
""" Test that handlers without the `@cancellable` flag cannot be cancelled. """
channel = make_request (
self . reactor , self . site , " POST " , " /sleep " , await_result = False
)
2022-06-07 17:17:32 +00:00
test_disconnect (
2022-05-11 11:24:48 +00:00
self . reactor ,
channel ,
expect_cancellation = False ,
expected_body = { " result " : True } ,
)
2022-06-07 17:17:32 +00:00
class DirectServeHtmlResourceCancellationTests ( unittest . TestCase ) :
2022-05-11 11:24:48 +00:00
""" Tests for `DirectServeHtmlResource` cancellation. """
2022-06-28 12:12:17 +00:00
def setUp ( self ) - > None :
2022-05-11 11:24:48 +00:00
self . reactor = ThreadedMemoryReactorClock ( )
self . clock = Clock ( self . reactor )
self . resource = CancellableDirectServeHtmlResource ( self . clock )
self . site = FakeSite ( self . resource , self . reactor )
def test_cancellable_disconnect ( self ) - > None :
""" Test that handlers with the `@cancellable` flag can be cancelled. """
channel = make_request (
self . reactor , self . site , " GET " , " /sleep " , await_result = False
)
2022-06-07 17:17:32 +00:00
test_disconnect (
2022-05-11 11:24:48 +00:00
self . reactor ,
channel ,
expect_cancellation = True ,
expected_body = b " 499 Request cancelled " ,
)
def test_uncancellable_disconnect ( self ) - > None :
""" Test that handlers without the `@cancellable` flag cannot be cancelled. """
channel = make_request (
self . reactor , self . site , " POST " , " /sleep " , await_result = False
)
2022-06-07 17:17:32 +00:00
test_disconnect (
2022-05-11 11:24:48 +00:00
self . reactor , channel , expect_cancellation = False , expected_body = b " ok "
)