Example #1
0
    def handle(request):
        # kwargs for this function's response_cls constructor
        response_kwargs = {}
        status = OK

        try:
            response = yield gen.maybe_future(handler(request))
        except Exception as e:
            response = Response()

            for exc_spec in response_spec.exception_specs:
                # Each exc_spec is a thriftrw.spec.FieldSpec. The spec
                # attribute on that is the TypeSpec for the Exception class
                # and the surface on the TypeSpec is the exception class.
                exc_cls = exc_spec.spec.surface
                if isinstance(e, exc_cls):
                    status = FAILED
                    response_kwargs[exc_spec.name] = e
                    break
            else:
                raise_exc_info(sys.exc_info())
        else:
            response = response_from_mixed(response)

            if response_spec.return_spec is not None:
                assert response.body is not None, (
                    'Expected a value to be returned for %s, '
                    'but recieved None - only void procedures can '
                    'return None.' % function.endpoint)
                response_kwargs['success'] = response.body

        response.status = status
        response.body = response_cls(**response_kwargs)
        raise gen.Return(response)
Example #2
0
    def handle(request):
        # kwargs for this function's response_cls constructor
        response_kwargs = {}
        status = OK

        try:
            response = yield gen.maybe_future(handler(request))
        except Exception as e:
            response = Response()

            for exc_spec in response_spec.exception_specs:
                # Each exc_spec is a thriftrw.spec.FieldSpec. The spec
                # attribute on that is the TypeSpec for the Exception class
                # and the surface on the TypeSpec is the exception class.
                exc_cls = exc_spec.spec.surface
                if isinstance(e, exc_cls):
                    status = FAILED
                    response_kwargs[exc_spec.name] = e
                    break
            else:
                raise_exc_info(sys.exc_info())
        else:
            response = response_from_mixed(response)

            if response_spec.return_spec is not None:
                assert response.body is not None, (
                    'Expected a value to be returned for %s, '
                    'but recieved None - only void procedures can '
                    'return None.' % function.endpoint
                )
                response_kwargs['success'] = response.body

        response.status = status
        response.body = response_cls(**response_kwargs)
        raise gen.Return(response)
Example #3
0
    def handler(request):

        result = ThriftResponse(result_type())
        response = Response()

        try:
            response = yield gen.maybe_future(f(request))
        except Exception:
            result.write_exc_info(sys.exc_info())
        else:
            response = response_from_mixed(response)
            result.write_result(response.body)

        response.status = result.code
        response.body = result.result

        raise gen.Return(response)
    def handler(request):

        result = ThriftResponse(result_type())
        response = Response()

        try:
            response = yield gen.maybe_future(f(request))
        except Exception:
            result.write_exc_info(sys.exc_info())
        else:
            response = response_from_mixed(response)
            result.write_result(response.body)

        response.status = result.code
        response.body = result.result

        raise gen.Return(response)
Example #5
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)
Example #6
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()
        while chunk:
            request.endpoint += chunk
            chunk = yield request.argstreams[0].read()

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

        tchannel = connection.tchannel

        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)

        tracer = tracing.ServerTracer(tracer=tchannel.tracer, operation_name=request.endpoint)
        tracer.start_basic_span(request)

        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,
                )
                with tracer.start_span(
                    request=request, headers=he, peer_host=connection.remote_host, peer_port=connection.remote_host_port
                ) as span:
                    context_provider = tchannel.context_provider_fn()
                    with context_provider.span_in_context(span):
                        # Cannot yield while inside the StackContext
                        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 tracer.start_span(
                    request=request, headers={}, peer_host=connection.remote_host, peer_port=connection.remote_host_port
                ) as span:
                    context_provider = tchannel.context_provider_fn()
                    with context_provider.span_in_context(span):
                        # Cannot yield while inside the StackContext
                        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)
Example #7
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]

        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,
                    endpoint=request.endpoint,
                )

                # 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 TChannelError as e:
            connection.send_error(
                e.code,
                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)
Example #8
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)
Example #9
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)