Skip to content

HTTPClient: implement Stream class #6977

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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

francescolavra
Copy link

@francescolavra francescolavra commented Jan 2, 2020

It is now possible to stream HTTP request data without knowing in advance the data size (using chunked transfer encoding), and to stream HTTP response data (correctly decoded according to the transfer encoding method used by the server) without requiring an output Stream implementation.
(The existing getStream() method in the HTTPClient class returns the underlying WiFiClient stream which is not aware of the HTTP transfer encoding.)

When using chunked encoding in an HTTP request, each call to write() results in the transmission of a separate chunk. An example code snippet is below:

addHeader("Content-Type", "plain/text");
addHeader("Transfer-Encoding", "chunked");
sendHeader("POST");
print("first chunk");
print("second chunk");
endRequest();

A primary use case for using the Stream implementation to receive an HTTP response is when parsing large response data: using the HTTPClient Stream implementation, the parser does not require an in-memory buffer where the entire HTTP response data is stored.

@JAndrassy
Copy link
Contributor

JAndrassy commented Jan 2, 2020

a POST example with print and println could demonstrate the sending part.
(is it possible to send chunked?)

@francescolavra
Copy link
Author

No, currently the HTTPClient supports only receiving with chunked encoding, and doesn't implement the sending part; that was the case also without this Stream implementation.

@JAndrassy
Copy link
Contributor

JAndrassy commented Jan 3, 2020

No, currently the HTTPClient supports only receiving with chunked encoding, and doesn't implement the sending part; that was the case also without this Stream implementation.

but with this PR HTTPClient has println and it can't be used, because the payload can be send only at once with get, post functions

@francescolavra
Copy link
Author

but now HTTPClient has println and it can't be used, because the payload can be send only at once with get, post functions

Actually it's not true that the payload can be sent only at once: you can start an HTTP request via sendHeader() and then send the payload separately. Before, you could send the payload only by getting the underlying WiFiClient stream, via getStream(); now you can also stream directly to the HTTPClient.
So yes, println can be used (and it could be used also without this Stream implementation).

@JAndrassy
Copy link
Contributor

but now HTTPClient has println and it can't be used, because the payload can be send only at once with get, post functions

Actually it's not true that the payload can be sent only at once: you can start an HTTP request via sendHeader() and then send the payload separately. Before, you could send the payload only by getting the underlying WiFiClient stream, via getStream(); now you can also stream directly to the HTTPClient.
So yes, println can be used (and it could be used also without this Stream implementation).

ok. sorry. I didn't have an opportunity to use the ESP8266HTTPClient. But to send a POST request the Content-length header should be set or chunked transfer encoding should be used. And providing the Stream to a function which composes the request body usually means that we don't know the content-length before.
One could use my StreamLib library's ChunkedPrint, but it would be better if the ESP8266HTTPClient would support chunked POST body.

These new methods will be reused in the next commit when an
implementation of the Stream class will be added to the HTTPClient
class.
HTTPClient::writeToStream() is being refactored to use the new
methods.
It is now possible to stream HTTP request data without knowing in
advance the data size (using chunked transfer encoding), and to
stream HTTP response data (correctly decoded according to the
transfer encoding method used by the server) without requiring an
output Stream implementation.
(The existing getStream() method in the HTTPClient class returns
the underlying WiFiClient stream which is not aware of the HTTP
transfer encoding.)
When using chunked encoding in an HTTP request, each call to
write() results in the transmission of a separate chunk.

The setting of _transferEncoding to HTTPC_TE_IDENTITY in the
handleHeaderResponse() method has been removed because
_transferEncoding is now used for both transmission and reception,
and is reset to HTTPC_TE_IDENTITY in the clear() method.
@francescolavra
Copy link
Author

ok. sorry. I didn't have an opportunity to use the ESP8266HTTPClient. But to send a POST request the Content-length header should be set or chunked transfer encoding should be used. And providing the Stream to a function which composes the request body usually means that we don't know the content-length before.
One could use my StreamLib library's ChunkedPrint, but it would be better if the ESP8266HTTPClient would support chunked POST body.

OK, I added chunked encoding support in HTTP request transmission as well.

@earlephilhower earlephilhower added this to the 3.0.0 milestone Aug 18, 2020
@earlephilhower earlephilhower added the merge-conflict PR has a merge conflict that needs manual correction label Nov 5, 2020
@d-a-v d-a-v modified the milestones: 3.0.0, 3.0.1 Mar 31, 2021
@d-a-v d-a-v modified the milestones: 3.0.1, 3.1 Jun 16, 2021
@d-a-v d-a-v removed the merge-conflict PR has a merge conflict that needs manual correction label Jun 12, 2022
@d-a-v
Copy link
Collaborator

d-a-v commented Jun 13, 2022

This proposal

  • makes the http client a stream class
    • read() is implemented but not read(buf,len)
  • allows chunk encoding for both receiving and sending data
  • initial sendRequest() is not aware of chunk encoding (maybe ok ?)
  • has no example / test / doc

Legacy behaviour seems to be OK.
For a better understanding, can someone tell about a use case, or an implicit use case where this addition is useful ?

@francescolavra
Copy link
Author

An example where this addition would be useful is at https://github.com/francescolavra/arduino-iota-client/blob/f6ce76453cfa1984df14b3be9b6de478a44e40b6/src/IotaClient.cpp#L355, where we need to parse a JSON string coming from an HTTP response: the existing code calls the getStream() method of the HTTPClient class, but this method returns the underlying WiFiClient stream which is not aware of the HTTP transfer encoding, so the parsing will fail if the HTTP server uses chunked encoding in its response; with this addition, the above code can be changed to pass _client instead of _client.getStream() as input stream argument to the deserializeJson() method, and it will work for both non-chunked and chunked encoding.

Copy link
Collaborator

@d-a-v d-a-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@d-a-v d-a-v self-requested a review June 27, 2022 21:16
return -1;
}
return _client->peek();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

peek() is using available() to manage chunk-encoded streams.
Can read() do the same instead of duplicating code ?

Copy link
Author

@francescolavra francescolavra Aug 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think something like below would work for read():

while (!available()) {
    if (!connected()) {
        return -1;
    }
    yield();
}
if (_transferEncoding == HTTPC_TE_CHUNKED) {
    _chunkOffset++;
}
return _client->read();

But I'm afraid I don't have time to dig up my development environment and an ESP8266 to test this code.

@d-a-v d-a-v self-requested a review June 27, 2022 21:22
@d-a-v d-a-v added the alpha included in alpha release label Dec 16, 2022
@d-a-v d-a-v modified the milestones: 3.1, 4.0.0 Dec 16, 2022
@astrolemonade
Copy link

Hey! What changes should be done to finally merge these?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants