示例#1
0
    def handle_call(self, request, connection):
        # read arg_1 so that handle_call is able to get the endpoint
        # name and find the endpoint handler.
        # the arg_1 value will be store in the request.endpoint field.

        # NOTE: after here, the correct way to access value of arg_1 is through
        # request.endpoint. The original argstream[0] is no longer valid. If
        # user still tries read from it, it will return empty.
        chunk = yield request.argstreams[0].read()
        response = None
        while chunk:
            request.endpoint += chunk
            chunk = yield request.argstreams[0].read()

        log.debug('Received a call to %s.', request.endpoint)

        tchannel = connection.tchannel

        # event: receive_request
        request.tracing.name = request.endpoint
        tchannel.event_emitter.fire(EventType.before_receive_request, request)

        handler = self.get_endpoint(request.endpoint)

        requested_as = request.headers.get('as', None)
        expected_as = handler.req_serializer.name

        if request.endpoint in self.handlers and requested_as != expected_as:
            connection.send_error(BadRequestError(
                description=(
                    "Server expected a '%s' but request is '%s'"
                    % (
                        expected_as,
                        requested_as,
                    )
                ),
                id=request.id,
                tracing=request.tracing,
            ))

            raise gen.Return(None)

        request.serializer = handler.req_serializer
        response = DeprecatedResponse(
            id=request.id,
            checksum=request.checksum,
            tracing=request.tracing,
            connection=connection,
            headers={'as': request.headers.get('as', 'raw')},
            serializer=handler.resp_serializer,
        )

        def _on_post_response(future):
            if not future.exception():
                return

            # Failed to write response because client disappeared. Nothing to
            # do.
            if isinstance(future.exception(), StreamClosedError):
                return

            log.error('failed to write response', exc_info=future.exc_info())

        connection.post_response(response).add_done_callback(_on_post_response)

        try:
            # New impl - the handler takes a request and returns a response
            if self._handler_returns_response:
                # convert deprecated req to new top-level req
                b = yield request.get_body()
                he = yield request.get_header()
                t = TransportHeaders.from_dict(request.headers)
                new_req = Request(
                    body=b,
                    headers=he,
                    transport=t,
                    endpoint=request.endpoint,
                    service=request.service,
                    timeout=request.ttl,
                )

                # Not safe to have coroutine yields statement within
                # stack context.
                # The right way to do it is:
                # with request_context(..):
                #    future = f()
                # yield future

                with request_context(request.tracing):
                    f = handler.endpoint(new_req)

                new_resp = yield gen.maybe_future(f)

                # instantiate a tchannel.Response
                new_resp = response_from_mixed(new_resp)

                response.code = new_resp.status

                # assign resp values to dep response
                response.write_header(new_resp.headers)

                if new_resp.body is not None:
                    response.write_body(new_resp.body)

            # Dep impl - the handler is provided with a req & resp writer
            else:
                with request_context(request.tracing):
                    f = handler.endpoint(request, response)

                yield gen.maybe_future(f)

            response.flush()
        except TChannelError as e:
            e.tracing = request.tracing
            e.id = request.id
            connection.send_error(e)
        except Exception as e:
            # Maintain a reference to our original exc info because we stomp
            # the traceback below.
            exc_info = sys.exc_info()
            exc_type, exc_obj, exc_tb = exc_info
            try:
                # Walk to the traceback to find our offending line.
                while exc_tb.tb_next is not None:
                    exc_tb = exc_tb.tb_next

                description = "%r from %s in %s:%s" % (
                    e,
                    request.endpoint,
                    exc_tb.tb_frame.f_code.co_filename,
                    exc_tb.tb_lineno,
                )
                error = UnexpectedError(
                    description=description,
                    id=request.id,
                    tracing=request.tracing,
                )

                response.set_exception(error, exc_info=exc_info)
                connection.request_message_factory.remove_buffer(response.id)

                connection.send_error(error)
                tchannel.event_emitter.fire(
                    EventType.on_exception,
                    request,
                    error,
                )
                log.error("Unexpected error", exc_info=exc_info)
            finally:
                # Clean up circular reference.
                # https://docs.python.org/2/library/sys.html#sys.exc_info
                del exc_tb
                del exc_info
        raise gen.Return(response)
