| 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 |
|---|