async def agent_processing(http_connection) -> bool: # we block here to give it some time for the envelope to make it to the queue await asyncio.sleep(1) envelope = await http_connection.receive() if envelope is not None: incoming_message = cast( HttpMessage, HttpSerializer().decode(envelope.message)) message = HttpMessage( performative=HttpMessage.Performative.RESPONSE, dialogue_reference=("", ""), target=incoming_message.message_id, message_id=incoming_message.message_id + 1, version=incoming_message.version, headers=incoming_message.headers, status_code=201, status_text="Created", bodyy=b"Response body", ) response_envelope = Envelope( to=envelope.sender, sender=envelope.to, protocol_id=envelope.protocol_id, context=envelope.context, message=HttpSerializer().encode(message), ) await http_connection.send(response_envelope) is_exiting_correctly = True else: is_exiting_correctly = False return is_exiting_correctly
async def test_connecting_to_aca(self): http_client_connection = HTTPClientConnection( address=self.aea_address, provider_address=self.aca_admin_address, provider_port=self.aca_admin_port, ) http_client_connection.loop = asyncio.get_event_loop() # Request messages request_http_message = HttpMessage( dialogue_reference=("", ""), target=0, message_id=1, performative=HttpMessage.Performative.REQUEST, method="GET", url="http://{}:{}/status".format(self.aca_admin_address, self.aca_admin_port), headers="", version="", bodyy=b"", ) request_envelope = Envelope( to="ACA", sender="AEA", protocol_id=HTTP_PROTOCOL_PUBLIC_ID, message=HttpSerializer().encode(request_http_message), ) try: # connect to ACA await http_client_connection.connect() assert http_client_connection.connection_status.is_connected is True # send request to ACA await http_client_connection.send(envelope=request_envelope) # receive response from ACA response_envelop = await http_client_connection.receive() # check the response assert response_envelop.to == self.aea_address assert response_envelop.sender == "HTTP Server" assert response_envelop.protocol_id == HTTP_PROTOCOL_PUBLIC_ID decoded_response_message = HttpSerializer().decode( response_envelop.message) assert (decoded_response_message.performative == HttpMessage.Performative.RESPONSE) assert decoded_response_message.version == "" assert decoded_response_message.status_code == 200 assert decoded_response_message.status_text == "OK" assert decoded_response_message.headers is not None assert decoded_response_message.version is not None finally: # disconnect from ACA await http_client_connection.disconnect() assert http_client_connection.connection_status.is_connected is False
async def test_send_connection_send(self): """Test send connection error.""" client_id = "to_key" message = HttpMessage( performative=HttpMessage.Performative.RESPONSE, dialogue_reference=("", ""), target=1, message_id=2, headers="", version="", status_code=200, status_text="Success", bodyy=b"", ) envelope = Envelope( to=client_id, sender="from_key", protocol_id=self.protocol_id, message=HttpSerializer().encode(message), ) self.http_connection.channel.pending_request_ids.add("to_key") await self.http_connection.send(envelope) assert (self.http_connection.channel.dispatch_ready_envelopes.get( client_id) == envelope) assert self.http_connection.channel.pending_request_ids == set() # clean up: self.http_connection.channel.dispatch_ready_envelopes = ( {}) # type: Dict[str, Envelope]
def to_envelope(self, connection_id: PublicId, agent_address: str) -> Envelope: """ Process incoming API request by packaging into Envelope and sending it in-queue. The Envelope's message body contains the "performative", "path", "params", and "payload". :param http_method: the http method :param url: the url :param param: the parameter :param body: the body """ uri = URI(self.full_url_pattern) context = EnvelopeContext(connection_id=connection_id, uri=uri) http_message = HttpMessage( dialogue_reference=("", ""), target=0, message_id=1, performative=HttpMessage.Performative.REQUEST, method=self.method, url=self.full_url_pattern, headers=self.parameters.header.as_string(), bodyy=self.body.encode() if self.body is not None else b"", version="", ) envelope = Envelope( to=agent_address, sender=self.id, protocol_id=PublicId.from_str("fetchai/http:0.1.0"), context=context, message=HttpSerializer().encode(http_message), ) return envelope
def send(self, request_envelope: Envelope) -> None: """ Convert an http envelope into an http request, send the http request, wait for and receive its response, translate the response into a response envelop, and send the response envelope to the in-queue. :param request_envelope: the envelope containing an http request :return: None """ if self.excluded_protocols is not None: if request_envelope.protocol_id in self.excluded_protocols: logger.error( "This envelope cannot be sent with the http client connection: protocol_id={}" .format(request_envelope.protocol_id)) raise ValueError("Cannot send message.") if request_envelope is not None: request_http_message = cast( HttpMessage, HttpSerializer().decode(request_envelope.message)) if (request_http_message.performative == HttpMessage.Performative.REQUEST): response = requests.request( method=request_http_message.method, url=request_http_message.url, headers=request_http_message.headers, data=request_http_message.bodyy, ) response_envelope = self.to_envelope( self.connection_id, request_http_message, response) self.in_queue.put_nowait(response_envelope) # type: ignore else: logger.warning( "The HTTPMessage performative must be a REQUEST. Envelop dropped." )
def to_envelope( self, connection_id: PublicId, http_request_message: HttpMessage, http_response: requests.models.Response, ) -> Envelope: """ Convert an HTTP response object (from the 'requests' library) into an Envelope containing an HttpMessage (from the 'http' Protocol). :param connection_id: the connection id :param http_request_message: the message of the http request envelop :param http_response: the http response object """ context = EnvelopeContext(connection_id=connection_id) http_message = HttpMessage( dialogue_reference=http_request_message.dialogue_reference, target=http_request_message.target, message_id=http_request_message.message_id, performative=HttpMessage.Performative.RESPONSE, status_code=http_response.status_code, headers=json.dumps(dict(http_response.headers)), status_text=http_response.reason, bodyy=http_response.content if http_response.content is not None else b"", version="", ) envelope = Envelope( to=self.agent_address, sender="HTTP Server", protocol_id=PublicId.from_str("fetchai/http:0.1.0"), context=context, message=HttpSerializer().encode(http_message), ) return envelope
async def to_envelope(self, request: web.Request) -> Envelope: """ Convert a webhook request object into an Envelope containing an HttpMessage (from the 'http' Protocol). :param request: the webhook request :return: The envelop representing the webhook request """ payload_bytes = await request.read() version = str(request.version[0]) + "." + str(request.version[1]) context = EnvelopeContext(uri=URI("aea/mail/base.py")) http_message = HttpMessage( performative=HttpMessage.Performative.REQUEST, method=request.method, url=str(request.url), version=version, headers=json.dumps(dict(request.headers)), bodyy=payload_bytes if payload_bytes is not None else b"", ) envelope = Envelope( to=self.agent_address, sender=request.remote, protocol_id=PublicId.from_str("fetchai/http:0.1.0"), context=context, message=HttpSerializer().encode(http_message), ) return envelope
def _handle_get(self, http_msg: HttpMessage) -> None: """ Handle a Http request of verb GET. :param http_msg: the http message :return: None """ http_response = HttpMessage( dialogue_reference=http_msg.dialogue_reference, target=http_msg.message_id, message_id=http_msg.message_id + 1, performative=HttpMessage.Performative.RESPONSE, version=http_msg.version, status_code=200, status_text="Success", headers=http_msg.headers, bodyy=json.dumps({ "tom": { "type": "cat", "age": 10 } }).encode("utf-8"), ) self.context.logger.info("[{}] responding with: {}".format( self.context.agent_name, http_response)) self.context.outbox.put_message( sender=self.context.agent_address, to=http_msg.counterparty, protocol_id=http_response.protocol_id, message=HttpSerializer().encode(http_response), )
async def test_http_send(): """Test the send functionality of the http client connection.""" address = get_host() port = get_unused_tcp_port() agent_address = "some agent address" http_client_connection = HTTPClientConnection( address=agent_address, provider_address=address, provider_port=port, ) http_client_connection.loop = asyncio.get_event_loop() request_http_message = HttpMessage( dialogue_reference=("", ""), target=0, message_id=1, performative=HttpMessage.Performative.REQUEST, method="", url="", headers="", version="", bodyy=b"", ) request_envelope = Envelope( to="receiver", sender="sender", protocol_id=UNKNOWN_PROTOCOL_PUBLIC_ID, message=HttpSerializer().encode(request_http_message), ) connection_response_mock = Mock() connection_response_mock.status_code = 200 with mock.patch.object(requests, "request", return_value=connection_response_mock): await http_client_connection.connect() assert http_client_connection.connection_status.is_connected is True send_response_mock = Mock() send_response_mock.status_code = 200 send_response_mock.headers = {"headers": "some header"} send_response_mock.reason = "OK" send_response_mock.content = b"Some content" with mock.patch.object(requests, "request", return_value=send_response_mock): await http_client_connection.send(envelope=request_envelope) # TODO: Consider returning the response from the server in order to be able to assert that the message send! assert True await http_client_connection.disconnect() assert http_client_connection.connection_status.is_connected is False
def admin_get(self, path: str, content: Dict = None): # Request message & envelope request_http_message = HttpMessage( performative=HttpMessage.Performative.REQUEST, method="GET", url=self.admin_url + path, headers="", version="", bodyy=b"" if content is None else json.dumps(content).encode("utf-8"), ) self.context.outbox.put_message( to=self.admin_url, sender=self.context.agent_address, protocol_id=HTTP_PROTOCOL_PUBLIC_ID, message=HttpSerializer().encode(request_http_message), )
def from_envelope(cls, envelope: Optional[Envelope] = None) -> "Response": """ Turn an envelope into a response. :param envelope: the envelope :return: the response """ if envelope is not None: http_message = cast(HttpMessage, HttpSerializer().decode(envelope.message)) if http_message.performative == HttpMessage.Performative.RESPONSE: response = Response( http_message.status_code, http_message.status_text, http_message.bodyy, ) else: response = Response(SERVER_ERROR, "Server error") else: response = Response(REQUEST_TIMEOUT, "Request Timeout") return response
def _admin_post(self, path: str, content: Dict = None): # Request message & envelope request_http_message = HttpMessage( performative=HttpMessage.Performative.REQUEST, method="POST", url=self.admin_url + path, headers="", version="", bodyy=b"" if content is None else json.dumps(content).encode("utf-8"), ) context = EnvelopeContext( connection_id=HTTP_CLIENT_CONNECTION_PUBLIC_ID) envelope = Envelope( to=self.admin_url, sender=self.context.agent_address, protocol_id=HTTP_PROTOCOL_PUBLIC_ID, context=context, message=HttpSerializer().encode(request_http_message), ) self.context.outbox.put(envelope)
async def test_send_connection_drop(self): """Test send connection error.""" client_id = "to_key" message = HttpMessage( performative=HttpMessage.Performative.RESPONSE, dialogue_reference=("", ""), target=1, message_id=2, headers="", version="", status_code=200, status_text="Success", bodyy=b"", ) envelope = Envelope( to=client_id, sender="from_key", protocol_id=self.protocol_id, message=HttpSerializer().encode(message), ) await self.http_connection.send(envelope) # we expect the envelope to be dropped assert (self.http_connection.channel.dispatch_ready_envelopes.get( client_id) is None)
async def test_end_to_end_aea_aca(self): # AEA components ledger_apis = LedgerApis({}, FetchAICrypto.identifier) wallet = Wallet({FetchAICrypto.identifier: FETCHAI_PRIVATE_KEY_FILE}) identity = Identity( name="my_aea_1", address=wallet.addresses.get(FetchAICrypto.identifier), default_address_key=FetchAICrypto.identifier, ) http_client_connection = HTTPClientConnection( address=self.aea_address, provider_address=self.aca_admin_address, provider_port=self.aca_admin_port, ) resources = Resources() # create AEA aea = AEA(identity, [http_client_connection], wallet, ledger_apis, resources) # Add http protocol to AEA resources http_protocol_configuration = ProtocolConfig.from_json( yaml.safe_load( open( os.path.join( self.cwd, "packages", "fetchai", "protocols", "http", "protocol.yaml", )))) http_protocol = Protocol(http_protocol_configuration, HttpSerializer()) resources.add_protocol(http_protocol) # Request message & envelope request_http_message = HttpMessage( dialogue_reference=("", ""), target=0, message_id=1, performative=HttpMessage.Performative.REQUEST, method="GET", url="http://{}:{}/status".format(self.aca_admin_address, self.aca_admin_port), headers="", version="", bodyy=b"", ) request_envelope = Envelope( to="ACA", sender="AEA", protocol_id=HTTP_PROTOCOL_PUBLIC_ID, message=HttpSerializer().encode(request_http_message), ) # add a simple skill with handler skill_context = SkillContext(aea.context) skill_config = SkillConfig(name="simple_skill", author="fetchai", version="0.1.0") aea_handler = AEAHandler(skill_context=skill_context, name="aea_handler") simple_skill = Skill(skill_config, skill_context, handlers={aea_handler.name: aea_handler}) resources.add_skill(simple_skill) # add error skill to AEA error_skill = Skill.from_dir(os.path.join(AEA_DIR, "skills", "error")) resources.add_skill(error_skill) # start AEA thread t_aea = Thread(target=aea.start) try: t_aea.start() time.sleep(1.0) aea.outbox.put(request_envelope) time.sleep(5.0) assert (aea_handler.handled_message.performative == HttpMessage.Performative.RESPONSE) assert aea_handler.handled_message.version == "" assert aea_handler.handled_message.status_code == 200 assert aea_handler.handled_message.status_text == "OK" assert aea_handler.handled_message.headers is not None assert aea_handler.handled_message.version is not None finally: aea.stop() t_aea.join()