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