1166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathpackage com.squareup.okhttp.sample; 2166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 3166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.internal.Util; 4166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.Dispatcher; 5166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.MockResponse; 6166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.MockWebServer; 7166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport com.squareup.okhttp.mockwebserver.RecordedRequest; 8166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport java.io.File; 9166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport java.io.FileInputStream; 10166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport java.io.FileNotFoundException; 11166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport java.io.IOException; 123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.InputStream; 13166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport java.security.GeneralSecurityException; 14166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport java.security.KeyStore; 15166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport java.security.SecureRandom; 16166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport javax.net.ssl.KeyManagerFactory; 17166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport javax.net.ssl.SSLContext; 18166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathimport javax.net.ssl.TrustManagerFactory; 19166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 20166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamathpublic class SampleServer extends Dispatcher { 21166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath private final SSLContext sslContext; 22166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath private final String root; 23166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath private final int port; 24166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 25166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath public SampleServer(SSLContext sslContext, String root, int port) { 26166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath this.sslContext = sslContext; 27166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath this.root = root; 28166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath this.port = port; 29166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 30166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 31166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath public void run() throws IOException { 32166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath MockWebServer server = new MockWebServer(); 33166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath server.useHttps(sslContext.getSocketFactory(), false); 34166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath server.setDispatcher(this); 35166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath server.play(port); 36166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 37166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 38166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath @Override public MockResponse dispatch(RecordedRequest request) { 39166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath String path = request.getPath(); 40166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath try { 41166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath if (!path.startsWith("/") || path.contains("..")) throw new FileNotFoundException(); 42166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 43166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath File file = new File(root + path); 44166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath return file.isDirectory() 45166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath ? directoryToResponse(path, file) 46166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath : fileToResponse(path, file); 47166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } catch (FileNotFoundException e) { 48166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath return new MockResponse() 49166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .setStatus("HTTP/1.1 404") 50166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .addHeader("content-type: text/plain; charset=utf-8") 51166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .setBody("NOT FOUND: " + path); 52166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } catch (IOException e) { 53166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath return new MockResponse() 54166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .setStatus("HTTP/1.1 500") 55166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .addHeader("content-type: text/plain; charset=utf-8") 56166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .setBody("SERVER ERROR: " + e); 57166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 58166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 59166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 60166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath private MockResponse directoryToResponse(String basePath, File directory) { 61166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath if (!basePath.endsWith("/")) basePath += "/"; 62166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 63166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath StringBuilder response = new StringBuilder(); 64166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath response.append(String.format("<html><head><title>%s</title></head><body>", basePath)); 65166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath response.append(String.format("<h1>%s</h1>", basePath)); 66166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath for (String file : directory.list()) { 67166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath response.append(String.format("<div class='file'><a href='%s'>%s</a></div>", 68166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath basePath + file, file)); 69166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 70166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath response.append("</body></html>"); 71166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 72166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath return new MockResponse() 73166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .setStatus("HTTP/1.1 200") 74166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .addHeader("content-type: text/html; charset=utf-8") 75166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .setBody(response.toString()); 76166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 77166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 78166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath private MockResponse fileToResponse(String path, File file) throws IOException { 79166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath return new MockResponse() 80166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .setStatus("HTTP/1.1 200") 81166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .setBody(fileToBytes(file)) 82166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath .addHeader("content-type: " + contentType(path)); 83166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 84166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 85166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath private byte[] fileToBytes(File file) throws IOException { 86166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath byte[] result = new byte[(int) file.length()]; 87166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath Util.readFully(new FileInputStream(file), result); 88166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath return result; 89166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 90166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 91166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath private String contentType(String path) { 92166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath if (path.endsWith(".png")) return "image/png"; 93166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath if (path.endsWith(".jpg")) return "image/jpeg"; 94166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath if (path.endsWith(".jpeg")) return "image/jpeg"; 95166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath if (path.endsWith(".gif")) return "image/gif"; 96166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath if (path.endsWith(".html")) return "text/html; charset=utf-8"; 97166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath if (path.endsWith(".txt")) return "text/plain; charset=utf-8"; 98166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath return "application/octet-stream"; 99166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 100166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 101166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath public static void main(String[] args) throws Exception { 102166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath if (args.length != 4) { 103166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath System.out.println("Usage: SampleServer <keystore> <password> <root file> <port>"); 104166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath return; 105166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 106166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 107166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath String keystoreFile = args[0]; 108166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath String password = args[1]; 109166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath String root = args[2]; 110166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath int port = Integer.parseInt(args[3]); 111166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 112166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath SSLContext sslContext = sslContext(keystoreFile, password); 113166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath SampleServer server = new SampleServer(sslContext, root, port); 114166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath server.run(); 115166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 116166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 117166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath private static SSLContext sslContext(String keystoreFile, String password) 118166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath throws GeneralSecurityException, IOException { 119166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); 1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller InputStream in = new FileInputStream(keystoreFile); 1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller keystore.load(in, password.toCharArray()); 1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } finally { 1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Util.closeQuietly(in); 1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 126166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath KeyManagerFactory keyManagerFactory = 127166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 128166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath keyManagerFactory.init(keystore, password.toCharArray()); 129166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 130166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath TrustManagerFactory trustManagerFactory = 131166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 132166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath trustManagerFactory.init(keystore); 133166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 134166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath SSLContext sslContext = SSLContext.getInstance("TLS"); 135166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath sslContext.init( 136166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath keyManagerFactory.getKeyManagers(), 137166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath trustManagerFactory.getTrustManagers(), 138166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath new SecureRandom()); 139166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath 140166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath return sslContext; 141166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath } 142166772be0e5cfdaea1a64b9f63e4c8dbfe48cba3Narayan Kamath} 143