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