From e135bf0b42234d03a7f26990b255851e9baa3355 Mon Sep 17 00:00:00 2001 From: Alexey Danilov Date: Fri, 25 Sep 2015 20:46:02 +0300 Subject: [PATCH] ping proxy after starting to make sure it works fine --- README.md | 5 +- library/build.gradle | 2 +- .../videocache/HttpProxyCacheServer.java | 84 ++++++++++++++++++- sample/build.gradle | 2 +- 4 files changed, 88 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fba3fd3..c45930c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ repositories { maven { url 'https://dl.bintray.com/alexeydanilov/maven' } } dependencies { - compile 'com.danikula:videocache:2.1.2' + compile 'com.danikula:videocache:2.1.3' } ``` @@ -59,6 +59,9 @@ More preferable way is use some dependency injector like [Dagger](http://square. See `sample` app for details. ## Whats new +### 2.1.3 +- ping proxy after starting to make sure it works fine + ### 2.1.2 - fix offline work diff --git a/library/build.gradle b/library/build.gradle index 7c64ea2..6dbe8a1 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -26,7 +26,7 @@ publish { userOrg = 'alexeydanilov' groupId = 'com.danikula' artifactId = 'videocache' - publishVersion = '2.1.2' + publishVersion = '2.1.3' description = 'Cache support for android VideoView' website = 'https://github.com/danikula/AndroidVideoCache' } diff --git a/library/src/main/java/com/danikula/videocache/HttpProxyCacheServer.java b/library/src/main/java/com/danikula/videocache/HttpProxyCacheServer.java index 6ddd95e..49920ae 100644 --- a/library/src/main/java/com/danikula/videocache/HttpProxyCacheServer.java +++ b/library/src/main/java/com/danikula/videocache/HttpProxyCacheServer.java @@ -1,17 +1,25 @@ package com.danikula.videocache; +import android.os.SystemClock; import android.util.Log; import java.io.IOException; +import java.io.OutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; +import java.util.Arrays; import java.util.Map; +import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import static com.danikula.videocache.Preconditions.checkAllNotNull; import static com.danikula.videocache.Preconditions.checkNotNull; @@ -39,6 +47,8 @@ public class HttpProxyCacheServer { private static final String PROXY_HOST = "127.0.0.1"; + private static final String PING_REQUEST = "ping"; + private static final String PING_RESPONSE = "ping ok"; private final Object clientsLock = new Object(); private final ExecutorService socketProcessor = Executors.newFixedThreadPool(8); @@ -47,6 +57,7 @@ public class HttpProxyCacheServer { private final int port; private final Thread waitConnectionThread; private final FileNameGenerator fileNameGenerator; + private boolean pinged; public HttpProxyCacheServer(FileNameGenerator fileNameGenerator) { this.fileNameGenerator = checkNotNull(fileNameGenerator); @@ -58,13 +69,65 @@ public HttpProxyCacheServer(FileNameGenerator fileNameGenerator) { this.waitConnectionThread = new Thread(new WaitRequestsRunnable(startSignal)); this.waitConnectionThread.start(); startSignal.await(); // freeze thread, wait for server starts + Log.i(LOG_TAG, "Proxy cache server started. Ping it..."); + makeSureServerWorks(); } catch (IOException | InterruptedException e) { socketProcessor.shutdown(); throw new IllegalStateException("Error starting local proxy server", e); } } + private void makeSureServerWorks() { + int maxPingAttempts = 3; + int delay = 100; + int pingAttempts = 0; + while (pingAttempts < maxPingAttempts) { + try { + Future pingFuture = socketProcessor.submit(new PingCallable()); + pinged = pingFuture.get(delay, TimeUnit.MILLISECONDS); + if (pinged) { + return; + } + pingAttempts++; + SystemClock.sleep(delay); + delay *= 2; + } catch (InterruptedException | ExecutionException | TimeoutException e) { + Log.e(LOG_TAG, "Error pinging server. Shutdown it... If you see this message, please, email me danikula@gmail.com", e); + } + } + + if (!pinged) { + shutdown(); + } + } + + private boolean pingServer() throws ProxyCacheException { + String pingUrl = appendToProxyUrl(PING_REQUEST); + HttpUrlSource source = new HttpUrlSource(pingUrl); + try { + byte[] expectedResponse = PING_RESPONSE.getBytes(); + source.open(0); + byte[] response = new byte[expectedResponse.length]; + source.read(response); + boolean pingOk = Arrays.equals(expectedResponse, response); + Log.d(LOG_TAG, "Ping response: `" + new String(response) + "`, pinged? " + pingOk); + return pingOk; + } catch (ProxyCacheException e) { + Log.e(LOG_TAG, "Error reading ping response", e); + return false; + } finally { + source.close(); + } + } + public String getProxyUrl(String url) { + if (!pinged) { + Log.e(LOG_TAG, "Proxy server isn't pinged. Caching doesn't work. If you see this message, please, email me danikula@gmail.com"); + } + return pinged ? appendToProxyUrl(url) : url; + } + + private String appendToProxyUrl(String url) { return String.format("http://%s:%d/%s", PROXY_HOST, port, ProxyCacheUtils.encode(url)); } @@ -140,8 +203,12 @@ private void processSocket(Socket socket) { GetRequest request = GetRequest.read(socket.getInputStream()); Log.i(LOG_TAG, "Request to cache proxy:" + request); String url = ProxyCacheUtils.decode(request.uri); - HttpProxyCacheServerClients clients = getClients(url); - clients.processRequest(request, socket); + if (PING_REQUEST.equals(url)) { + responseToPing(socket); + } else { + HttpProxyCacheServerClients clients = getClients(url); + clients.processRequest(request, socket); + } } catch (SocketException e) { // There is no way to determine that client closed connection http://stackoverflow.com/a/10241044/999458 // So just to prevent log flooding don't log stacktrace @@ -154,6 +221,12 @@ private void processSocket(Socket socket) { } } + private void responseToPing(Socket socket) throws IOException { + OutputStream out = socket.getOutputStream(); + out.write("HTTP/1.1 200 OK\n\n".getBytes()); + out.write(PING_RESPONSE.getBytes()); + } + private HttpProxyCacheServerClients getClients(String url) throws ProxyCacheException { synchronized (clientsLock) { HttpProxyCacheServerClients clients = clientsMap.get(url); @@ -248,4 +321,11 @@ public void run() { } } + private class PingCallable implements Callable { + + @Override + public Boolean call() throws Exception { + return pingServer(); + } + } } diff --git a/sample/build.gradle b/sample/build.gradle index e36776b..799a3cd 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -39,7 +39,7 @@ dependencies { // compile project(':library') compile 'com.android.support:support-v4:23.0.1' compile 'org.androidannotations:androidannotations-api:3.3.2' - compile 'com.danikula:videocache:2.1.2' + compile 'com.danikula:videocache:2.1.3' compile 'com.viewpagerindicator:library:2.4.2-SNAPSHOT@aar' apt 'org.androidannotations:androidannotations:3.3.2' }