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
[TIMOB-20483] Fix HTTPClient.onsendstream progress #8522
Conversation
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 think we need to try and use setFixedLengthStreamingMode, but call it later in the lifecycle, like in the ConnectionRunnable. We also need some tests to verify whether this breaks POST/PUT against URLs requiring basic auth or having at least one redirect.
client.setRequestMethod(method); | ||
client.setDoInput(true); | ||
|
||
if (isPostOrPutOrPatch) { | ||
client.setDoOutput(true); | ||
client.setChunkedStreamingMode(0); |
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.
Some notes:
- This method is explicitly noted in the docs as to be used when we don't know the content length. But I think we always do know the content length (stored as totalLength in the connectionRunnable class?). When the length is known, they urge you to use #setFixedLengthStreamingMode(long) https://developer.android.com/reference/java/net/HttpURLConnection.html#setFixedLengthStreamingMode(long)
- Secondly, when either of these are used, we avoid buffering which improves performance, but both have an important note in the docs:
When output streaming is enabled, authentication and redirection cannot be handled automatically. A HttpRetryException will be thrown when reading the response if authentication or redirection are required. This exception can be queried for the details of the error.
As such, I think this change will actually break requests done against URLs that have either redirects or ask for basic auth. It'd be very useful to add some unit tests that cover these scenarios to verify.
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 did initially want to use setFixedLengthStreamingMode
but totalLength
isn't the true length of the content that gets sent. The data gets manipulated into a form, which causes length issues when using setFixedLengthStreamingMode
.
I did test against HTTPS and here's one with a redirect that works too:
var http = Ti.Network.createHTTPClient({
onload: function() {
Ti.API.info('response: ' + this.responseText);
}
});
http.open('POST', 'https://jigsaw.w3.org/HTTP/300/301.html');
http.send();
However, I'll look into correcting totalLength and use setFixedLengthStreamingMode
0a440ce
to
ca41f13
Compare
@sgtcoolguy Updated PR |
@garymathews Looks like two httpclient unit tests failed. |
Looks to me like you're missing a few of the parts to add up the total length. The original code is nasty, so it's hard to point out. But in ConnectionRunnable.run() you set the contentLength to the size of the form. I don't think that's actually right, because below that you'll see with multipart there could be several parts and a form. if (parts.size() > 0 && needMultipart) {
contentLength = 0;
for(ContentBody body : parts.values()) {
contentLength += body.getContentLength();
}
if (form != null) {
contentLength += form.getContentLength();
}
} else {
// This code may get ugly. But basically you have to check if raw data was String, Entity, or fall back to form, see #handleURLEncodedData
if (data instanceof String) {
contentLength = ((String) data).length(); // This is probably wrong. We turn it into a StringEntity later and can get content length from that.
} else if (data instanceof FileEntity) { // As far as I can tell, this is the only other valid type that raw data gets converted to
contentLength = ((FileEntity) data).getContentLength();
} else {
contentLength = form.getContentLength();
}
} |
@sgtcoolguy Another problem is, when calls such as |
@garymathews that's true, but that happens later in #run(). The chunk of code I gave above at the top of #run() should work OK. I know it's not the most pleasant to iterate over the parts/form at top just to get length and then again to actually set up the connection contents, but it should work. |
7f59557
to
4e5638c
Compare
@sgtcoolguy Updated PR, let's see if the tests pass! |
4e5638c
to
bf6a608
Compare
test this please |
setUseCaches(true)
TEST CASE #1
TEST CASE #2
TEST CASE #3
NOTE: Also needs testing with TIMOB-23852
JIRA Ticket