App engine Channel API returns no messages - python

Problem description: channel messages no returned to ajax script.
Initially, messages are delivered to clietn side, but the problem appears when I set larger timeout in js:
goog.appengine.Socket.POLLING_TIMEOUT_MS = 5000; //poll every 5 seconds
I've added a very basic Python code to test if Channel API works in my Google App Engine app.
index:
token = channel.create_channel(CHANNEL_NAME)
channel.send_message(CHANNEL_NAME, message)
#token is passed to template
additional_view:
#is another view, trigger manually from browser after index
from django.utils import simplejson
channel.send_message(CHANNEL_NAME, simplejson.dumps(data))
At client side I have a regular js with onMessage code.
The problem is that no messages are returned to client-side requests. They all come empty to polling ajax (as seen in Firebug). In application log I can see that channel is created:
"Creating channel token channel-2382918168-broadcast with client id broadcast"
and later message is sent but with a comment:
in between come these requests:
INFO 2011-08-03 14:33:32,000 dev_appserver.py:4248] "POST /_ah/channel/connected/ HTTP/1.1" 404 -
INFO 2011-08-03 14:33:33,780 dev_appserver.py:4248] "POST /_ah/channel/disconnected/ HTTP/1.1" 404 -
** ....message text...to channel with key (broadcast): no clients connected***
How does channel/message function at deeper level? Are messages lost if no clients are connected or they are retrived by newly connect clients?
If for some reason I create a channel with the same name, would it distroy undelivered messages it has inside?

Stay away from setting the POLLING_TIMEOUT_MS higher than 1.5 sec, the dev_appserver will assume you have disconnected.
It does not work via polling in production, so you do not have to worry about the timeout really.
Edit: just saw Robert's comment; personally I have even had issues if I set the polling to 3sec in Chrome/Safari/Firefox. I now just have ?disable_channel=true query strings on my apps so that I can run them without setting my laptop on fire with the CPU usage.

Related

Android app written in kotlin with the volley library fails to call my backend, which was written in python with django

so as the post says, i'm making an app, for which i have a server/rest api written with the django framework. When i try to call the api using volley, i fail to get any response, and i don't see it show up on my server's dashboard
The server is running on my local machine, and the app is running on an emulator, since i'm using android studio
i'd like to send a request and display the response in a textview on the app, for now thats all i need to continue onto the next part
What ends up happening instead is that it seems to not even hit my server, the app displays the text i set for if the request fails, adn it doesn't show up on the dashboard of my server
here's basically all the code in my mobile app
val textView = findViewById<TextView>(R.id.text)
val queue = Volley.newRequestQueue(this)
val url = "http://127.0.0.1:8000//index"//i also tried to use 192.168.1.2 as the host ip. the port is always 8000
val stringRequest = StringRequest(Request.Method.GET, url,
Response.Listener<String> { response ->
// Display the first 500 characters of the response string.
textView.text = "Response is: ${response.substring(0, 500)}"
},
Response.ErrorListener { textView.text = "That didn't work!" })
queue.add(stringRequest)
Please check the logs. They should show you a security error because you use HTTP clear text traffic.
With the default settings, apps aren't allowed to transmit cleartest HTTP traffic.
You must either add an SSL certificate to your backend or allow HTTP traffic.
Check out this StackOverflow thread to see how to enable clear text web requests.

GoneException when calling post_to_connection on AWS lambda and API gateway

I want to send a message to a websocket client when it connects to the server on AWS lambda and API gateway. Currently, I use wscat as a client. Since the response 'connected' is not shown on the wscat console when I connect to the server, I added post_to_connection to send a message 'hello world' to the client. However, it raises GoneException.
An error occurred (GoneException) when calling the PostToConnection
operation
How can I solve this problem and send some message to wscat when connecting to the server?
My python code is below. I use Python 3.8.5.
import os
import boto3
import botocore
dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table(os.environ['TABLE_NAME'])
def lambda_handler(event, context):
domain_name = event.get('requestContext',{}).get('domainName')
stage = event.get('requestContext',{}).get('stage')
connection_id = event.get('requestContext',{}).get('connectionId')
result = connections.put_item(Item={ 'id': connection_id })
apigw_management = boto3.client('apigatewaymanagementapi',
endpoint_url=F"https://{domain_name}/{stage}")
ret = "hello world";
try:
_ = apigw_management.post_to_connection(ConnectionId=connection_id,
Data=ret)
except botocore.exceptions.ClientError as e:
print(e);
return { 'statusCode': 500,
'body': 'something went wrong' }
return { 'statusCode': 200,
"body": 'connected'};
Self-answer: you cannot post_to_connection to the connection itself in onconnect.
I have found that the GoneException can occur when the client that initiated the websocket has disconnected from the socket and its connectionId can no longer be found. Is there something causing the originating client to disconnect from the socket before it can receive your return message?
My use case is different but I am basically using a DB to check the state of a connection before replying to it, and not using the request context to do that. This error's appearance was reduced by writing connectionIds to DynamoDB on connect, and deleting them from the table upon disconnect events. Messaging now writes to connectionIds in the table instead of the id in the request context. Most messages go through but some errors are still emitted when the client leaves the socket but does not emit a proper disconnect event which leaves orphans in the table. The next step is to enforce item deletes when irregular disconnections occur. Involving a DB may be overkill for your situation, just sharing what helped me make progress on the GoneException error.
We need to post to connection after connecting (i.e. when the routeKey is not $connect)
routeKey = event.get('requestContext', {}).get('routeKey')
print(routeKey) # for debugging
if routeKey != '$connect': # if we have defined multiple route keys we can choose the right one here
apigw_management.post_to_connection(ConnectionId=connection_id, Data=ret)
#nemy's answer is totally true but it doesn't explain the reason. So, I just want to explain...
So, first of all What is GoneException or GoneError 410 ?
A 410 Gone error occurs when a user tries to access an asset which no longer exists on the requested server. In order for a request to return a 410 Gone status, the resource must also have no forwarding address and be considered to be gone permanently.
you can find more details about GoneException in this article.
In here, GoneException has occured; it means that the POST connection we are trying to make, doesn't exist - which fits perfectly in the scenario. Because we still haven't established the connection between Client and Server. The way APIGatewayWebsocketAPIs work is that you request an Endpoint(Route) and that Endpoint will invoke that Lambda Function (In our case it is ConnectionLambdaFunction for $connect Route).
Now, if The Lambda function resolves with statusCode: 200 then and only then the API Gateway will allow the connection to be established. So, basically untill we return statusCode: 200 from our Lambda Function we are not connected and untill then we are totally unknown to server and thats why the Post call that has been made before the return statement itself will throw an error.

