/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.permissionrequest;
import android.content.res.AssetManager;
import android.text.TextUtils;
import android.util.Log;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
/**
* Implementation of a very basic HTTP server. The contents are loaded from the assets folder. This
* server handles one request at a time. It only supports GET method.
*/
public class SimpleWebServer implements Runnable {
private static final String TAG = "SimpleWebServer";
/**
* The port number we listen to
*/
private final int mPort;
/**
* {@link android.content.res.AssetManager} for loading files to serve.
*/
private final AssetManager mAssets;
/**
* True if the server is running.
*/
private boolean mIsRunning;
/**
* The {@link java.net.ServerSocket} that we listen to.
*/
private ServerSocket mServerSocket;
/**
* WebServer constructor.
*/
public SimpleWebServer(int port, AssetManager assets) {
mPort = port;
mAssets = assets;
}
/**
* This method starts the web server listening to the specified port.
*/
public void start() {
mIsRunning = true;
new Thread(this).start();
}
/**
* This method stops the web server
*/
public void stop() {
try {
mIsRunning = false;
if (null != mServerSocket) {
mServerSocket.close();
mServerSocket = null;
}
} catch (IOException e) {
Log.e(TAG, "Error closing the server socket.", e);
}
}
@Override
public void run() {
try {
mServerSocket = new ServerSocket(mPort);
while (mIsRunning) {
Socket socket = mServerSocket.accept();
handle(socket);
socket.close();
}
} catch (SocketException e) {
// The server was stopped; ignore.
} catch (IOException e) {
Log.e(TAG, "Web server error.", e);
}
}
/**
* Respond to a request from a client.
*
* @param socket The client socket.
* @throws IOException
*/
private void handle(Socket socket) throws IOException {
BufferedReader reader = null;
PrintStream output = null;
try {
String route = null;
// Read HTTP headers and parse out the route.
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while (!TextUtils.isEmpty(line = reader.readLine())) {
if (line.startsWith("GET /")) {
int start = line.indexOf('/') + 1;
int end = line.indexOf(' ', start);
route = line.substring(start, end);
break;
}
}
// Output stream that we send the response to
output = new PrintStream(socket.getOutputStream());
// Prepare the content to send.
if (null == route) {
writeServerError(output);
return;
}
byte[] bytes = loadContent(route);
if (null == bytes) {
writeServerError(output);
return;
}
// Send out the content.
output.println("HTTP/1.0 200 OK");
output.println("Content-Type: " + detectMimeType(route));
output.println("Content-Length: " + bytes.length);
output.println();
output.write(bytes);
output.flush();
} finally {
if (null != output) {
output.close();
}
if (null != reader) {
reader.close();
}
}
}
/**
* Writes a server error response (HTTP/1.0 500) to the given output stream.
*
* @param output The output stream.
*/
private void writeServerError(PrintStream output) {
output.println("HTTP/1.0 500 Internal Server Error");
output.flush();
}
/**
* Loads all the content of {@code fileName}.
*
* @param fileName The name of the file.
* @return The content of the file.
* @throws IOException
*/
private byte[] loadContent(String fileName) throws IOException {
InputStream input = null;
try {
ByteArrayOutputStream output = new ByteArrayOutputStream();
input = mAssets.open(fileName);
byte[] buffer = new byte[1024];
int size;
while (-1 != (size = input.read(buffer))) {
output.write(buffer, 0, size);
}
output.flush();
return output.toByteArray();
} catch (FileNotFoundException e) {
return null;
} finally {
if (null != input) {
input.close();
}
}
}
/**
* Detects the MIME type from the {@code fileName}.
*
* @param fileName The name of the file.
* @return A MIME type.
*/
private String detectMimeType(String fileName) {
if (TextUtils.isEmpty(fileName)) {
return null;
} else if (fileName.endsWith(".html")) {
return "text/html";
} else if (fileName.endsWith(".js")) {
return "application/javascript";
} else if (fileName.endsWith(".css")) {
return "text/css";
} else {
return "application/octet-stream";
}
}
}