WebServerThread.java :  » UnTagged » droid-copter » com » quadcopter » background » webserver » Android Open Source

Android Open Source » UnTagged » droid copter 
droid copter » com » quadcopter » background » webserver » WebServerThread.java
package com.quadcopter.background.webserver;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.RequestLine;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpResponse;
import org.apache.http.params.BasicHttpParams;

import com.quadcopter.background.BackgroundService;
import com.quadcopter.webserver.servlets.Servlet;
import com.quadcopter.webserver.servlets.ServletLoader;

import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
import android.util.Log;

public class WebServerThread extends Thread 
{
  private static final String TAG = "WebServerThread";
  
  private static final String SERVLET_PACKAGE = "com.quadcopter.webserver.servlets";

  private int mPort;

  Context mContext;
  
  private ServerSocket mServerSocket;

  private SharedPreferences mSharedPreferences;

  private SQLiteDatabase mCookiesDatabase;

  /* How long we allow session cookies to last. */
  private static final int COOKIE_EXPIRY_SECONDS = 3600;
  
  private byte[] webCameraImg = null;
  
  private ServletLoader servletLoader = null;
  
  public void setWebCamImg(byte[] img)
  {
    webCameraImg = img;
  }
  
  /* Returns port we're using */
  public int getPort() 
  {
    return mPort;
  }

  /* Start the webserver on specified port */
  public WebServerThread(Context context, SharedPreferences sharedPreferences,
      SQLiteDatabase cookiesDatabase, int port) throws IOException 
  {
    mContext = context;
    mPort = port;
    mServerSocket = new ServerSocket(mPort);
    mServerSocket.setReuseAddress(true);
    mSharedPreferences = sharedPreferences;
    mCookiesDatabase = cookiesDatabase;
    deleteOldCookies();
    
    //Make sure we have an instance of servletLoader 
    //ServletLoader will load an instance of a servlet
    //We have a global variable because, it will keep
    //previously loaded servlets in memory.
    servletLoader = new ServletLoader();
    //some servlets need to be loaded as soon as we start
    //the web server. So . . . we will load them here.
    Servlet servlet = servletLoader.loadServlet(SERVLET_PACKAGE + "." + "CounterServlet");
    servlet.setupServlet(context);
    servlet = servletLoader.loadServlet(SERVLET_PACKAGE + "." + "GetMessageServlet");
    servlet.setupServlet(context);
    servlet = servletLoader.loadServlet(SERVLET_PACKAGE + "." + "SendMessageServlet");
    servlet.setupServlet(context);
  }

  @Override
  public void run() 
  {
    super.run();
    
    //This crazy loop with a thread inside is because mServerSocket.accept()
    //pauses until a request is made. Once a request is made, it creates a 
    //thread to handle the request and then repeats the loop. The whole web
    //server should be run within a thread too. That is dealt with in the
    //WebServerService.java class
    Log.i(TAG, "Running main webserver thread");
    while (!interrupted()) {
      try {
        final Socket socket = mServerSocket.accept();
        Log.d(TAG, "Socket accepted");
        Thread t = new Thread() {
          @Override
          public void run() {
            handleRequest(socket);
          }
        };
        //This command ensures that this thread will be closed 
        //if the the parent thread crashes.
        t.setDaemon(true);
        t.start();
      } catch (IOException e) {
        Log.e(TAG, "Problem accepting socket " + e.toString());
      }
    }
    try {
      mServerSocket.close();
    } catch (IOException e) {
      Log.e(TAG, "Problem closing socket " + e.toString());
    }
  }

