• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.squareup.okhttp.internal.framed;
18 
19 import com.squareup.okhttp.Protocol;
20 import com.squareup.okhttp.internal.Platform;
21 import com.squareup.okhttp.internal.SslContextBuilder;
22 import com.squareup.okhttp.internal.Util;
23 import java.io.File;
24 import java.io.IOException;
25 import java.net.ProtocolException;
26 import java.net.ServerSocket;
27 import java.net.Socket;
28 import java.util.Arrays;
29 import java.util.List;
30 import java.util.logging.Level;
31 import java.util.logging.Logger;
32 import javax.net.ssl.SSLSocket;
33 import javax.net.ssl.SSLSocketFactory;
34 import okio.BufferedSink;
35 import okio.Okio;
36 import okio.Source;
37 
38 /** A basic SPDY/HTTP_2 server that serves the contents of a local directory. */
39 public final class FramedServer extends FramedConnection.Listener {
40   static final Logger logger = Logger.getLogger(FramedServer.class.getName());
41 
42   private final List<Protocol> framedProtocols =
43       Util.immutableList(Protocol.HTTP_2, Protocol.SPDY_3);
44 
45   private final File baseDirectory;
46   private final SSLSocketFactory sslSocketFactory;
47 
FramedServer(File baseDirectory, SSLSocketFactory sslSocketFactory)48   public FramedServer(File baseDirectory, SSLSocketFactory sslSocketFactory) {
49     this.baseDirectory = baseDirectory;
50     this.sslSocketFactory = sslSocketFactory;
51   }
52 
run()53   private void run() throws Exception {
54     ServerSocket serverSocket = new ServerSocket(8888);
55     serverSocket.setReuseAddress(true);
56 
57     while (true) {
58       Socket socket = null;
59       try {
60         socket = serverSocket.accept();
61 
62         SSLSocket sslSocket = doSsl(socket);
63         String protocolString = Platform.get().getSelectedProtocol(sslSocket);
64         Protocol protocol = protocolString != null ? Protocol.get(protocolString) : null;
65         if (protocol == null || !framedProtocols.contains(protocol)) {
66           throw new ProtocolException("Protocol " + protocol + " unsupported");
67         }
68         FramedConnection framedConnection = new FramedConnection.Builder(false)
69             .socket(sslSocket)
70             .protocol(protocol)
71             .listener(this)
72             .build();
73         framedConnection.sendConnectionPreface();
74       } catch (IOException e) {
75         logger.log(Level.INFO, "FramedServer connection failure: " + e);
76         Util.closeQuietly(socket);
77       } catch (Exception e) {
78         logger.log(Level.WARNING, "FramedServer unexpected failure", e);
79         Util.closeQuietly(socket);
80       }
81     }
82   }
83 
doSsl(Socket socket)84   private SSLSocket doSsl(Socket socket) throws IOException {
85     SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(
86         socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true);
87     sslSocket.setUseClientMode(false);
88     Platform.get().configureTlsExtensions(sslSocket, null, framedProtocols);
89     sslSocket.startHandshake();
90     return sslSocket;
91   }
92 
onStream(final FramedStream stream)93   @Override public void onStream(final FramedStream stream) throws IOException {
94     try {
95       List<Header> requestHeaders = stream.getRequestHeaders();
96       String path = null;
97       for (int i = 0, size = requestHeaders.size(); i < size; i++) {
98         if (requestHeaders.get(i).name.equals(Header.TARGET_PATH)) {
99           path = requestHeaders.get(i).value.utf8();
100           break;
101         }
102       }
103 
104       if (path == null) {
105         // TODO: send bad request error
106         throw new AssertionError();
107       }
108 
109       File file = new File(baseDirectory + path);
110 
111       if (file.isDirectory()) {
112         serveDirectory(stream, file.listFiles());
113       } else if (file.exists()) {
114         serveFile(stream, file);
115       } else {
116         send404(stream, path);
117       }
118     } catch (IOException e) {
119       System.out.println(e.getMessage());
120     }
121   }
122 
send404(FramedStream stream, String path)123   private void send404(FramedStream stream, String path) throws IOException {
124     List<Header> responseHeaders = Arrays.asList(
125         new Header(":status", "404"),
126         new Header(":version", "HTTP/1.1"),
127         new Header("content-type", "text/plain")
128     );
129     stream.reply(responseHeaders, true);
130     BufferedSink out = Okio.buffer(stream.getSink());
131     out.writeUtf8("Not found: " + path);
132     out.close();
133   }
134 
serveDirectory(FramedStream stream, File[] files)135   private void serveDirectory(FramedStream stream, File[] files) throws IOException {
136     List<Header> responseHeaders = Arrays.asList(
137         new Header(":status", "200"),
138         new Header(":version", "HTTP/1.1"),
139         new Header("content-type", "text/html; charset=UTF-8")
140     );
141     stream.reply(responseHeaders, true);
142     BufferedSink out = Okio.buffer(stream.getSink());
143     for (File file : files) {
144       String target = file.isDirectory() ? (file.getName() + "/") : file.getName();
145       out.writeUtf8("<a href='" + target + "'>" + target + "</a><br>");
146     }
147     out.close();
148   }
149 
serveFile(FramedStream stream, File file)150   private void serveFile(FramedStream stream, File file) throws IOException {
151     List<Header> responseHeaders = Arrays.asList(
152         new Header(":status", "200"),
153         new Header(":version", "HTTP/1.1"),
154         new Header("content-type", contentType(file))
155     );
156     stream.reply(responseHeaders, true);
157     Source source = Okio.source(file);
158     try {
159       BufferedSink out = Okio.buffer(stream.getSink());
160       out.writeAll(source);
161       out.close();
162     } finally {
163       Util.closeQuietly(source);
164     }
165   }
166 
contentType(File file)167   private String contentType(File file) {
168     if (file.getName().endsWith(".css")) return "text/css";
169     if (file.getName().endsWith(".gif")) return "image/gif";
170     if (file.getName().endsWith(".html")) return "text/html";
171     if (file.getName().endsWith(".jpeg")) return "image/jpeg";
172     if (file.getName().endsWith(".jpg")) return "image/jpeg";
173     if (file.getName().endsWith(".js")) return "application/javascript";
174     if (file.getName().endsWith(".png")) return "image/png";
175     return "text/plain";
176   }
177 
main(String... args)178   public static void main(String... args) throws Exception {
179     if (args.length != 1 || args[0].startsWith("-")) {
180       System.out.println("Usage: FramedServer <base directory>");
181       return;
182     }
183 
184     FramedServer server = new FramedServer(new File(args[0]),
185         SslContextBuilder.localhost().getSocketFactory());
186     server.run();
187   }
188 }
189