How to store a message from the client in the server and send it further via some route in Flask

From a web application, this is making a request to the backend application (Python with Flask and flask-socketio). From this route on the backend, an emit should be done to a socketio client standalone application. This works fine, but when the client app sends back a message directly after, I want to retrieve this message and send it back in my route to the web application. The message I get back from the client via a callback will be asynchronous, so how in the simplest manner could this be achieved? Each time I fetch the message from the client, the route has already sent back a reply to the web app without the message.
I fully understand that this flow is usually not normal, but can this be achieved without saving this message into a database, but store it somewhere on the backend and send it back to the web app?
You can use an Event object from the Python standard library.
from threading import Event
my_event = Event()
In your Flask route:
my_event.wait() # block until the event is signaled
return socketio_response
In your Socket.IO callback function:
socketio_response = data
my_event.set() # alert the route that a result is now available

HTTP REST Gateway to AMQP Request-Response, Without Web Sockets Or Polling

I've struggled for two days to understand how REST API Gateways should return GET requests to browsers when the backend service runs on AMQP (without using Web Sockets or polling).
Have successfully RPC'ed betweeen AMQP service (with RabbitMqs reply_to & correlation_id), but with Flask HTTP request waiting I'm still lost.
gateway.py - Response Handler Inside The HTTP Handler, Times out
def products_get():
def handler(ch=None, method=None, properties=None, body=None):
if body:
return body
return False
return_queue = 'products.get.return'
broker.channel.queue_declare(return_queue)
broker.channel.basic_consume(handler, return_queue)
broker.publish(exchange='', routing_key='products.get', body='Request data', properties=pika.BasicProperties(reply_to=return_queue))
now = time.time() # for timeout. Not having this returns 'no content' immediately
while time.time() < now + 1:
if handler():
return handler()
return 'Time out'
POST/PUT can simply send the AMQP message, return 200/201/201 immediately and the service work at its own pace. A separate REST interface just for GET requests seems implausible, but don't know the other options.
Regards
I think what you're asking is "how to perform asynchronous GET requests". and I reckon that the answer is - you can't. and should not. its bad practice or bad design. and it does not scale.
Why are you trying to get your GET response payload from AMQP?
If the paylaod (the content of the response) can be pulled from some DB, just pull it from there. that's called a synchronous request.
If the payload must be processed in some backend, send it away and don't have the requester wait for a response. You could assign some ID and have the requester ask again later (or collect some callback URL from the requester and have your backend POST the response once its ready - less common design).
EDIT:
so, given that you have to work with AMQP-backed backend, I would do something a little more elaborate: spawn a thread or a process in your front end that would constantly consume from AMQP and store the results locally or in some db. and serve GET results based on data that you stored locally. if the data isn't yet available, just return 404. ideally you'll need to re-shape your API: split it into "post" requests (that would trigger work at the backend) and "get" requests (that would return the results if they're available).

Flask: asynchronous response to client

I'm using Flask to develop a web server in a python app. I'm achieving this scenario: the client (it won't be a browser) sends a request, the server does some long task in background and on completion sends the response back to the client asynchronously. Is it possible to do that?
What you ask cannot be done with the HTTP protocol. Each request receives a response synchronously. The closest thing to achieve what you want would be this:
The client sends the request and the server responds with a job id immediately, while it also starts a background task for this long calculation.
The client can then poll the server for status by sending the job id in a new request. The response is again immediate and contains a job status, such as "in progress", "completed", "failed", etc. The server can also return a progress percentage, which the client can use to render a progress bar.
You could also implement web sockets, but that will require socket enabled server and client.

Categories