示例#2
0
    def handle_call(self, request, connection):
        # read arg_1 so that handle_call is able to get the endpoint
        # name and find the endpoint handler.
        # the arg_1 value will be store in the request.endpoint field.

        # NOTE: after here, the correct way to access value of arg_1 is through
        # request.endpoint. The original argstream[0] is no longer valid. If
        # user still tries read from it, it will return empty.
        chunk = yield request.argstreams[0].read()
        response = None
        while chunk:
            request.endpoint += chunk
            chunk = yield request.argstreams[0].read()

        log.debug('Received a call to %s.', request.endpoint)

        tchannel = connection.tchannel

        # event: receive_request
        request.tracing.name = request.endpoint
        tchannel.event_emitter.fire(EventType.before_receive_request, request)

        handler = self.handlers.get(request.endpoint)

        if handler is None:
            handler = self.handlers[self.FALLBACK]

        requested_as = request.headers.get('as', None)
        expected_as = handler.req_serializer.name

        if request.endpoint in self.handlers and requested_as != expected_as:
            connection.send_error(
                BadRequestError(
                    description=("Server expected a '%s' but request is '%s'" %
                                 (
                                     expected_as,
                                     requested_as,
                                 )),
                    id=request.id,
                    tracing=request.tracing,
                ))

            raise gen.Return(None)

        request.serializer = handler.req_serializer
        response = DeprecatedResponse(
            id=request.id,
            checksum=request.checksum,
            tracing=request.tracing,
            connection=connection,
            headers={'as': request.headers.get('as', 'raw')},
            serializer=handler.resp_serializer,
        )

        connection.post_response(response)

        try:
            # New impl - the handler takes a request and returns a response
            if self._handler_returns_response:
                # convert deprecated req to new top-level req
                b = yield request.get_body()
                he = yield request.get_header()
                t = TransportHeaders.from_dict(request.headers)
                new_req = Request(
                    body=b,
                    headers=he,
                    transport=t,
                    endpoint=request.endpoint,
                    service=request.service,
                    timeout=request.ttl,
                )

                # Not safe to have coroutine yields statement within
                # stack context.
                # The right way to do it is:
                # with request_context(..):
                #    future = f()
                # yield future

                with request_context(request.tracing):
                    f = handler.endpoint(new_req)

                new_resp = yield gen.maybe_future(f)

                # instantiate a tchannel.Response
                new_resp = response_from_mixed(new_resp)

                response.code = new_resp.status

                # assign resp values to dep response
                response.write_header(new_resp.headers)

                if new_resp.body is not None:
                    response.write_body(new_resp.body)

            # Dep impl - the handler is provided with a req & resp writer
            else:
                with request_context(request.tracing):
                    f = handler.endpoint(request, response)

                yield gen.maybe_future(f)

            response.flush()
        except TChannelError as e:
            e.tracing = request.tracing
            e.id = request.id
            connection.send_error(e)
        except Exception as e:
            error = UnexpectedError(
                description="Unexpected Error: '%s'" % e.message,
                id=request.id,
                tracing=request.tracing,
            )
            response.set_exception(error)
            connection.request_message_factory.remove_buffer(response.id)

            connection.send_error(error)
            tchannel.event_emitter.fire(EventType.on_exception, request, error)
            log.exception(error.description)

        raise gen.Return(response)
示例#3
0
    def handle_call(self, request, connection):
        # read arg_1 so that handle_call is able to get the endpoint
        # name and find the endpoint handler.
        # the arg_1 value will be store in the request.endpoint field.

        # NOTE: after here, the correct way to access value of arg_1 is through
        # request.endpoint. The original argstream[0] is no longer valid. If
        # user still tries read from it, it will return empty.
        chunk = yield request.argstreams[0].read()
        response = None
        while chunk:
            request.endpoint += chunk
            chunk = yield request.argstreams[0].read()

        log.debug('Received a call to %s.', request.endpoint)

        tchannel = connection.tchannel

        # event: receive_request
        request.tracing.name = request.endpoint
        tchannel.event_emitter.fire(EventType.before_receive_request, request)

        handler = self.handlers[request.endpoint]
        if request.headers.get('as', None) != handler.req_serializer.name:
            connection.send_error(
                ErrorCode.bad_request,
                "Invalid arg scheme in request header",
                request.id,
            )
            raise gen.Return(None)

        request.serializer = handler.req_serializer
        response = DeprecatedResponse(
            id=request.id,
            checksum=request.checksum,
            tracing=request.tracing,
            connection=connection,
            headers={'as': request.headers.get('as', 'raw')},
            serializer=handler.resp_serializer,
        )

        connection.post_response(response)

        try:
            # New impl - the handler takes a request and returns a response
            if self._handler_returns_response:

                # convert deprecated req to new top-level req
                b = yield request.get_body()
                he = yield request.get_header()
                t = request.headers
                t = transport.to_kwargs(t)
                t = TransportHeaders(**t)
                new_req = Request(
                    body=b,
                    headers=he,
                    transport=t,
                )

                # Not safe to have coroutine yields statement within
                # stack context.
                # The right way to do it is:
                # with request_context(..):
                #    future = f()
                # yield future

                with request_context(request.tracing):
                    f = handler.endpoint(new_req)

                new_resp = yield gen.maybe_future(f)

                # instantiate a tchannel.Response
                new_resp = response_from_mixed(new_resp)

                # assign resp values to dep response
                response.write_header(new_resp.headers)

                if new_resp.body is not None:
                    response.write_body(new_resp.body)

            # Dep impl - the handler is provided with a req & resp writer
            else:
                with request_context(request.tracing):
                    f = handler.endpoint(request, response)

                yield gen.maybe_future(f)

            response.flush()
        except InvalidEndpointError as e:
            connection.send_error(
                ErrorCode.bad_request,
                e.message,
                request.id,
            )
        except Exception as e:
            msg = "An unexpected error has occurred from the handler"
            log.exception(msg)

            response.set_exception(TChannelError(e.message))
            connection.request_message_factory.remove_buffer(response.id)

            connection.send_error(ErrorCode.unexpected, msg, response.id)
            tchannel.event_emitter.fire(EventType.on_exception, request, e)

        raise gen.Return(response)