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