Skip to content

Commit 647ec73

Browse files
committed
Prevent timeout from occuring while a request is being sent.
Resume the timeout once sent and while waiting for a header. Add a completion callback to AsyncRequestBody. Change-Id: I3995452a5d21707ef217fb7478caee6569038be7
1 parent 1ee5343 commit 647ec73

File tree

9 files changed

+68
-21
lines changed

9 files changed

+68
-21
lines changed

AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpClient.java

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public Future<AsyncHttpResponse> execute(final AsyncHttpRequest request, final H
8181
private class FutureAsyncHttpResponse extends SimpleFuture<AsyncHttpResponse> {
8282
public AsyncSocket socket;
8383
public Object scheduled;
84+
public Runnable timeoutRunnable;
8485

8586
@Override
8687
public boolean cancel() {
@@ -135,6 +136,10 @@ public void run() {
135136
}
136137
}
137138

139+
private static long getTimeoutRemaining(AsyncHttpRequest request) {
140+
return Math.max(System.currentTimeMillis() - request.executionTime + request.getTimeout(), 1);
141+
}
142+
138143
private void executeAffinity(final AsyncHttpRequest request, final int redirectCount, final FutureAsyncHttpResponse cancel, final HttpConnectCallback callback) {
139144
assert mServer.isAffinityThread();
140145
if (redirectCount > 5) {
@@ -148,9 +153,19 @@ private void executeAffinity(final AsyncHttpRequest request, final int redirectC
148153

149154
request.logd("Executing request.");
150155

151-
final Object scheduled;
156+
// flow:
157+
// 1) set a connect timeout
158+
// 2) wait for connect
159+
// 3) on connect, cancel timeout
160+
// 4) wait for request to be sent fully
161+
// 5) after request is sent, set a header timeout
162+
// 6) wait for headers
163+
// 7) on headers, cancel timeout
164+
// 8) TODO: response can take as long as it wants to arrive?
165+
152166
if (request.getTimeout() > 0) {
153-
cancel.scheduled = scheduled = mServer.postDelayed(new Runnable() {
167+
// set connect timeout
168+
cancel.timeoutRunnable = new Runnable() {
154169
@Override
155170
public void run() {
156171
// we've timed out, kill the connections
@@ -161,12 +176,11 @@ public void run() {
161176
}
162177
reportConnectedCompleted(cancel, new TimeoutException(), null, request, callback);
163178
}
164-
}, request.getTimeout());
165-
}
166-
else {
167-
scheduled = null;
179+
};
180+
cancel.scheduled = mServer.postDelayed(cancel.timeoutRunnable, getTimeoutRemaining(request));
168181
}
169182

183+
// 2) wait for a connect
170184
data.connectCallback = new ConnectCallback() {
171185
@Override
172186
public void onConnectCompleted(Exception ex, AsyncSocket socket) {
@@ -176,6 +190,10 @@ public void onConnectCompleted(Exception ex, AsyncSocket socket) {
176190
return;
177191
}
178192

193+
// 3) on connect, cancel timeout
194+
if (cancel.timeoutRunnable != null)
195+
mServer.removeAllCallbacks(cancel.scheduled);
196+
179197
data.socket = socket;
180198
for (AsyncHttpClientMiddleware middleware: mMiddleware) {
181199
middleware.onSocket(data);
@@ -188,7 +206,21 @@ public void onConnectCompleted(Exception ex, AsyncSocket socket) {
188206
return;
189207
}
190208

209+
// 4) wait for request to be sent fully
210+
// and
211+
// 6) wait for headers
191212
final AsyncHttpResponseImpl ret = new AsyncHttpResponseImpl(request) {
213+
@Override
214+
protected void onRequestCompleted(Exception ex) {
215+
if (cancel.isCancelled())
216+
return;
217+
// 5) after request is sent, set a header timeout
218+
if (cancel.timeoutRunnable != null && data.headers == null) {
219+
mServer.removeAllCallbacks(cancel.scheduled);
220+
cancel.scheduled = mServer.postDelayed(cancel.timeoutRunnable, getTimeoutRemaining(request));
221+
}
222+
}
223+
192224
@Override
193225
public void setDataEmitter(DataEmitter emitter) {
194226
data.bodyEmitter = emitter;
@@ -228,8 +260,9 @@ protected void onHeadersReceived() {
228260
if (cancel.isCancelled())
229261
return;
230262

231-
if (scheduled != null)
232-
mServer.removeAllCallbacks(scheduled);
263+
// 7) on headers, cancel timeout
264+
if (cancel.timeoutRunnable != null)
265+
mServer.removeAllCallbacks(cancel.scheduled);
233266

234267
// allow the middleware to massage the headers before the body is decoded
235268
request.logv("Received headers: " + mHeaders.getHeaders().toHeaderString());

AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpRequestBody.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import com.koushikdutta.async.callback.CompletedCallback;
66

77
public interface AsyncHttpRequestBody<T> {
8-
public void write(AsyncHttpRequest request, DataSink sink);
8+
public void write(AsyncHttpRequest request, DataSink sink, CompletedCallback completed);
99
public void parse(DataEmitter emitter, CompletedCallback completed);
1010
public String getContentType();
1111
public boolean readFullyOnRequest();

AndroidAsync/src/com/koushikdutta/async/http/AsyncHttpResponseImpl.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,27 @@ public void onCompleted(Exception ex) {
5151
com.koushikdutta.async.Util.writeAll(exchange, rs.getBytes(), new CompletedCallback() {
5252
@Override
5353
public void onCompleted(Exception ex) {
54-
if (mWriter != null)
55-
mWriter.write(mRequest, AsyncHttpResponseImpl.this);
54+
if (mWriter != null) {
55+
mWriter.write(mRequest, AsyncHttpResponseImpl.this, new CompletedCallback() {
56+
@Override
57+
public void onCompleted(Exception ex) {
58+
onRequestCompleted(ex);
59+
}
60+
});
61+
}
62+
else {
63+
onRequestCompleted(null);
64+
}
5665
}
5766
});
5867

5968
LineEmitter liner = new LineEmitter();
6069
exchange.setDataCallback(liner);
6170
liner.setLineCallback(mHeaderCallback);
6271
}
72+
73+
protected void onRequestCompleted(Exception ex) {
74+
}
6375

6476
private CompletedCallback mReporter = new CompletedCallback() {
6577
@Override
@@ -161,6 +173,7 @@ public void write(ByteBufferList bb) {
161173

162174
@Override
163175
public void end() {
176+
164177
write(ByteBuffer.wrap(new byte[0]));
165178
}
166179

AndroidAsync/src/com/koushikdutta/async/http/JSONArrayBody.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ public void onCompleted(Exception e, JSONArray result) {
3232
}
3333

3434
@Override
35-
public void write(AsyncHttpRequest request, DataSink sink) {
36-
Util.writeAll(sink, mBodyBytes, null);
35+
public void write(AsyncHttpRequest request, DataSink sink, final CompletedCallback completed) {
36+
Util.writeAll(sink, mBodyBytes, completed);
3737
}
3838

3939
@Override

AndroidAsync/src/com/koushikdutta/async/http/JSONObjectBody.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ public void onCompleted(Exception e, JSONObject result) {
3232
}
3333

3434
@Override
35-
public void write(AsyncHttpRequest request, DataSink sink) {
36-
Util.writeAll(sink, mBodyBytes, null);
35+
public void write(AsyncHttpRequest request, DataSink sink, final CompletedCallback completed) {
36+
Util.writeAll(sink, mBodyBytes, completed);
3737
}
3838

3939
@Override

AndroidAsync/src/com/koushikdutta/async/http/MultipartFormDataBody.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public MultipartCallback getMultipartCallback() {
122122

123123
int written;
124124
@Override
125-
public void write(AsyncHttpRequest request, final DataSink sink) {
125+
public void write(AsyncHttpRequest request, final DataSink sink, final CompletedCallback completed) {
126126
if (mParts == null) {
127127
sink.end();
128128
return;
@@ -131,6 +131,7 @@ public void write(AsyncHttpRequest request, final DataSink sink) {
131131
Continuation c = new Continuation(new CompletedCallback() {
132132
@Override
133133
public void onCompleted(Exception ex) {
134+
completed.onCompleted(ex);
134135
// if (ex == null)
135136
// sink.end();
136137
// else

AndroidAsync/src/com/koushikdutta/async/http/StringBody.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ public void onCompleted(Exception e, String result) {
3232
public static final String CONTENT_TYPE = "text/plain";
3333

3434
@Override
35-
public void write(AsyncHttpRequest request, DataSink sink) {
35+
public void write(AsyncHttpRequest request, DataSink sink, final CompletedCallback completed) {
3636
if (mBodyBytes == null)
3737
mBodyBytes = string.getBytes();
38-
Util.writeAll(sink, mBodyBytes, null);
38+
Util.writeAll(sink, mBodyBytes, completed);
3939
}
4040

4141
@Override

AndroidAsync/src/com/koushikdutta/async/http/UrlEncodedFormBody.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ private void buildData() {
4545
}
4646

4747
@Override
48-
public void write(AsyncHttpRequest request, final DataSink response) {
48+
public void write(AsyncHttpRequest request, final DataSink response, final CompletedCallback completed) {
4949
if (mBodyBytes == null)
5050
buildData();
51-
Util.writeAll(response, mBodyBytes, null);
51+
Util.writeAll(response, mBodyBytes, completed);
5252
}
5353

5454
public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";

AndroidAsync/src/com/koushikdutta/async/http/server/UnknownRequestBody.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public UnknownRequestBody(String contentType) {
1313
}
1414

1515
@Override
16-
public void write(AsyncHttpRequest request, DataSink sink) {
16+
public void write(AsyncHttpRequest request, DataSink sink, final CompletedCallback completed) {
1717
assert false;
1818
}
1919

0 commit comments

Comments
 (0)