-
Notifications
You must be signed in to change notification settings - Fork 31
Refactor into separate files, additional features, addition of missing typing #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor into separate files, additional features, addition of missing typing #25
Conversation
Changed name and added new attributes to HTTPRequest like method, path, query_params, http_version, headers and body
…utes to route_handlers
…at are not utf-8 decodable
Take a look at using |
Solved problem when data is sent in chunks and is not received in full. Bypassed ESP32 TCP buffer limit of 2880 bytes.
…ethod on HTTPStatus
I'm not able to get the revised |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple of questions. The only way I could complete a request from a browser was to use an arbitrary timeout, incurring a performance hit and assuming that whatever was received in that time was complete.
adafruit_httpserver/server.py
Outdated
while "Receiving data": | ||
try: | ||
length = conn.recv_into(self._buffer) | ||
received_data += self._buffer[:length] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the blocking case, we need a way to know when the request is complete?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Realized there is no blocking case currently allowed (timeout must be >=0, not None
), but perhaps there should be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that giving an option to set timeout to None
(blocking) should be possible, thus not liming if one wants to do so.
It might also be logical to increase default timeout from 0
to 0.5
or 1
that would allow receiving request without explicitly setting timeout using server.socket_timeout
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest I do not really see any other way of determining whether it is the end of mesage other than timeout, I do not like that it unnecessarily waits after last part of message is received but it is the only way that seems to work no matter which client wants to connect to server. But, compared to current main
branch, each request takes at least 2x longer.
One positive here is that it makes it possible to send data longer that max buffer size of 2880 bytes, which makes it more universal.
It is hard to find a middle point here, and we have to remember that we are talking about microcontrollers, not load-balanced web servers, so maybe a longer delay is not as much of a problem as the buffer size limit.
adafruit_httpserver/server.py
Outdated
try: | ||
length = conn.recv_into(self._buffer) | ||
received_data += self._buffer[:length] | ||
except OSError as ex: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the non-blocking (no timeout) case, we need to handle EAGAIN here? And again I think we'll hit the question of how to determine when a request is complete.
And perhaps defensively add a raise ex
to catch-all any other OSError
s.
After more testing, it seems to me that the most reliable way of solving the socket blocking problem is making it possible only to set it to positive number, no I know it is not perfect and has an impact on loading speed, but I don't see any other way around it. Even if library api enabled setting Maybe there is other solution that I am missing, I do not completely understand all the quirks of TCP sockets and blocking behaviour. But from testing this way seems most reasonable to me and the impact itself is not that noticeable. I would leave it at only |
What is different about the way HTTPServer is operating compared to, say, the simple CPython HTTP server, or Apache, nginx, etc. Do we need to fix something underlying? |
As I mentioned above, I am not very familiar with TCP communication on low-level, but I found that a client should send a Also when analysing CPython Maybe that is the problem, but it is hard for me to say, as it is very low-level. |
The issue with a timeout, aside from just the dead time, is that it can trip up async by extending the minimum overall async loop time. It's also a guess how long to make the timeout to fully capture any given request, so the chosen timeout will have to err on the long side. Someone do please correct me if I'm wrong, but my understanding is that HTTP/1.1 adds some protocol assumptions over simple TCP: if a request has a message body it will either have a Does the client closing not trigger a If we can't detect the client closing, we can still have a timeout applied on top of handling addendum 1: Apache seems to handle the blocking + no-content-length + no-transfer-encoding case (full code here): s.settimeout(None)
s.connect((HOST, PORT))
size = s.send(f"GET {PATH} HTTP/1.1\r\nHost: {HOST}:{PORT}\r\n\r\n".encode())
print("Sent", size, "bytes")
buf = bytearray(MAXBUF)
size = s.recv_into(buf)
print('Received', size, "bytes", buf[:size]) gets an immediate response from the server with the headers and content. Interestingly, adendum 2: I know we are looking for some happy medium here, I'm not sure what it is... performance, buffer size, protocol conformance, etc. Hard to know what aspects will be most expected by the community. In general, this PR is a big step up in functionality and we could push some of this to future exploration. |
Finally I was able to get the expected result. As @anecdata suggested I used I adjusted default I also changed how Right now there is no support for To be honest I am quite happy with current state of this PR, and I don't really see much that can be changed now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested this with the reactle game from https://learn.adafruit.com/wordle-personal-esp32-s2-web-server, on Firefox and Chrome. It worked fine.
@michalpokusa We really appreciate your work on this!
@anecdata Do you want to test further? I will merge after hearing if you if you think this is good to go.
I will release this as 1.0.0 since there are API changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks really good. Tested with and without content-length
client header.
Thanks, @michalpokusa!
Thank you @michalpokusa ! |
Great job @michalpokusa! Glad to see it was merged. |
Thank you! 😊 |
Updating https://github.com/adafruit/Adafruit_CircuitPython_HTTPServer to 1.0.0 from 0.5.4: > Merge pull request adafruit/Adafruit_CircuitPython_HTTPServer#25 from michalpokusa/refactor-and-additional-features
This PR, other that separating adafruit_httpserver into multiple files #8 adds following functionality:
HTTPRequest
:http_version
extracted from Start line, e.g."HTTP/1.1"
query_params
extracted frompath
, e.g./path?foo=bar
is parsed to{"foo": "bar"}
headers
as adict
, e.g.{'user-agent': '...', 'host': 'esp32-s2-tft.local', 'connection': 'close'}
body
asbytes
, so both text content and images can be processed Add support for reading contents of POST requests #16HTTPResponse
which allows to set it'sheaders
Allow setting of HTTP headers in HTTP response #24Other than that, this PR also includes:
HTTPMethod
andMIMEType
Removed blocking socketImplementedsocket_timeout
property, which fixes Server blocks when a connection is opened with no request sent. #21status
in response cannot be a tuple #6Despite major refactor, the usage of library stays nearly unchanged, so it won't be neccessary to rewrite existing projects from scratch, only minor changes might be required.
It surely is not perfect, but I belive it is a step in the right direction.
Closes #2
Closes #6
Closes #8
Closes #16
Closes #17
Closes #21
Closes #24