def _next_ticket(self): """Creates the next ticket to be transmitted. Returns: A links.Ticket to be sent to the other side of the operation or None if there is nothing to be sent at this time. """ if self._abort.kind is _Abort.Kind.ABORTED_NO_NOTIFY: return None elif self._abort.kind is _Abort.Kind.ABORTED_NOTIFY_NEEDED: termination = self._abort.termination code, details = self._abort.code, self._abort.details self._abort = _ABORTED_NO_NOTIFY return links.Ticket(self._operation_id, self._lowest_unused_sequence_number, None, None, None, None, None, None, None, None, code, details, termination, None) action = False # TODO(nathaniel): Support other subscriptions. local_subscription = links.Ticket.Subscription.FULL timeout = self._timeout if timeout is not None: self._timeout = None action = True if self._local_allowance <= 0: allowance = None else: allowance = self._local_allowance self._local_allowance = 0 action = True initial_metadata = self._initial_metadata if initial_metadata is not None: self._initial_metadata = None action = True if not self._payloads or self._remote_allowance <= 0: payload = None else: payload = self._payloads.pop(0) self._remote_allowance -= 1 action = True if self._completion is None or self._payloads: terminal_metadata, code, message, termination = None, None, None, None else: terminal_metadata, code, message, termination = _explode_completion( self._completion) self._completion = None action = True if action: ticket = links.Ticket(self._operation_id, self._lowest_unused_sequence_number, None, None, local_subscription, timeout, allowance, initial_metadata, payload, terminal_metadata, code, message, termination, None) self._lowest_unused_sequence_number += 1 return ticket else: return None
def testSimplestRoundTrip(self): """Tests transmission of one ticket in each direction.""" invocation_operation_id = object() invocation_payload = b'\x07' * 1023 timeout = test_constants.LONG_TIMEOUT invocation_initial_metadata = self.create_invocation_initial_metadata() invocation_terminal_metadata = self.create_invocation_terminal_metadata( ) invocation_code, invocation_message = self.create_invocation_completion( ) service_payload = b'\x08' * 1025 service_initial_metadata = self.create_service_initial_metadata() service_terminal_metadata = self.create_service_terminal_metadata() service_code, service_message = self.create_service_completion() original_invocation_ticket = links.Ticket( invocation_operation_id, 0, _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, links.Ticket.Subscription.FULL, timeout, 0, invocation_initial_metadata, invocation_payload, invocation_terminal_metadata, invocation_code, invocation_message, links.Ticket.Termination.COMPLETION) self._invocation_link.accept_ticket(original_invocation_ticket) # TODO(nathaniel): This shouldn't be necessary. Detecting the end of the # invocation-side ticket sequence shouldn't require granting allowance for # another payload. self._service_mate.block_until_tickets_satisfy( at_least_n_payloads_received_predicate(1)) service_operation_id = self._service_mate.tickets()[0].operation_id self._service_link.accept_ticket( links.Ticket(service_operation_id, 0, None, None, links.Ticket.Subscription.FULL, None, 1, None, None, None, None, None, None)) self._service_mate.block_until_tickets_satisfy(terminated) self._assert_is_valid_invocation_sequence( self._service_mate.tickets(), _TRANSMISSION_GROUP, _TRANSMISSION_METHOD, (invocation_payload, ), invocation_initial_metadata, invocation_terminal_metadata, links.Ticket.Termination.COMPLETION) original_service_ticket = links.Ticket( service_operation_id, 1, None, None, links.Ticket.Subscription.FULL, timeout, 0, service_initial_metadata, service_payload, service_terminal_metadata, service_code, service_message, links.Ticket.Termination.COMPLETION) self._service_link.accept_ticket(original_service_ticket) self._invocation_mate.block_until_tickets_satisfy(terminated) self._assert_is_valid_service_sequence( self._invocation_mate.tickets(), (service_payload, ), service_initial_metadata, service_terminal_metadata, service_code, service_message, links.Ticket.Termination.COMPLETION)
def testZeroMessageRoundTrip(self): test_operation_id = object() test_group = 'test package.Test Group' test_method = 'test method' identity_transformation = {(test_group, test_method): _IDENTITY} test_code = beta_interfaces.StatusCode.OK test_message = 'a test message' service_link = service.service_link(identity_transformation, identity_transformation) service_mate = test_utilities.RecordingLink() service_link.join_link(service_mate) port = service_link.add_port('[::]:0', None) service_link.start() channel = _intermediary_low.Channel('localhost:%d' % port, None) invocation_link = invocation.invocation_link(channel, None, None, identity_transformation, identity_transformation) invocation_mate = test_utilities.RecordingLink() invocation_link.join_link(invocation_mate) invocation_link.start() invocation_ticket = links.Ticket( test_operation_id, 0, test_group, test_method, links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, None, None, None, None, links.Ticket.Termination.COMPLETION, None) invocation_link.accept_ticket(invocation_ticket) service_mate.block_until_tickets_satisfy(test_cases.terminated) service_ticket = links.Ticket(service_mate.tickets()[-1].operation_id, 0, None, None, None, None, None, None, None, None, test_code, test_message, links.Ticket.Termination.COMPLETION, None) service_link.accept_ticket(service_ticket) invocation_mate.block_until_tickets_satisfy(test_cases.terminated) invocation_link.stop() service_link.begin_stop() service_link.end_stop() self.assertIs(service_mate.tickets()[-1].termination, links.Ticket.Termination.COMPLETION) self.assertIs(invocation_mate.tickets()[-1].termination, links.Ticket.Termination.COMPLETION) self.assertIs(invocation_mate.tickets()[-1].code, test_code) self.assertEqual(invocation_mate.tickets()[-1].message, test_message)
def _test_lonely_invocation_with_termination(self, termination): test_operation_id = object() test_group = 'test package.Test Service' test_method = 'test method' invocation_link_mate = test_utilities.RecordingLink() channel = _intermediary_low.Channel('nonexistent:54321', None) invocation_link = invocation.invocation_link( channel, 'nonexistent', {(test_group, test_method): _NULL_BEHAVIOR}, {(test_group, test_method): _NULL_BEHAVIOR}) invocation_link.join_link(invocation_link_mate) invocation_link.start() ticket = links.Ticket(test_operation_id, 0, test_group, test_method, links.Ticket.Subscription.FULL, test_constants.SHORT_TIMEOUT, 1, None, None, None, None, None, termination) invocation_link.accept_ticket(ticket) invocation_link_mate.block_until_tickets_satisfy(test_cases.terminated) invocation_link.stop() self.assertIsNot(invocation_link_mate.tickets()[-1].termination, links.Ticket.Termination.COMPLETION)
def _on_service_acceptance_event(self, event, server): server.service(None) service_acceptance = event.service_acceptance call = service_acceptance.call call.accept(self._completion_queue, call) try: group, method = service_acceptance.method.split('/')[1:3] except ValueError: logging.info('Illegal path "%s"!', service_acceptance.method) return request_deserializer = self._request_deserializers.get((group, method), _IDENTITY) response_serializer = self._response_serializers.get((group, method), _IDENTITY) call.read(call) context = _Context(call) self._rpc_states[call] = _RPCState(request_deserializer, response_serializer, 1, _Read.READING, None, 1, _HighWrite.OPEN, _LowWrite.OPEN, False, None, None, None, set(( _READ, _FINISH, )), context) protocol = links.Protocol(links.Protocol.Kind.SERVICER_CONTEXT, context) ticket = links.Ticket(call, 0, group, method, links.Ticket.Subscription.FULL, service_acceptance.deadline - time.time(), None, event.metadata, None, None, None, None, None, protocol) self._relay.add_value(ticket)
def _on_service_acceptance_event(self, event, server): server.service(None) service_acceptance = event.service_acceptance call = service_acceptance.call call.accept(self._completion_queue, call) try: group, method = service_acceptance.method.split('/')[1:3] except ValueError: logging.info('Illegal path "%s"!', service_acceptance.method) return request_deserializer = self._request_deserializers.get((group, method)) response_serializer = self._response_serializers.get((group, method)) if request_deserializer is None or response_serializer is None: # TODO(nathaniel): Terminate the RPC with code NOT_FOUND. call.cancel() return call.read(call) self._rpc_states[call] = _RPCState(request_deserializer, response_serializer, 1, _Read.READING, None, 1, _HighWrite.OPEN, _LowWrite.OPEN, False, None, None, None) ticket = links.Ticket(call, 0, group, method, links.Ticket.Subscription.FULL, service_acceptance.deadline - time.time(), None, event.metadata, None, None, None, None, None, 'TODO: Service Context Object!') self._relay.add_value(ticket)
def _on_read_event(self, event): call = event.tag rpc_state = self._rpc_states.get(call, None) if rpc_state is None: return if event.bytes is None: rpc_state.read = _Read.CLOSED payload = None termination = links.Ticket.Termination.COMPLETION else: if 0 < rpc_state.allowance: payload = rpc_state.request_deserializer(event.bytes) termination = None rpc_state.allowance -= 1 call.read(call) else: rpc_state.early_read = event.bytes return # TODO(issue 2916): Instead of returning: # rpc_state.read = _Read.AWAITING_ALLOWANCE ticket = links.Ticket(call, rpc_state.sequence_number, None, None, None, None, None, None, payload, None, None, None, termination, None) rpc_state.sequence_number += 1 self._relay.add_value(ticket)
def _safe_for_log_ticket(ticket): """Creates a safe-for-printing-to-the-log ticket for a given ticket. Args: ticket: Any links.Ticket. Returns: A links.Ticket that is as much as can be equal to the given ticket but possibly features values like the string "<payload of length 972321>" in place of the actual values of the given ticket. """ if isinstance(ticket.payload, (basestring, )): payload_length = len(ticket.payload) else: payload_length = -1 if payload_length < _UNCOMFORTABLY_LONG: return ticket else: return links.Ticket(ticket.operation_id, ticket.sequence_number, ticket.group, ticket.method, ticket.subscription, ticket.timeout, ticket.allowance, ticket.initial_metadata, '<payload of length {}>'.format(payload_length), ticket.terminal_metadata, ticket.code, ticket.message, ticket.termination, None)
def _on_metadata_event(self, operation_id, event, rpc_state): rpc_state.allowance -= 1 rpc_state.call.read(operation_id) rpc_state.read = _Read.READING ticket = links.Ticket(operation_id, rpc_state.sequence_number, None, None, links.Ticket.Subscription.FULL, None, None, event.metadata, None, None, None, None, None) rpc_state.sequence_number += 1 self._relay.add_value(ticket)
def timeout(self, timeout): """See _interfaces.TransmissionManager.timeout for specification.""" if self._transmitting: self._timeout = timeout else: ticket = links.Ticket(self._operation_id, self._lowest_unused_sequence_number, None, None, None, timeout, None, None, None, None, None, None, None, None) self._lowest_unused_sequence_number += 1 self._transmit(ticket)
def _invoke(self, operation_id, group, method, initial_metadata, payload, termination, timeout, allowance): """Invoke an RPC. Args: operation_id: Any object to be used as an operation ID for the RPC. group: The group to which the RPC method belongs. method: The RPC method name. initial_metadata: The initial metadata object for the RPC. payload: A payload object for the RPC or None if no payload was given at invocation-time. termination: A links.Ticket.Termination value or None indicated whether or not more writes will follow from this side of the RPC. timeout: A duration of time in seconds to allow for the RPC. allowance: The number of payloads (beyond the free first one) that the local ticket exchange mate has granted permission to be read. """ if termination is links.Ticket.Termination.COMPLETION: high_write = _HighWrite.CLOSED elif termination is None: high_write = _HighWrite.OPEN else: return request_serializer = self._request_serializers.get((group, method)) response_deserializer = self._response_deserializers.get( (group, method)) if request_serializer is None or response_deserializer is None: cancellation_ticket = links.Ticket( operation_id, 0, None, None, None, None, None, None, None, None, None, None, links.Ticket.Termination.CANCELLATION) self._relay.add_value(cancellation_ticket) return call = _intermediary_low.Call(self._channel, self._completion_queue, '/%s/%s' % (group, method), self._host, time.time() + timeout) if initial_metadata is not None: for metadata_key, metadata_value in initial_metadata: call.add_metadata(metadata_key, metadata_value) call.invoke(self._completion_queue, operation_id, operation_id) if payload is None: if high_write is _HighWrite.CLOSED: call.complete(operation_id) low_write = _LowWrite.CLOSED else: low_write = _LowWrite.OPEN else: call.write(request_serializer(payload), operation_id) low_write = _LowWrite.ACTIVE self._rpc_states[operation_id] = _RPCState( call, request_serializer, response_deserializer, 0, _Read.AWAITING_METADATA, 1 if allowance is None else (1 + allowance), high_write, low_write)
def _on_write_event(self, operation_id, unused_event, rpc_state): if rpc_state.high_write is _HighWrite.CLOSED: rpc_state.call.complete(operation_id) rpc_state.low_write = _LowWrite.CLOSED else: ticket = links.Ticket(operation_id, rpc_state.sequence_number, None, None, None, None, 1, None, None, None, None, None, None) rpc_state.sequence_number += 1 self._relay.add_value(ticket) rpc_state.low_write = _LowWrite.OPEN
def _on_write_event(self, operation_id, unused_event, rpc_state): if rpc_state.high_write is _HighWrite.CLOSED: rpc_state.call.complete(operation_id) rpc_state.due.add(_COMPLETE) rpc_state.due.remove(_WRITE) rpc_state.low_write = _LowWrite.CLOSED else: ticket = links.Ticket(operation_id, rpc_state.sequence_number, None, None, None, None, 1, None, None, None, None, None, None, None) rpc_state.sequence_number += 1 self._relay.add_value(ticket) rpc_state.low_write = _LowWrite.OPEN _no_longer_due(_WRITE, rpc_state, operation_id, self._rpc_states)
def kick_off(self, group, method, timeout, initial_metadata, payload, completion, allowance): """See _interfaces.TransmissionManager.kickoff for specification.""" # TODO(nathaniel): Support other subscriptions. subscription = links.Ticket.Subscription.FULL terminal_metadata, code, message, termination = _explode_completion( completion) self._remote_allowance = 1 if payload is None else 0 ticket = links.Ticket(self._operation_id, 0, group, method, subscription, timeout, allowance, initial_metadata, payload, terminal_metadata, code, message, termination, None) self._lowest_unused_sequence_number = 1 self._transmit(ticket)
def _on_read_event(self, operation_id, event, rpc_state): if event.bytes is None: rpc_state.read = _Read.CLOSED else: if 0 < rpc_state.allowance: rpc_state.allowance -= 1 rpc_state.call.read(operation_id) else: rpc_state.read = _Read.AWAITING_ALLOWANCE ticket = links.Ticket(operation_id, rpc_state.sequence_number, None, None, None, None, None, None, rpc_state.response_deserializer(event.bytes), None, None, None, None) rpc_state.sequence_number += 1 self._relay.add_value(ticket)
def advance(self, initial_metadata, payload, completion, allowance): """See _interfaces.TransmissionManager.advance for specification.""" if self._abort.kind is not _Abort.Kind.NOT_ABORTED: return effective_initial_metadata = initial_metadata effective_payload = payload effective_completion = completion if allowance is not None and not self._remote_complete: effective_allowance = allowance else: effective_allowance = None if self._transmitting: if effective_initial_metadata is not None: self._initial_metadata = effective_initial_metadata if effective_payload is not None: self._payloads.append(effective_payload) if effective_completion is not None: self._completion = effective_completion if effective_allowance is not None: self._local_allowance += effective_allowance else: if effective_payload is not None: if 0 < self._remote_allowance: ticket_payload = effective_payload self._remote_allowance -= 1 else: self._payloads.append(effective_payload) ticket_payload = None else: ticket_payload = None if effective_completion is not None and not self._payloads: ticket_completion = effective_completion else: self._completion = effective_completion ticket_completion = None if any((effective_initial_metadata, ticket_payload, ticket_completion, effective_allowance)): terminal_metadata, code, message, termination = _explode_completion( completion) ticket = links.Ticket(self._operation_id, self._lowest_unused_sequence_number, None, None, None, None, allowance, effective_initial_metadata, ticket_payload, terminal_metadata, code, message, termination, None) self._lowest_unused_sequence_number += 1 self._transmit(ticket)
def abort(self, outcome): """See _interfaces.TransmissionManager.abort for specification.""" if self._transmitting: self._aborted, self._abortion_outcome = True, outcome else: self._aborted = True if outcome is not None: termination = _constants.ABORTION_OUTCOME_TO_TICKET_TERMINATION[ outcome] if termination is not None: ticket = links.Ticket(self._operation_id, self._lowest_unused_sequence_number, None, None, None, None, None, None, None, None, None, None, termination, None) self._transmit(ticket)
def _on_metadata_event(self, operation_id, event, rpc_state): if _FINISH in rpc_state.due: rpc_state.allowance -= 1 rpc_state.call.read(operation_id) rpc_state.read = _Read.READING rpc_state.due.add(_READ) rpc_state.due.remove(_METADATA) ticket = links.Ticket(operation_id, rpc_state.sequence_number, None, None, links.Ticket.Subscription.FULL, None, None, event.metadata, None, None, None, None, None, None) rpc_state.sequence_number += 1 self._relay.add_value(ticket) else: _no_longer_due(_METADATA, rpc_state, operation_id, self._rpc_states)
def _on_finish_event(self, operation_id, event, rpc_state): self._rpc_states.pop(operation_id, None) if event.status.code is _intermediary_low.Code.OK: termination = links.Ticket.Termination.COMPLETION elif event.status.code is _intermediary_low.Code.CANCELLED: termination = links.Ticket.Termination.CANCELLATION elif event.status.code is _intermediary_low.Code.DEADLINE_EXCEEDED: termination = links.Ticket.Termination.EXPIRATION else: termination = links.Ticket.Termination.TRANSMISSION_FAILURE ticket = links.Ticket(operation_id, rpc_state.sequence_number, None, None, None, None, None, None, None, event.metadata, event.status.code, event.status.details, termination) rpc_state.sequence_number += 1 self._relay.add_value(ticket)
def abort(self, outcome, code, message): """See _interfaces.TransmissionManager.abort for specification.""" if self._abort.kind is _Abort.Kind.NOT_ABORTED: termination = _constants.ABORTION_OUTCOME_TO_TICKET_TERMINATION.get( outcome) if termination is None: self._abort = _ABORTED_NO_NOTIFY elif self._transmitting: self._abort = _Abort(_Abort.Kind.ABORTED_NOTIFY_NEEDED, termination, code, message) else: ticket = links.Ticket(self._operation_id, self._lowest_unused_sequence_number, None, None, None, None, None, None, None, None, code, message, termination, None) self._transmit(ticket)
def _on_finish_event(self, event): call = event.tag rpc_state = self._rpc_states[call] _no_longer_due(_FINISH, rpc_state, call, self._rpc_states) code = event.status.code if code is _intermediary_low.Code.OK: return if code is _intermediary_low.Code.CANCELLED: termination = links.Ticket.Termination.CANCELLATION elif code is _intermediary_low.Code.DEADLINE_EXCEEDED: termination = links.Ticket.Termination.EXPIRATION else: termination = links.Ticket.Termination.TRANSMISSION_FAILURE ticket = links.Ticket(call, rpc_state.sequence_number, None, None, None, None, None, None, None, None, None, None, termination, None) rpc_state.sequence_number += 1 self._relay.add_value(ticket)
def _on_write_event(self, event): call = event.tag rpc_state = self._rpc_states.get(call, None) if rpc_state is None: return if rpc_state.high_write is _HighWrite.CLOSED: if rpc_state.terminal_metadata is not None: _metadatafy(call, rpc_state.terminal_metadata) call.status( _intermediary_low.Status(rpc_state.code, rpc_state.message), call) rpc_state.low_write = _LowWrite.CLOSED else: ticket = links.Ticket(call, rpc_state.sequence_number, None, None, None, None, 1, None, None, None, None, None, None, None) rpc_state.sequence_number += 1 self._relay.add_value(ticket) rpc_state.low_write = _LowWrite.OPEN
def allowance(self, allowance): """See _interfaces.TransmissionManager.allowance for specification.""" if self._transmitting or not self._payloads: self._remote_allowance += allowance else: self._remote_allowance += allowance - 1 payload = self._payloads.pop(0) if self._payloads: completion = None else: completion = self._completion self._completion = None terminal_metadata, code, message, termination = _explode_completion( completion) ticket = links.Ticket(self._operation_id, self._lowest_unused_sequence_number, None, None, None, None, None, None, payload, terminal_metadata, code, message, termination, None) self._lowest_unused_sequence_number += 1 self._transmit(ticket)
def _on_write_event(self, event): call = event.tag rpc_state = self._rpc_states[call] if rpc_state.high_write is _HighWrite.CLOSED: if rpc_state.terminal_metadata is not None: _metadatafy(call, rpc_state.terminal_metadata) status = _status(links.Ticket.Termination.COMPLETION, rpc_state.code, rpc_state.message) call.status(status, call) rpc_state.low_write = _LowWrite.CLOSED rpc_state.due.add(_COMPLETE) rpc_state.due.remove(_WRITE) else: ticket = links.Ticket(call, rpc_state.sequence_number, None, None, None, None, 1, None, None, None, None, None, None, None) rpc_state.sequence_number += 1 self._relay.add_value(ticket) rpc_state.low_write = _LowWrite.OPEN _no_longer_due(_WRITE, rpc_state, call, self._rpc_states)
def _on_finish_event(self, operation_id, event, rpc_state): _no_longer_due(_FINISH, rpc_state, operation_id, self._rpc_states) if event.status.code == _intermediary_low.Code.OK: termination = links.Ticket.Termination.COMPLETION elif event.status.code == _intermediary_low.Code.CANCELLED: termination = links.Ticket.Termination.CANCELLATION elif event.status.code == _intermediary_low.Code.DEADLINE_EXCEEDED: termination = links.Ticket.Termination.EXPIRATION elif event.status.code == _intermediary_low.Code.UNIMPLEMENTED: termination = links.Ticket.Termination.REMOTE_FAILURE elif event.status.code == _intermediary_low.Code.UNKNOWN: termination = links.Ticket.Termination.LOCAL_FAILURE else: termination = links.Ticket.Termination.TRANSMISSION_FAILURE code = _constants.LOW_STATUS_CODE_TO_HIGH_STATUS_CODE[ event.status.code] ticket = links.Ticket(operation_id, rpc_state.sequence_number, None, None, None, None, None, None, None, event.metadata, code, event.status.details, termination, None) rpc_state.sequence_number += 1 self._relay.add_value(ticket)
def _perform_scenario_test(self, scenario): test_operation_id = object() test_group, test_method = scenario.group_and_method() test_code = _intermediary_low.Code.OK test_message = 'a scenario test message' service_link = service.service_link( {(test_group, test_method): scenario.deserialize_request}, {(test_group, test_method): scenario.serialize_response}) service_mate = test_utilities.RecordingLink() service_link.join_link(service_mate) port = service_link.add_port('[::]:0', None) service_link.start() channel = _intermediary_low.Channel('localhost:%d' % port, None) invocation_link = invocation.invocation_link( channel, 'localhost', None, {(test_group, test_method): scenario.serialize_request}, {(test_group, test_method): scenario.deserialize_response}) invocation_mate = test_utilities.RecordingLink() invocation_link.join_link(invocation_mate) invocation_link.start() invocation_ticket = links.Ticket(test_operation_id, 0, test_group, test_method, links.Ticket.Subscription.FULL, test_constants.LONG_TIMEOUT, None, None, None, None, None, None, None, None) invocation_link.accept_ticket(invocation_ticket) requests = scenario.requests() for request_index, request in enumerate(requests): request_ticket = links.Ticket(test_operation_id, 1 + request_index, None, None, None, None, 1, None, request, None, None, None, None, None) invocation_link.accept_ticket(request_ticket) service_mate.block_until_tickets_satisfy( test_cases.at_least_n_payloads_received_predicate( 1 + request_index)) response_ticket = links.Ticket( service_mate.tickets()[0].operation_id, request_index, None, None, None, None, 1, None, scenario.response_for_request(request), None, None, None, None, None) service_link.accept_ticket(response_ticket) invocation_mate.block_until_tickets_satisfy( test_cases.at_least_n_payloads_received_predicate( 1 + request_index)) request_count = len(requests) invocation_completion_ticket = links.Ticket( test_operation_id, request_count + 1, None, None, None, None, None, None, None, None, None, None, links.Ticket.Termination.COMPLETION, None) invocation_link.accept_ticket(invocation_completion_ticket) service_mate.block_until_tickets_satisfy(test_cases.terminated) service_completion_ticket = links.Ticket( service_mate.tickets()[0].operation_id, request_count, None, None, None, None, None, None, None, None, test_code, test_message, links.Ticket.Termination.COMPLETION, None) service_link.accept_ticket(service_completion_ticket) invocation_mate.block_until_tickets_satisfy(test_cases.terminated) invocation_link.stop() service_link.begin_stop() service_link.end_stop() observed_requests = tuple(ticket.payload for ticket in service_mate.tickets() if ticket.payload is not None) observed_responses = tuple(ticket.payload for ticket in invocation_mate.tickets() if ticket.payload is not None) self.assertTrue(scenario.verify_requests(observed_requests)) self.assertTrue(scenario.verify_responses(observed_responses))
def _next_ticket(self): """Creates the next ticket to be transmitted. Returns: A links.Ticket to be sent to the other side of the operation or None if there is nothing to be sent at this time. """ if self._aborted: if self._abortion_outcome is None: return None else: termination = _constants.ABORTION_OUTCOME_TO_TICKET_TERMINATION[ self._abortion_outcome] if termination is None: return None else: self._abortion_outcome = None return links.Ticket(self._operation_id, self._lowest_unused_sequence_number, None, None, None, None, None, None, None, None, None, None, termination, None) action = False # TODO(nathaniel): Support other subscriptions. local_subscription = links.Ticket.Subscription.FULL timeout = self._timeout if timeout is not None: self._timeout = None action = True if self._local_allowance <= 0: allowance = None else: allowance = self._local_allowance self._local_allowance = 0 action = True initial_metadata = self._initial_metadata if initial_metadata is not None: self._initial_metadata = None action = True if not self._payloads or self._remote_allowance <= 0: payload = None else: payload = self._payloads.pop(0) self._remote_allowance -= 1 action = True if self._completion is None or self._payloads: terminal_metadata, code, message, termination = None, None, None, None else: terminal_metadata, code, message, termination = _explode_completion( self._completion) self._completion = None action = True if action: ticket = links.Ticket(self._operation_id, self._lowest_unused_sequence_number, None, None, local_subscription, timeout, allowance, initial_metadata, payload, terminal_metadata, code, message, termination, None) self._lowest_unused_sequence_number += 1 return ticket else: return None
def add_ticket(self, ticket): with self._lock: call = ticket.operation_id rpc_state = self._rpc_states.get(call) if rpc_state is None: return if ticket.initial_metadata is not None: _metadatafy(call, ticket.initial_metadata) call.premetadata() rpc_state.premetadataed = True elif not rpc_state.premetadataed: if (ticket.terminal_metadata is not None or ticket.payload is not None or ticket.termination is not None or ticket.code is not None or ticket.message is not None): call.premetadata() rpc_state.premetadataed = True if ticket.allowance is not None: if rpc_state.early_read is None: rpc_state.allowance += ticket.allowance else: payload = rpc_state.request_deserializer( rpc_state.early_read) rpc_state.allowance += ticket.allowance - 1 rpc_state.early_read = None if rpc_state.read is _Read.READING: call.read(call) rpc_state.due.add(_READ) termination = None else: termination = links.Ticket.Termination.COMPLETION early_read_ticket = links.Ticket(call, rpc_state.sequence_number, None, None, None, None, None, None, payload, None, None, None, termination, None) rpc_state.sequence_number += 1 self._relay.add_value(early_read_ticket) if ticket.payload is not None: disable_compression = rpc_state.context.next_compression_disabled( ) if disable_compression: flags = _intermediary_low.WriteFlags.WRITE_NO_COMPRESS else: flags = 0 call.write(rpc_state.response_serializer(ticket.payload), call, flags) rpc_state.due.add(_WRITE) rpc_state.low_write = _LowWrite.ACTIVE if ticket.terminal_metadata is not None: rpc_state.terminal_metadata = ticket.terminal_metadata if ticket.code is not None: rpc_state.code = ticket.code if ticket.message is not None: rpc_state.message = ticket.message if ticket.termination is links.Ticket.Termination.COMPLETION: rpc_state.high_write = _HighWrite.CLOSED if rpc_state.low_write is _LowWrite.OPEN: if rpc_state.terminal_metadata is not None: _metadatafy(call, rpc_state.terminal_metadata) status = _status(links.Ticket.Termination.COMPLETION, rpc_state.code, rpc_state.message) call.status(status, call) rpc_state.due.add(_COMPLETE) rpc_state.low_write = _LowWrite.CLOSED elif ticket.termination is not None: if rpc_state.terminal_metadata is not None: _metadatafy(call, rpc_state.terminal_metadata) status = _status(ticket.termination, rpc_state.code, rpc_state.message) call.status(status, call) rpc_state.due.add(_COMPLETE)
def add_ticket(self, ticket): with self._lock: if self._server is None: return call = ticket.operation_id rpc_state = self._rpc_states.get(call) if rpc_state is None: return if ticket.initial_metadata is not None: _metadatafy(call, ticket.initial_metadata) call.premetadata() rpc_state.premetadataed = True elif not rpc_state.premetadataed: if (ticket.terminal_metadata is not None or ticket.payload is not None or ticket.termination is not None or ticket.code is not None or ticket.message is not None): call.premetadata() rpc_state.premetadataed = True if ticket.allowance is not None: if rpc_state.early_read is None: rpc_state.allowance += ticket.allowance else: payload = rpc_state.request_deserializer( rpc_state.early_read) rpc_state.allowance += ticket.allowance - 1 rpc_state.early_read = None if rpc_state.read is _Read.READING: call.read(call) termination = None else: termination = links.Ticket.Termination.COMPLETION early_read_ticket = links.Ticket(call, rpc_state.sequence_number, None, None, None, None, None, None, payload, None, None, None, termination, None) rpc_state.sequence_number += 1 self._relay.add_value(early_read_ticket) if ticket.payload is not None: call.write(rpc_state.response_serializer(ticket.payload), call) rpc_state.low_write = _LowWrite.ACTIVE if ticket.terminal_metadata is not None: rpc_state.terminal_metadata = ticket.terminal_metadata if ticket.code is not None: rpc_state.code = ticket.code if ticket.message is not None: rpc_state.message = ticket.message if ticket.termination is links.Ticket.Termination.COMPLETION: rpc_state.high_write = _HighWrite.CLOSED if rpc_state.low_write is _LowWrite.OPEN: if rpc_state.terminal_metadata is not None: _metadatafy(call, rpc_state.terminal_metadata) status = _intermediary_low.Status( _intermediary_low.Code.OK if rpc_state.code is None else rpc_state.code, '' if rpc_state.message is None else rpc_state.message) call.status(status, call) rpc_state.low_write = _LowWrite.CLOSED elif ticket.termination is not None: call.cancel() self._rpc_states.pop(call, None)
def _invoke(self, operation_id, group, method, initial_metadata, payload, termination, timeout, allowance, options): """Invoke an RPC. Args: operation_id: Any object to be used as an operation ID for the RPC. group: The group to which the RPC method belongs. method: The RPC method name. initial_metadata: The initial metadata object for the RPC. payload: A payload object for the RPC or None if no payload was given at invocation-time. termination: A links.Ticket.Termination value or None indicated whether or not more writes will follow from this side of the RPC. timeout: A duration of time in seconds to allow for the RPC. allowance: The number of payloads (beyond the free first one) that the local ticket exchange mate has granted permission to be read. options: A beta_interfaces.GRPCCallOptions value or None. """ if termination is links.Ticket.Termination.COMPLETION: high_write = _HighWrite.CLOSED elif termination is None: high_write = _HighWrite.OPEN else: return transformed_initial_metadata = self._metadata_transformer( initial_metadata) request_serializer = self._request_serializers.get((group, method), _IDENTITY) response_deserializer = self._response_deserializers.get( (group, method), _IDENTITY) call = _intermediary_low.Call(self._channel, self._completion_queue, '/%s/%s' % (group, method), self._host, time.time() + timeout) if options is not None and options.credentials is not None: call.set_credentials(options.credentials._low_credentials) if transformed_initial_metadata is not None: for metadata_key, metadata_value in transformed_initial_metadata: call.add_metadata(metadata_key, metadata_value) call.invoke(self._completion_queue, operation_id, operation_id) if payload is None: if high_write is _HighWrite.CLOSED: call.complete(operation_id) low_write = _LowWrite.CLOSED due = set(( _METADATA, _COMPLETE, _FINISH, )) else: low_write = _LowWrite.OPEN due = set(( _METADATA, _FINISH, )) else: if options is not None and options.disable_compression: flags = _intermediary_low.WriteFlags.WRITE_NO_COMPRESS else: flags = 0 call.write(request_serializer(payload), operation_id, flags) low_write = _LowWrite.ACTIVE due = set(( _WRITE, _METADATA, _FINISH, )) context = _Context() self._rpc_states[operation_id] = _RPCState( call, request_serializer, response_deserializer, 1, _Read.AWAITING_METADATA, 1 if allowance is None else (1 + allowance), high_write, low_write, due, context) protocol = links.Protocol(links.Protocol.Kind.INVOCATION_CONTEXT, context) ticket = links.Ticket(operation_id, 0, None, None, None, None, None, None, None, None, None, None, None, protocol) self._relay.add_value(ticket)