1 /* 2 * Copyright (C) 2014 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.example.android.permissionrequest; 18 19 import android.content.res.AssetManager; 20 import android.text.TextUtils; 21 import android.util.Log; 22 23 import java.io.BufferedReader; 24 import java.io.ByteArrayOutputStream; 25 import java.io.FileNotFoundException; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.InputStreamReader; 29 import java.io.PrintStream; 30 import java.net.ServerSocket; 31 import java.net.Socket; 32 import java.net.SocketException; 33 34 /** 35 * Implementation of a very basic HTTP server. The contents are loaded from the assets folder. This 36 * server handles one request at a time. It only supports GET method. 37 */ 38 public class SimpleWebServer implements Runnable { 39 40 private static final String TAG = "SimpleWebServer"; 41 42 /** 43 * The port number we listen to 44 */ 45 private final int mPort; 46 47 /** 48 * {@link android.content.res.AssetManager} for loading files to serve. 49 */ 50 private final AssetManager mAssets; 51 52 /** 53 * True if the server is running. 54 */ 55 private boolean mIsRunning; 56 57 /** 58 * The {@link java.net.ServerSocket} that we listen to. 59 */ 60 private ServerSocket mServerSocket; 61 62 /** 63 * WebServer constructor. 64 */ SimpleWebServer(int port, AssetManager assets)65 public SimpleWebServer(int port, AssetManager assets) { 66 mPort = port; 67 mAssets = assets; 68 } 69 70 /** 71 * This method starts the web server listening to the specified port. 72 */ start()73 public void start() { 74 mIsRunning = true; 75 new Thread(this).start(); 76 } 77 78 /** 79 * This method stops the web server 80 */ stop()81 public void stop() { 82 try { 83 mIsRunning = false; 84 if (null != mServerSocket) { 85 mServerSocket.close(); 86 mServerSocket = null; 87 } 88 } catch (IOException e) { 89 Log.e(TAG, "Error closing the server socket.", e); 90 } 91 } 92 getPort()93 public int getPort() { 94 return mPort; 95 } 96 97 @Override run()98 public void run() { 99 try { 100 mServerSocket = new ServerSocket(mPort); 101 while (mIsRunning) { 102 Socket socket = mServerSocket.accept(); 103 handle(socket); 104 socket.close(); 105 } 106 } catch (SocketException e) { 107 // The server was stopped; ignore. 108 } catch (IOException e) { 109 Log.e(TAG, "Web server error.", e); 110 } 111 } 112 113 /** 114 * Respond to a request from a client. 115 * 116 * @param socket The client socket. 117 * @throws IOException 118 */ handle(Socket socket)119 private void handle(Socket socket) throws IOException { 120 BufferedReader reader = null; 121 PrintStream output = null; 122 try { 123 String route = null; 124 125 // Read HTTP headers and parse out the route. 126 reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); 127 String line; 128 while (!TextUtils.isEmpty(line = reader.readLine())) { 129 if (line.startsWith("GET /")) { 130 int start = line.indexOf('/') + 1; 131 int end = line.indexOf(' ', start); 132 route = line.substring(start, end); 133 break; 134 } 135 } 136 137 // Output stream that we send the response to 138 output = new PrintStream(socket.getOutputStream()); 139 140 // Prepare the content to send. 141 if (null == route) { 142 writeServerError(output); 143 return; 144 } 145 byte[] bytes = loadContent(route); 146 if (null == bytes) { 147 writeServerError(output); 148 return; 149 } 150 151 // Send out the content. 152 output.println("HTTP/1.0 200 OK"); 153 output.println("Content-Type: " + detectMimeType(route)); 154 output.println("Content-Length: " + bytes.length); 155 output.println(); 156 output.write(bytes); 157 output.flush(); 158 } finally { 159 if (null != output) { 160 output.close(); 161 } 162 if (null != reader) { 163 reader.close(); 164 } 165 } 166 } 167 168 /** 169 * Writes a server error response (HTTP/1.0 500) to the given output stream. 170 * 171 * @param output The output stream. 172 */ writeServerError(PrintStream output)173 private void writeServerError(PrintStream output) { 174 output.println("HTTP/1.0 500 Internal Server Error"); 175 output.flush(); 176 } 177 178 /** 179 * Loads all the content of {@code fileName}. 180 * 181 * @param fileName The name of the file. 182 * @return The content of the file. 183 * @throws IOException 184 */ loadContent(String fileName)185 private byte[] loadContent(String fileName) throws IOException { 186 InputStream input = null; 187 try { 188 ByteArrayOutputStream output = new ByteArrayOutputStream(); 189 input = mAssets.open(fileName); 190 byte[] buffer = new byte[1024]; 191 int size; 192 while (-1 != (size = input.read(buffer))) { 193 output.write(buffer, 0, size); 194 } 195 output.flush(); 196 return output.toByteArray(); 197 } catch (FileNotFoundException e) { 198 return null; 199 } finally { 200 if (null != input) { 201 input.close(); 202 } 203 } 204 } 205 206 /** 207 * Detects the MIME type from the {@code fileName}. 208 * 209 * @param fileName The name of the file. 210 * @return A MIME type. 211 */ detectMimeType(String fileName)212 private String detectMimeType(String fileName) { 213 if (TextUtils.isEmpty(fileName)) { 214 return null; 215 } else if (fileName.endsWith(".html")) { 216 return "text/html"; 217 } else if (fileName.endsWith(".js")) { 218 return "application/javascript"; 219 } else if (fileName.endsWith(".css")) { 220 return "text/css"; 221 } else { 222 return "application/octet-stream"; 223 } 224 } 225 226 } 227