1package com.squareup.okhttp.sample;
2
3import com.squareup.okhttp.internal.Util;
4import com.squareup.okhttp.mockwebserver.Dispatcher;
5import com.squareup.okhttp.mockwebserver.MockResponse;
6import com.squareup.okhttp.mockwebserver.MockWebServer;
7import com.squareup.okhttp.mockwebserver.RecordedRequest;
8import java.io.File;
9import java.io.FileInputStream;
10import java.io.FileNotFoundException;
11import java.io.IOException;
12import java.io.InputStream;
13import java.security.GeneralSecurityException;
14import java.security.KeyStore;
15import java.security.SecureRandom;
16import javax.net.ssl.KeyManagerFactory;
17import javax.net.ssl.SSLContext;
18import javax.net.ssl.TrustManagerFactory;
19import okio.Buffer;
20import okio.Okio;
21
22public class SampleServer extends Dispatcher {
23  private final SSLContext sslContext;
24  private final String root;
25  private final int port;
26
27  public SampleServer(SSLContext sslContext, String root, int port) {
28    this.sslContext = sslContext;
29    this.root = root;
30    this.port = port;
31  }
32
33  public void run() throws IOException {
34    MockWebServer server = new MockWebServer();
35    server.useHttps(sslContext.getSocketFactory(), false);
36    server.setDispatcher(this);
37    server.start(port);
38  }
39
40  @Override public MockResponse dispatch(RecordedRequest request) {
41    String path = request.getPath();
42    try {
43      if (!path.startsWith("/") || path.contains("..")) throw new FileNotFoundException();
44
45      File file = new File(root + path);
46      return file.isDirectory()
47          ? directoryToResponse(path, file)
48          : fileToResponse(path, file);
49    } catch (FileNotFoundException e) {
50      return new MockResponse()
51          .setStatus("HTTP/1.1 404")
52          .addHeader("content-type: text/plain; charset=utf-8")
53          .setBody("NOT FOUND: " + path);
54    } catch (IOException e) {
55      return new MockResponse()
56          .setStatus("HTTP/1.1 500")
57          .addHeader("content-type: text/plain; charset=utf-8")
58          .setBody("SERVER ERROR: " + e);
59    }
60  }
61
62  private MockResponse directoryToResponse(String basePath, File directory) {
63    if (!basePath.endsWith("/")) basePath += "/";
64
65    StringBuilder response = new StringBuilder();
66    response.append(String.format("<html><head><title>%s</title></head><body>", basePath));
67    response.append(String.format("<h1>%s</h1>", basePath));
68    for (String file : directory.list()) {
69      response.append(String.format("<div class='file'><a href='%s'>%s</a></div>",
70          basePath + file, file));
71    }
72    response.append("</body></html>");
73
74    return new MockResponse()
75        .setStatus("HTTP/1.1 200")
76        .addHeader("content-type: text/html; charset=utf-8")
77        .setBody(response.toString());
78  }
79
80  private MockResponse fileToResponse(String path, File file) throws IOException {
81    return new MockResponse()
82        .setStatus("HTTP/1.1 200")
83        .setBody(fileToBytes(file))
84        .addHeader("content-type: " + contentType(path));
85  }
86
87  private Buffer fileToBytes(File file) throws IOException {
88    Buffer result = new Buffer();
89    result.writeAll(Okio.source(file));
90    return result;
91  }
92
93  private String contentType(String path) {
94    if (path.endsWith(".png")) return "image/png";
95    if (path.endsWith(".jpg")) return "image/jpeg";
96    if (path.endsWith(".jpeg")) return "image/jpeg";
97    if (path.endsWith(".gif")) return "image/gif";
98    if (path.endsWith(".html")) return "text/html; charset=utf-8";
99    if (path.endsWith(".txt")) return "text/plain; charset=utf-8";
100    return "application/octet-stream";
101  }
102
103  public static void main(String[] args) throws Exception {
104    if (args.length != 4) {
105      System.out.println("Usage: SampleServer <keystore> <password> <root file> <port>");
106      return;
107    }
108
109    String keystoreFile = args[0];
110    String password = args[1];
111    String root = args[2];
112    int port = Integer.parseInt(args[3]);
113
114    SSLContext sslContext = sslContext(keystoreFile, password);
115    SampleServer server = new SampleServer(sslContext, root, port);
116    server.run();
117  }
118
119  private static SSLContext sslContext(String keystoreFile, String password)
120      throws GeneralSecurityException, IOException {
121    KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
122    InputStream in = new FileInputStream(keystoreFile);
123    try {
124      keystore.load(in, password.toCharArray());
125    } finally {
126      Util.closeQuietly(in);
127    }
128    KeyManagerFactory keyManagerFactory =
129        KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
130    keyManagerFactory.init(keystore, password.toCharArray());
131
132    TrustManagerFactory trustManagerFactory =
133        TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
134    trustManagerFactory.init(keystore);
135
136    SSLContext sslContext = SSLContext.getInstance("TLS");
137    sslContext.init(
138        keyManagerFactory.getKeyManagers(),
139        trustManagerFactory.getTrustManagers(),
140        new SecureRandom());
141
142    return sslContext;
143  }
144}
145