  /* Handles a single request. */
  public void handleRequest(Socket socket) 
  {
    try {
      DefaultHttpServerConnection serverConnection = new DefaultHttpServerConnection();
      serverConnection.bind(socket, new BasicHttpParams());
      HttpRequest request = serverConnection.receiveRequestHeader();
      RequestLine requestLine = request.getRequestLine();

      /* First make sure user is logged in if that is required. */
      boolean loggedIn = false;
      if (mSharedPreferences.getBoolean(
          BackgroundService.PREFS_REQUIRE_LOGIN, true)) {
        /* Does the user have a valid cookie? */
        Header cookiesHeader = request.getFirstHeader("Cookie");
        if (cookiesHeader != null) {
          String cookies = cookiesHeader.getValue();
          String cookie = cookies.substring(cookies.indexOf("id=")
              + "id=".length());
          loggedIn = isValidCookie(cookie);
        }
      } else {
        loggedIn = true;
      }

      if (!loggedIn) {
        /* Could be the result of the login form. */
        if (requestLine.getUri().equals("/login")) {
          handleLoginRequest(serverConnection, request, requestLine);
        } else {
          sendHTMLPage("login/form.html", serverConnection);
        }
      } else if (requestLine.getUri().equals("/")) {
        Log.i(TAG, "Default Page");
        sendHTMLPage("default.html", serverConnection);
      } else if (requestLine.getUri().startsWith("/webcam.jpg")) {
        Log.i(TAG, "Sending WebCam Image");
        sendWebCamImage(serverConnection);
      } else {
        doSomeSweetAction(serverConnection, request, requestLine);
      }
      serverConnection.flush();
      serverConnection.close();
    } catch (IOException e) {
      Log.e(TAG, "Problem with socket " + e.toString());
    } catch (HttpException e) {
      Log.e(TAG, "Problemw with HTTP server " + e.toString());
    }
  }
  
  /**
   * This function tries to load a servlet. If it can't find a servlet
   * then it tries to load a page from the root directory. If it still
   * can't find anything then it sends a "page not found" page
   * 
   * @param serverConnection
   * @param request
   * @param requestLine
   * @throws HttpException
   * @throws IOException
   */
  private void doSomeSweetAction(
        DefaultHttpServerConnection serverConnection, 
        HttpRequest request,
        RequestLine requestLine) throws HttpException, IOException
  {
    String data = null;
    //The string that is to the left of the question mark
    //and without the leading slash is the page name
    String uri = requestLine.getUri();
    //remove everything to the right of the question mark
    String pageName = uri.contains("?")?uri.split("\\?")[0]:uri;
    //remove everything to the right of the pound
    pageName = pageName.contains("#")?pageName.split("#")[0]:pageName;
    //replace the leading slash
    pageName = pageName.charAt(0)=='/'?pageName.replaceFirst("/", ""):pageName;
    
    //Attempt to load page as a servlet. If we can't 
    //load it as a servlet then we will attempt to load
    //the file within our root html directory
    Servlet servlet = servletLoader.loadServlet(SERVLET_PACKAGE + "." + pageName);
    if (servlet==null)
    {
      
      data = readFileFromHomeDirectory(pageName);
      if (data!=null)
      {
        Log.i(TAG, "Sending page - " + pageName);
        sendResponse(Servlet.getHTTPSuccessResponse(data), serverConnection);
      } else
      {
        //if data is null then we couldn't load from servlet or file. So
        //we send the page not found.
        Log.i(TAG, "No action for " + requestLine.getUri());
        sendNotFound(serverConnection);
      }
    } else 
    {
      //run servlet
      Log.i(TAG, "Runing servlet - " + SERVLET_PACKAGE + "." + pageName);
      HttpResponse response = servlet.runServlet(serverConnection, request);
      //send response
      sendResponse(response, serverConnection);
    }
  }

  private void handleLoginRequest(
      DefaultHttpServerConnection serverConnection, HttpRequest request,
      RequestLine requestLine) throws HttpException, IOException 
  {

    BasicHttpEntityEnclosingRequest enclosingRequest = new BasicHttpEntityEnclosingRequest(
        request.getRequestLine());
    serverConnection.receiveRequestEntity(enclosingRequest);

    InputStream input = enclosingRequest.getEntity().getContent();
    InputStreamReader reader = new InputStreamReader(input);

    StringBuffer form = new StringBuffer();
    while (reader.ready()) {
      form.append((char) reader.read());
    }
    String password = form.substring(form.indexOf("=") + 1);
    if (password.equals(mSharedPreferences.getString(
        BackgroundService.PREFS_PASSWORD, "Pa%24%24word"))) {
      HttpResponse response = new BasicHttpResponse(
          new HttpVersion(1, 1), 302, "Found");
      response.addHeader("Location", "/");
      response.addHeader("Set-Cookie", "id=" + createCookie());
      
      sendHTMLPage("login/success.html", response, serverConnection);
    } else {
      HttpResponse response = new BasicHttpResponse(
          new HttpVersion(1, 1), 401, "Unauthorized");
      
      sendHTMLPage("login/fail.html", response, serverConnection);
    }
  }

