1 | """ |
---|
2 | Ported to Python 3. |
---|
3 | """ |
---|
4 | |
---|
5 | from six import ensure_str |
---|
6 | |
---|
7 | __all__ = [ |
---|
8 | "do_http", |
---|
9 | "render", |
---|
10 | ] |
---|
11 | |
---|
12 | from twisted.internet.defer import ( |
---|
13 | inlineCallbacks, |
---|
14 | returnValue, |
---|
15 | ) |
---|
16 | from twisted.web.error import ( |
---|
17 | Error, |
---|
18 | ) |
---|
19 | from twisted.python.reflect import ( |
---|
20 | fullyQualifiedName, |
---|
21 | ) |
---|
22 | from twisted.internet.defer import ( |
---|
23 | succeed, |
---|
24 | ) |
---|
25 | from twisted.web.test.requesthelper import ( |
---|
26 | DummyChannel, |
---|
27 | ) |
---|
28 | from twisted.web.error import ( |
---|
29 | UnsupportedMethod, |
---|
30 | ) |
---|
31 | from twisted.web.http import ( |
---|
32 | NOT_ALLOWED, |
---|
33 | ) |
---|
34 | from twisted.web.server import ( |
---|
35 | NOT_DONE_YET, |
---|
36 | ) |
---|
37 | |
---|
38 | import treq |
---|
39 | |
---|
40 | from ..webish import ( |
---|
41 | TahoeLAFSRequest, |
---|
42 | ) |
---|
43 | |
---|
44 | |
---|
45 | class VerboseError(Error): |
---|
46 | """Include the HTTP body response too.""" |
---|
47 | |
---|
48 | def __str__(self): |
---|
49 | return Error.__str__(self) + " " + ensure_str(self.response) |
---|
50 | |
---|
51 | |
---|
52 | @inlineCallbacks |
---|
53 | def do_http(method, url, **kwargs): |
---|
54 | """ |
---|
55 | Run HTTP query, return Deferred of body as bytes. |
---|
56 | """ |
---|
57 | response = yield treq.request(method, url, persistent=False, **kwargs) |
---|
58 | body = yield treq.content(response) |
---|
59 | # TODO: replace this with response.fail_for_status when |
---|
60 | # https://github.com/twisted/treq/pull/159 has landed |
---|
61 | if 400 <= response.code < 600: |
---|
62 | raise VerboseError( |
---|
63 | response.code, response="For request {!r} to {!r}, got: {!r}".format( |
---|
64 | method, url, body)) |
---|
65 | returnValue(body) |
---|
66 | |
---|
67 | |
---|
68 | def render(resource, query_args): |
---|
69 | """ |
---|
70 | Render (in the manner of the Twisted Web Site) a Twisted ``Resource`` |
---|
71 | against a request with the given query arguments . |
---|
72 | |
---|
73 | :param resource: The page or resource to render. |
---|
74 | |
---|
75 | :param query_args: The query arguments to put into the request being |
---|
76 | rendered. A mapping from ``bytes`` to ``list`` of ``bytes``. |
---|
77 | |
---|
78 | :return Deferred: A Deferred that fires with the rendered response body as |
---|
79 | ``bytes``. |
---|
80 | """ |
---|
81 | channel = DummyChannel() |
---|
82 | request = TahoeLAFSRequest(channel) |
---|
83 | request.method = b"GET" |
---|
84 | request.args = query_args |
---|
85 | request.prepath = [b""] |
---|
86 | request.postpath = [] |
---|
87 | try: |
---|
88 | result = resource.render(request) |
---|
89 | except UnsupportedMethod: |
---|
90 | request.setResponseCode(NOT_ALLOWED) |
---|
91 | result = b"" |
---|
92 | |
---|
93 | if isinstance(result, bytes): |
---|
94 | request.write(result) |
---|
95 | done = succeed(None) |
---|
96 | elif result == NOT_DONE_YET: |
---|
97 | if request.finished: |
---|
98 | done = succeed(None) |
---|
99 | else: |
---|
100 | done = request.notifyFinish() |
---|
101 | else: |
---|
102 | raise ValueError( |
---|
103 | "{!r} returned {!r}, required bytes or NOT_DONE_YET.".format( |
---|
104 | fullyQualifiedName(resource.render), |
---|
105 | result, |
---|
106 | ), |
---|
107 | ) |
---|
108 | def get_body(ignored): |
---|
109 | complete_response = channel.transport.written.getvalue() |
---|
110 | header, body = complete_response.split(b"\r\n\r\n", 1) |
---|
111 | return body |
---|
112 | done.addCallback(get_body) |
---|
113 | return done |
---|