I'm building a simple IOT device (with a Raspberry Pi Zero) which pulls data from Firebase Realtime Database every 1 second and checks for updates.
However, after a certain time (not sure exactly how much but somewhere between 1 hour and 3 hours) the program exits with a 504 Server Error: Gateway Time-out message.
I couldn't understand exactly why this is happening, I tried to recreate this error by disconnecting the Pi from the internet and I did not get this message. Instead, the program simply paused in a ref.get() line and automatically resumed running once the connection was back.
This device is meant to be always on, so ideally if I get some kind of error, I would like to restart the program / reinitiate the connection / reboot the Pi. Is there a way to achieve something like this?
It seems like the message is actually generated by the firebase_admin package.
Here is the error message:
Traceback (most recent call last):
File "/home/pi/.local/lib/python3.7/site-packages/firebase_admin/db.py", line 944, in request
return super(_Client, self).request(method, url, **kwargs)
File "/home/pi/.local/lib/python3.7/site-packages/firebase_admin/_http_client.py", line 105, in request
resp.raise_for_status()
File "/usr/lib/python3/dist-packages/requests/models.py", line 940, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 504 Server Error: Gateway Time-out for url: https://someFirebaseProject.firebaseio.com/someRef/subSomeRef/payload.json
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/pi/Desktop/project/main.py", line 94, in <module>
lastUpdate = ref.get()['lastUpdate']
File "/home/pi/.local/lib/python3.7/site-packages/firebase_admin/db.py", line 223, in get
return self._client.body('get', self._add_suffix(), params=params)
File "/home/pi/.local/lib/python3.7/site-packages/firebase_admin/_http_client.py", line 117, in body
resp = self.request(method, url, **kwargs)
File "/home/pi/.local/lib/python3.7/site-packages/firebase_admin/db.py", line 946, in request
raise _Client.handle_rtdb_error(error)
firebase_admin.exceptions.UnknownError: Internal server error.
>>>
To reboot the whole Raspberry Pi, you can just run a shell command:
import os
os.system("sudo reboot")
I've had this problem too and usually feel safer with that, but there's obvious downsides. I'd try resetting the wifi connection or network interface in a similar way
Related
I am getting this error:
werkzeug.exceptions.ClientDisconnected: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
Full stack below. Happening with Python 3.10.5 on an Ubuntu 18.04 VM. Using Werkzeug 2.1.2.
Any idea of the cause?
No problem on Windows 10 or an Ubuntu 18.04 NUC. Works fine on Ubuntu 18.04 VM with Python 3.6.5 and Werkzeug 1.0.1.
Traceback (most recent call last):
File "/usr/local/lib/python3.10/site-packages/werkzeug/wsgi.py", line 921, in read
read = self._read(to_read)
ValueError: read of closed file
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/local/lib/python3.10/site-packages/werkzeug/formparser.py", line 140, in wrapper
return f(self, stream, *args, **kwargs)
File "/usr/local/lib/python3.10/site-packages/werkzeug/formparser.py", line 290, in _parse_multipart
form, files = parser.parse(stream, boundary, content_length)
File "/usr/local/lib/python3.10/site-packages/werkzeug/formparser.py", line 418, in parse
for data in iterator:
File "/usr/local/lib/python3.10/site-packages/werkzeug/wsgi.py", line 653, in _make_chunk_iter
item = _read(buffer_size)
File "/usr/local/lib/python3.10/site-packages/werkzeug/wsgi.py", line 923, in read
return self.on_disconnect()
File "/usr/local/lib/python3.10/site-packages/werkzeug/wsgi.py", line 893, in on_disconnect
raise ClientDisconnected()
werkzeug.exceptions.ClientDisconnected: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
It's just as it states, the client disconnected. The reason? could be several, they could have had a network problem, they could have clicked a different link, they could have navigated away. either way, the server could no longer talk to your client and finish the transfer of data.
It's the most annoying error in the world, you can't do anything about it.
I'm using the Firebase Realtime Database listener to listen to changes on a database path.
My program recently crashed because of the following 503 error that seems to be raised by the underlying requests library:
Traceback (most recent call last):
File "/usr/local/lib/python3.7/threading.py", line 917, in _bootstrap_inner
self.run()
File "/usr/local/lib/python3.7/threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "/usr/local/lib/python3.7/site-packages/firebase_admin/db.py", line 123, in _start_listen
for sse_event in self._sse:
File "/usr/local/lib/python3.7/site-packages/firebase_admin/_sseclient.py", line 128, in __next__
self._connect()
File "/usr/local/lib/python3.7/site-packages/firebase_admin/_sseclient.py", line 112, in _connect
self.resp.raise_for_status()
File "/usr/local/lib/python3.7/site-packages/requests/models.py", line 940, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 503 Server Error: Service Unavailable for url: https://database_url...
My listener initialization is wrapped in a try statement, so I'm unsure why this wasn't caught, swallowed and retried as I expected it to:
def init_listener():
try:
listener = firebase_admin.db.reference(db_path).listen(handle_change)
except Exception as e:
time.sleep(1) # Retry in one second.
init_listener()
I'd like to handle future 503 errors, but I'm not sure how to go about doing this.
Additionally, I'm using except Exception as e above for demo/debugging purposes, but I'm also not sure if requests.exceptions.HTTPError will be specific enough to catch only 500 errors (though I don't know what other errors can be raised).
From the firebase_admin reference docs:
This API is based on the event streaming support available in the
Firebase REST API. Each call to listen() starts a new HTTP connection
and a background thread. This is an experimental feature.
The key here is that this all runs in a background thread. Therefore, wrapping the call to listen() in a try/except will not catch exceptions thrown in the thread. There is no simple way to catch the exceptions happening in the background thread.
To solve your issue, you will probably need to know more about why the database is returning an HTTP 503 status. Or you will need to switch to some other firebase_admin API that will allow you to catch and ignore these exceptions.
I have a webservice running in python 2.7.10 / Tornado that uses SSL. This service throws an error when a non-SSL call comes through (http://...).
I don't want my service to be accessible when SSL is not used, but I'd like to handle it in a cleaner fashion.
Here is my main code that works great over SSL:
if __name__ == "__main__":
tornado.options.parse_command_line()
#does not work on 2.7.6
ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_ctx.load_cert_chain("...crt.pem","...key.pem")
ssl_ctx.load_verify_locations("...CA.crt.pem")
http_server = tornado.httpserver.HTTPServer(application, ssl_options=ssl_ctx, decompress_request=True)
http_server.listen(options.port)
mainloop = tornado.ioloop.IOLoop.instance()
print("Main Server started on port XXXX")
mainloop.start()
and here is the error when I hit that server with http://... instead of https://...:
[E 151027 20:45:57 http1connection:700] Uncaught exception
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/tornado/http1connection.py", line 691, in _server_request_loop
ret = yield conn.read_response(request_delegate)
File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 807, in run
value = future.result()
File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 209, in result
raise_exc_info(self._exc_info)
File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 810, in run
yielded = self.gen.throw(*sys.exc_info())
File "/usr/local/lib/python2.7/dist-packages/tornado/http1connection.py", line 166, in _read_message
quiet_exceptions=iostream.StreamClosedError)
File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 807, in run
value = future.result()
File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 209, in result
raise_exc_info(self._exc_info)
File "<string>", line 3, in raise_exc_info
SSLError: [SSL: HTTP_REQUEST] http request (_ssl.c:590)
Any ideas how I should handle that exception?
And what the standard-conform return value would be when I catch a non-SSL call to an SSL-only API?
UPDATE
This API runs on a specific port e.g. https://example.com:1234/. I want to inform a user who is trying to connect without SSL, e.g. http://example.com:1234/ that what they are doing is incorrect by returning an error message or status code. As it is the uncaught exception returns a 500, which they could interpret as a programming error on my part. Any ideas?
There's an excelent discussion in this Tornado issue about that, where Tornado maintainer says:
If you have both HTTP and HTTPS in the same tornado process, you must be running two separate HTTPServers (of course such a feature should not be tied to whether SSL is handled at the tornado level, since you could be terminating SSL in a proxy, but since your question stipulated that SSL was enabled in tornado let's focus on this case first). You could simply give the HTTP server a different Application, one that just does this redirect.
So, the best solution it's to HTTPServer that listens on port 80 and doesn't has the ssl_options parameter setted.
UPDATE
A request to https://example.com/some/path will go to port 443, where you must have an HTTPServer configured to handle https traffic; while a request to http://example.com/some/path will go to port 80, where you must have another instance of HTTPServer without ssl options, and this is where you must return the custom response code you want. That shouldn't raise any error.
Since yesterday a working Python gdata program has stopped working after I changed the IP address used.
I receive the following stack trace:
Traceback (most recent call last):
File "C:\prod\googleSite\googleSite2.py", line 23, in
feed = client.GetContentFeed()
File "C:\Python27\lib\site-packages\gdata\sites\client.py", line 155, in get_c
ontent_feed
auth_token=auth_token, **kwargs)
File "C:\Python27\lib\site-packages\gdata\client.py", line 635, in get_feed
**kwargs)
File "C:\Python27\lib\site-packages\gdata\client.py", line 320, in request
RequestError)
gdata.client.RequestError: Server responded with: 500, Internal Error
The code is as follow:
import gdata.sites.client
import gdata.sites.data
client = gdata.sites.client.SitesClient(source='xxx', site='yyy')
client.ssl = True # Force API requests through HTTPS
client.ClientLogin('user#googlemail.com', 'password', client.source);
feed = client.GetContentFeed();
Update:
The issue fixes itself after an hour - is there any kind of commit or logout to avoid this?
Since you're not passing anything in GetContentFeed, it's using CONTENT_FEED_TEMPLATE % (self.domain, self.site) as the URI. I'm not sure if the IP change had an impact on what the self.domain/self.site values should be, but it might be worth checking those out.
I am trying to use the Google Wave Active Robot API fetch_wavelet() and I get an HTTP 502 error
example:
from waveapi import robot
import passwords
robot = robot.Robot('gae-run', 'http://images.com/fake-image.jpg')
robot.setup_oauth(passwords.CONSUMER_KEY, passwords.CONSUMER_SECRET, server_rpc_base='http://www-opensocial.googleusercontent.com/api/rpc')
wavelet = robot.fetch_wavelet('googlewave.com!w+dtuZi6t3C','googlewave.com!conv+root')
robot.submit(wavelet)
self.response.out.write(wavelet.creator)
But the error I get is this:
Traceback (most recent call last):
File "/base/python_runtime/python_lib/versions/1/google/appengine/ext/webapp/__init__.py", line 511, in __call__
handler.get(*groups)
File "/base/data/home/apps/clstff/gae-run.342467577023864664/main.py", line 23, in get
robot.submit(wavelet)
File "/base/data/home/apps/clstff/gae-run.342467577023864664/waveapi/robot.py", line 486, in submit
res = self.make_rpc(pending)
File "/base/data/home/apps/clstff/gae-run.342467577023864664/waveapi/robot.py", line 251, in make_rpc
raise IOError('HttpError ' + str(code))
IOError: HttpError 502
Any ideas?
Edit:
When clstff#appspot.com is not a member of the wave I get the correct error message
Error: RPC Error500: internalError: clstff#appspot.com is not a participant of wave id: [WaveId:googlewave.com!w+Pq1HgvssD] wavelet id: [WaveletId:googlewave.com!conv+root]. Unable to apply operation: {'method':'robot.fetchWave','id':'655720','waveId':'googlewave.com!w+Pq1HgvssD','waveletId':'googlewave.com!conv+root','blipId':'null','parameters':{}}
But when clstff#appsot.com is a member of the wave I get the http 502 error.
IOError: HttpError 502
Joe Gregorio answered my question on the Google Wave API Google group
Did you make any changes to the
wavelet before submitting it? I think
there was an old bug where sending in
an empty change would cause a 502,
this might be a regression in that
behavior.
If I removed the robot.submit(wavelet) line, it worked!