use of com.koushikdutta.async.AsyncSocket in project AndroidAsync by koush.
the class AsyncSocketMiddleware method getSocket.
@Override
public Cancellable getSocket(final GetSocketData data) {
final Uri uri = data.request.getUri();
final int port = getSchemePort(data.request.getUri());
if (port == -1) {
return null;
}
data.state.put("socket-owner", this);
final String lookup = computeLookup(uri, port, data.request.getProxyHost(), data.request.getProxyPort());
ConnectionInfo info = getOrCreateConnectionInfo(lookup);
synchronized (AsyncSocketMiddleware.this) {
if (info.openCount >= maxConnectionCount) {
// wait for a connection queue to free up
SimpleCancellable queueCancel = new SimpleCancellable();
info.queue.add(data);
return queueCancel;
}
info.openCount++;
while (!info.sockets.isEmpty()) {
IdleSocketHolder idleSocketHolder = info.sockets.pop();
final AsyncSocket socket = idleSocketHolder.socket;
if (idleSocketHolder.idleTime + idleTimeoutMs < System.currentTimeMillis()) {
socket.setClosedCallback(null);
socket.close();
continue;
}
if (!socket.isOpen())
continue;
data.request.logd("Reusing keep-alive socket");
data.connectCallback.onConnectCompleted(null, socket);
// just a noop/dummy, as this can't actually be cancelled.
SimpleCancellable ret = new SimpleCancellable();
ret.setComplete();
return ret;
}
}
if (!connectAllAddresses || proxyHost != null || data.request.getProxyHost() != null) {
// just default to connecting to a single address
data.request.logd("Connecting socket");
String unresolvedHost;
int unresolvedPort;
boolean proxied = false;
if (data.request.getProxyHost() == null && proxyHost != null)
data.request.enableProxy(proxyHost, proxyPort);
if (data.request.getProxyHost() != null) {
unresolvedHost = data.request.getProxyHost();
unresolvedPort = data.request.getProxyPort();
proxied = true;
} else {
unresolvedHost = uri.getHost();
unresolvedPort = port;
}
if (proxied) {
data.request.logv("Using proxy: " + unresolvedHost + ":" + unresolvedPort);
}
return mClient.getServer().connectSocket(unresolvedHost, unresolvedPort, wrapCallback(data, uri, port, proxied, data.connectCallback));
}
// try to connect to everything...
data.request.logv("Resolving domain and connecting to all available addresses");
return mClient.getServer().getAllByName(uri.getHost()).then(new TransformFuture<AsyncSocket, InetAddress[]>() {
Exception lastException;
@Override
protected void error(Exception e) {
super.error(e);
wrapCallback(data, uri, port, false, data.connectCallback).onConnectCompleted(e, null);
}
@Override
protected void transform(final InetAddress[] result) throws Exception {
Continuation keepTrying = new Continuation(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
// if it completed, that means that the connection failed
if (lastException == null)
lastException = new ConnectionFailedException("Unable to connect to remote address");
if (setComplete(lastException)) {
wrapCallback(data, uri, port, false, data.connectCallback).onConnectCompleted(lastException, null);
}
}
});
for (final InetAddress address : result) {
final String inetSockAddress = String.format(Locale.ENGLISH, "%s:%s", address, port);
keepTrying.add(new ContinuationCallback() {
@Override
public void onContinue(Continuation continuation, final CompletedCallback next) throws Exception {
data.request.logv("attempting connection to " + inetSockAddress);
mClient.getServer().connectSocket(new InetSocketAddress(address, port), wrapCallback(data, uri, port, false, new ConnectCallback() {
@Override
public void onConnectCompleted(Exception ex, AsyncSocket socket) {
if (isDone()) {
lastException = new Exception("internal error during connect to " + inetSockAddress);
next.onCompleted(null);
return;
}
// try the next address
if (ex != null) {
lastException = ex;
next.onCompleted(null);
return;
}
// if the socket is no longer needed, just hang onto it...
if (isDone() || isCancelled()) {
data.request.logd("Recycling extra socket leftover from cancelled operation");
idleSocket(socket);
recycleSocket(socket, data.request);
return;
}
if (setComplete(null, socket)) {
data.connectCallback.onConnectCompleted(null, socket);
}
}
}));
}
});
}
keepTrying.start();
}
});
}
use of com.koushikdutta.async.AsyncSocket in project AndroidAsync by koush.
the class AsyncHttpClient method executeSocket.
private void executeSocket(final AsyncHttpRequest request, final int redirectCount, final FutureAsyncHttpResponse cancel, final HttpConnectCallback callback, final AsyncHttpClientMiddleware.OnResponseCompleteDataOnRequestSentData data) {
// 4) wait for request to be sent fully
// and
// 6) wait for headers
final AsyncHttpResponseImpl ret = new AsyncHttpResponseImpl(request) {
@Override
protected void onRequestCompleted(Exception ex) {
if (ex != null) {
reportConnectedCompleted(cancel, ex, null, request, callback);
return;
}
request.logv("request completed");
if (cancel.isCancelled())
return;
// 5) after request is sent, set a header timeout
if (cancel.timeoutRunnable != null && mHeaders == null) {
mServer.removeAllCallbacks(cancel.scheduled);
cancel.scheduled = mServer.postDelayed(cancel.timeoutRunnable, getTimeoutRemaining(request));
}
for (AsyncHttpClientMiddleware middleware : mMiddleware) {
middleware.onRequestSent(data);
}
}
@Override
public void setDataEmitter(DataEmitter emitter) {
data.bodyEmitter = emitter;
for (AsyncHttpClientMiddleware middleware : mMiddleware) {
middleware.onBodyDecoder(data);
}
super.setDataEmitter(data.bodyEmitter);
Headers headers = mHeaders;
int responseCode = code();
if ((responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == 307) && request.getFollowRedirect()) {
String location = headers.get("Location");
Uri redirect;
try {
redirect = Uri.parse(location);
if (redirect.getScheme() == null) {
redirect = Uri.parse(new URL(new URL(request.getUri().toString()), location).toString());
}
} catch (Exception e) {
reportConnectedCompleted(cancel, e, this, request, callback);
return;
}
final String method = request.getMethod().equals(AsyncHttpHead.METHOD) ? AsyncHttpHead.METHOD : AsyncHttpGet.METHOD;
AsyncHttpRequest newReq = new AsyncHttpRequest(redirect, method);
newReq.executionTime = request.executionTime;
newReq.logLevel = request.logLevel;
newReq.LOGTAG = request.LOGTAG;
newReq.proxyHost = request.proxyHost;
newReq.proxyPort = request.proxyPort;
setupAndroidProxy(newReq);
copyHeader(request, newReq, "User-Agent");
copyHeader(request, newReq, "Range");
request.logi("Redirecting");
newReq.logi("Redirected");
execute(newReq, redirectCount + 1, cancel, callback);
setDataCallback(new NullDataCallback());
return;
}
request.logv("Final (post cache response) headers:\n" + toString());
// at this point the headers are done being modified
reportConnectedCompleted(cancel, null, this, request, callback);
}
protected void onHeadersReceived() {
super.onHeadersReceived();
if (cancel.isCancelled())
return;
// 7) on headers, cancel timeout
if (cancel.timeoutRunnable != null)
mServer.removeAllCallbacks(cancel.scheduled);
// allow the middleware to massage the headers before the body is decoded
request.logv("Received headers:\n" + toString());
for (AsyncHttpClientMiddleware middleware : mMiddleware) {
middleware.onHeadersReceived(data);
}
// drop through, and setDataEmitter will be called for the body decoder.
// headers will be further massaged in there.
}
@Override
protected void report(Exception ex) {
if (ex != null)
request.loge("exception during response", ex);
if (cancel.isCancelled())
return;
if (ex instanceof AsyncSSLException) {
request.loge("SSL Exception", ex);
AsyncSSLException ase = (AsyncSSLException) ex;
request.onHandshakeException(ase);
if (ase.getIgnore())
return;
}
final AsyncSocket socket = socket();
if (socket == null)
return;
super.report(ex);
if (!socket.isOpen() || ex != null) {
if (headers() == null && ex != null)
reportConnectedCompleted(cancel, ex, null, request, callback);
}
data.exception = ex;
for (AsyncHttpClientMiddleware middleware : mMiddleware) {
middleware.onResponseComplete(data);
}
}
@Override
public AsyncSocket detachSocket() {
request.logd("Detaching socket");
AsyncSocket socket = socket();
if (socket == null)
return null;
socket.setWriteableCallback(null);
socket.setClosedCallback(null);
socket.setEndCallback(null);
socket.setDataCallback(null);
setSocket(null);
return socket;
}
};
data.sendHeadersCallback = new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
if (ex != null)
ret.report(ex);
else
ret.onHeadersSent();
}
};
data.receiveHeadersCallback = new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
if (ex != null)
ret.report(ex);
else
ret.onHeadersReceived();
}
};
data.response = ret;
ret.setSocket(data.socket);
for (AsyncHttpClientMiddleware middleware : mMiddleware) {
if (middleware.exchangeHeaders(data))
break;
}
}
use of com.koushikdutta.async.AsyncSocket in project AndroidAsync by koush.
the class AsyncHttpClient method executeAffinity.
private void executeAffinity(final AsyncHttpRequest request, final int redirectCount, final FutureAsyncHttpResponse cancel, final HttpConnectCallback callback) {
assert mServer.isAffinityThread();
if (redirectCount > 15) {
reportConnectedCompleted(cancel, new RedirectLimitExceededException("too many redirects"), null, request, callback);
return;
}
final Uri uri = request.getUri();
final AsyncHttpClientMiddleware.OnResponseCompleteDataOnRequestSentData data = new AsyncHttpClientMiddleware.OnResponseCompleteDataOnRequestSentData();
request.executionTime = System.currentTimeMillis();
data.request = request;
request.logd("Executing request.");
for (AsyncHttpClientMiddleware middleware : mMiddleware) {
middleware.onRequest(data);
}
if (request.getTimeout() > 0) {
// set connect timeout
cancel.timeoutRunnable = new Runnable() {
@Override
public void run() {
// we've timed out, kill the connections
if (data.socketCancellable != null) {
data.socketCancellable.cancel();
if (data.socket != null)
data.socket.close();
}
reportConnectedCompleted(cancel, new TimeoutException(), null, request, callback);
}
};
cancel.scheduled = mServer.postDelayed(cancel.timeoutRunnable, getTimeoutRemaining(request));
}
// 2) wait for a connect
data.connectCallback = new ConnectCallback() {
boolean reported;
@Override
public void onConnectCompleted(Exception ex, AsyncSocket socket) {
if (reported) {
if (socket != null) {
socket.setDataCallback(new DataCallback.NullDataCallback());
socket.setEndCallback(new CompletedCallback.NullCompletedCallback());
socket.close();
throw new AssertionError("double connect callback");
}
}
reported = true;
request.logv("socket connected");
if (cancel.isCancelled()) {
if (socket != null)
socket.close();
return;
}
// 3) on connect, cancel timeout
if (cancel.timeoutRunnable != null)
mServer.removeAllCallbacks(cancel.scheduled);
if (ex != null) {
reportConnectedCompleted(cancel, ex, null, request, callback);
return;
}
data.socket = socket;
cancel.socket = socket;
executeSocket(request, redirectCount, cancel, callback, data);
}
};
// set up the system default proxy and connect
setupAndroidProxy(request);
// set the implicit content type
if (request.getBody() != null) {
if (request.getHeaders().get("Content-Type") == null)
request.getHeaders().set("Content-Type", request.getBody().getContentType());
}
final Exception unsupportedURI;
for (AsyncHttpClientMiddleware middleware : mMiddleware) {
Cancellable socketCancellable = middleware.getSocket(data);
if (socketCancellable != null) {
data.socketCancellable = socketCancellable;
cancel.setParent(socketCancellable);
return;
}
}
unsupportedURI = new IllegalArgumentException("invalid uri=" + request.getUri() + " middlewares=" + mMiddleware);
reportConnectedCompleted(cancel, unsupportedURI, null, request, callback);
}
use of com.koushikdutta.async.AsyncSocket in project AndroidAsync by koush.
the class AsyncSSLSocketMiddleware method wrapCallback.
@Override
protected ConnectCallback wrapCallback(final GetSocketData data, final Uri uri, final int port, final boolean proxied, final ConnectCallback callback) {
return new ConnectCallback() {
@Override
public void onConnectCompleted(Exception ex, final AsyncSocket socket) {
if (ex != null) {
callback.onConnectCompleted(ex, socket);
return;
}
if (!proxied) {
tryHandshake(socket, data, uri, port, callback);
return;
}
// this SSL connection is proxied, must issue a CONNECT request to the proxy server
// http://stackoverflow.com/a/6594880/704837
// some proxies also require 'Host' header, it should be safe to provide it every time
String connect = String.format(Locale.ENGLISH, "CONNECT %s:%s HTTP/1.1\r\nHost: %s\r\n\r\n", uri.getHost(), port, uri.getHost());
data.request.logv("Proxying: " + connect);
Util.writeAll(socket, connect.getBytes(), new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
if (ex != null) {
callback.onConnectCompleted(ex, socket);
return;
}
LineEmitter liner = new LineEmitter();
liner.setLineCallback(new LineEmitter.StringCallback() {
String statusLine;
@Override
public void onStringAvailable(String s) {
data.request.logv(s);
if (statusLine == null) {
statusLine = s.trim();
if (!statusLine.matches("HTTP/1.\\d 2\\d\\d .*")) {
// connect response is allowed to have any 2xx status code
socket.setDataCallback(null);
socket.setEndCallback(null);
callback.onConnectCompleted(new IOException("non 2xx status line: " + statusLine), socket);
}
} else if (TextUtils.isEmpty(s.trim())) {
// skip all headers, complete handshake once empty line is received
socket.setDataCallback(null);
socket.setEndCallback(null);
tryHandshake(socket, data, uri, port, callback);
}
}
});
socket.setDataCallback(liner);
socket.setEndCallback(new CompletedCallback() {
@Override
public void onCompleted(Exception ex) {
if (!socket.isOpen() && ex == null)
ex = new IOException("socket closed before proxy connect response");
callback.onConnectCompleted(ex, socket);
}
});
}
});
}
};
}
use of com.koushikdutta.async.AsyncSocket in project AndroidAsync by koush.
the class AsyncSocketMiddleware method maybeCleanupConnectionInfo.
private void maybeCleanupConnectionInfo(String lookup) {
ConnectionInfo info = connectionInfo.get(lookup);
if (info == null)
return;
while (!info.sockets.isEmpty()) {
IdleSocketHolder idleSocketHolder = info.sockets.peekLast();
AsyncSocket socket = idleSocketHolder.socket;
if (idleSocketHolder.idleTime + idleTimeoutMs > System.currentTimeMillis())
break;
info.sockets.pop();
// remove the callback, prevent reentrancy.
socket.setClosedCallback(null);
socket.close();
}
if (info.openCount == 0 && info.queue.isEmpty() && info.sockets.isEmpty())
connectionInfo.remove(lookup);
}
Aggregations