  private String createCookie() 
  {
    Random r = new Random();
    String value = Long.toString(Math.abs(r.nextLong()), 36);
    ContentValues values = new ContentValues();
    values.put("name", "id");
    values.put("value", value);
    values.put("expiry", (int) System.currentTimeMillis() / 1000
        + COOKIE_EXPIRY_SECONDS);
    mCookiesDatabase.insert("cookies", "name", values);
    return value;
  }

  private boolean isValidCookie(String cookie) 
  {
    Cursor cursor = mCookiesDatabase.query("cookies",
        new String[] { "value" },
        "name = ? and value = ? and expiry > ?", new String[] { "id",
            cookie, "" + (int) System.currentTimeMillis() / 1000 },
        null, null, null);
    boolean isValid = cursor.getCount() > 0;
    cursor.close();
    return isValid;
  }

  private void deleteOldCookies() 
  {
    mCookiesDatabase.delete("cookies", "expiry < ?", new String[] { ""
        + (int) System.currentTimeMillis() / 1000 });
  }

  private void sendNotFound(DefaultHttpServerConnection serverConnection)
      throws UnsupportedEncodingException, HttpException, IOException 
  {
    HttpResponse response = new BasicHttpResponse(new HttpVersion(1, 1),
        404, "NOT FOUND");
    response.setEntity(new StringEntity("NOT FOUND"));
    serverConnection.sendResponseHeader(response);
    serverConnection.sendResponseEntity(response);
  }
  
  private void sendHTMLPage(String page,
      DefaultHttpServerConnection serverConnection)
      throws UnsupportedEncodingException, HttpException, IOException 
  {
    sendHTMLPage(page, (Header)null, serverConnection);
  }
  
  private void sendHTMLPage(String page, Header header,
      DefaultHttpServerConnection serverConnection)
      throws UnsupportedEncodingException, HttpException, IOException 
  {
    HttpResponse response = new BasicHttpResponse(new HttpVersion(1, 1), 200,"OK");
    String body = readFileFromHomeDirectory(page);
    response.setEntity(new StringEntity(body));
    sendResponse(response, serverConnection);
  }
  
  private void sendHTMLPage(String page, HttpResponse response,
      DefaultHttpServerConnection serverConnection)
      throws UnsupportedEncodingException, HttpException, IOException 
  {
    String body = readFileFromHomeDirectory(page);
    response.setEntity(new StringEntity(body));
    sendResponse(response, serverConnection);
  }
  
  private void sendResponse(HttpResponse response,
      DefaultHttpServerConnection serverConnection)
      throws HttpException, IOException 
  {
    serverConnection.sendResponseHeader(response);
    serverConnection.sendResponseEntity(response);
  }
  
  private void sendWebCamImage(DefaultHttpServerConnection serverConnection) throws IOException, HttpException 
  {
     HttpResponse response = new BasicHttpResponse(new HttpVersion(1, 1), 200,"OK");
     addImageEntity(webCameraImg, response);
     serverConnection.sendResponseHeader(response);
     serverConnection.sendResponseEntity(response);
  }
  
  private void addImageEntity(byte[] img, HttpResponse response) throws IOException 
  {
    String contentType = "application/octet-stream";
    contentType = "image/jpg";
    if (img!=null)
    {
      response.addHeader("Content-Type", contentType);
      response.addHeader("Content-Length", "" + img.length);
      response.setEntity(new ByteArrayEntity(img));
    }
  }

//  private void sendLoginForm(DefaultHttpServerConnection serverConnection,
//      RequestLine requestLine) throws UnsupportedEncodingException,
//      HttpException, IOException 
//  {
//    HttpResponse response = new BasicHttpResponse(new HttpVersion(1, 1),
//        200, "OK");
//    response.setEntity(new StringEntity(getHTMLHeader()
//            + "<p>Password Required</p>" + getLoginForm()
//            + getHTMLFooter()));
//    serverConnection.sendResponseHeader(response);
//    serverConnection.sendResponseEntity(response);
//  }

  public static String readFileFromHomeDirectory(String strFileName)
  {
    String ret = "";
    try{
      
      File f = new File(Environment.getExternalStorageDirectory()+"/QuadCopter/" + strFileName);
      FileInputStream fileIS = new FileInputStream(f);
      BufferedReader buf = new BufferedReader(new InputStreamReader(fileIS));
      String readString = new String();
      while((readString = buf.readLine())!= null){
        ret += readString;
      }
      fileIS.close();
    } catch (Exception e) {
      ret = null;
    } 
    return ret;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.