Android Open Source - spydroid-ipcamera Tiny Http Server






From Project

Back to project page spydroid-ipcamera.

License

The source code is released under:

GNU General Public License

If you think the Android project spydroid-ipcamera listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2011-2013 GUIGUI Simon, fyhertz@gmail.com
 * /* w  w w  . j ava  2 s. c om*/
 * This file is part of Spydroid (http://code.google.com/p/spydroid-ipcamera/)
 * 
 * Spydroid is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This source code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Based on that: http://hc.apache.org/httpcomponents-core-ga/examples.html.
 * 
 */

package net.majorkernelpanic.http;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Date;
import java.util.LinkedList;
import java.util.Map;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.X509KeyManager;

import org.apache.http.ConnectionClosedException;
import org.apache.http.HttpException;
import org.apache.http.HttpServerConnection;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.DefaultHttpServerConnection;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestHandler;
import org.apache.http.protocol.HttpRequestHandlerRegistry;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
import org.apache.http.protocol.UriPatternMatcher;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Binder;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;

/**
 * 
 * A Service that contains a HTTP server and a HTTPS server.
 * 
 * Check out the github page of this project for further information.
 * 
 * You may add some logic to this server with {@link #addRequestHandler(String, HttpRequestHandler)}.
 * By default it serves files from /assets/www.
 * 
 */
public class TinyHttpServer extends Service {

  /** The tag used by the server. */
  public static final String TAG = "TinyHttpServer";

  /** Default port for HTTP. */
  public final static int DEFAULT_HTTP_PORT = 8080;

  /** Default port for HTTPS. */
  public final static int DEFAULT_HTTPS_PORT = 8443;
  
  /** Port already in use. */
  public final static int ERROR_HTTP_BIND_FAILED = 0x00;

  /** Port already in use. */
  public final static int ERROR_HTTPS_BIND_FAILED = 0x01;

  /** You need to add the class {@link ModSSL} to the package {@link net.majorkernelpanic.http}. */
  public final static int ERROR_HTTPS_NOT_SUPPORTED = 0x02;

  /** An error occured with the HTTPS server :( */
  public final static int ERROR_HTTPS_SERVER_CRASHED = 0x03;

  /** Key used in the SharedPreferences to store whether the HTTP server is enabled or not. */
  public final static String KEY_HTTP_ENABLED = "http_enabled";
  
  /** Key used in the SharedPreferences to store whether the HTTPS server is enabled or not. */
  public final static String KEY_HTTPS_ENABLED = "https_enabled";
  
  /** Key used in the SharedPreferences for the port used by the HTTP server. */
  public final static String KEY_HTTP_PORT = "http_port";
  
  /** Key used in the SharedPreferences for the port used by the HTTPS server. */
  public final static String KEY_HTTPS_PORT = "https_port";

  /** Key used in the SharedPreferences for storing the password of the keystore. */
  public final static String KEY_PASSWORD = "https_password";
  
  /** Name of the file used to store the keystore containing the certificates for HTTPS. */
  public final static String KEYSTORE_FILE_NAME = "keystore.jks";  
  
  /** 
   * Common name that will appear in the root certificate. 
   * You might want to extend {@link TinyHttpServer} to change that. 
   **/
  protected String mCACommonName = "TinyHttpServer CA";

  protected String[] MODULES = new String[] {
    "ModAssetServer",
    "ModInternationalization"
    };
  
  protected int mHttpPort = DEFAULT_HTTP_PORT;
  protected int mHttpsPort = DEFAULT_HTTPS_PORT;
  protected boolean mHttpEnabled = true, mHttpsEnabled = false;
  protected LinkedList<CallbackListener> mListeners = new LinkedList<CallbackListener>();
  
  private BasicHttpProcessor mHttpProcessor;
  private HttpParams mParams; 
  private HttpRequestListener mHttpRequestListener = null;
  private HttpsRequestListener mHttpsRequestListener = null;
  private SharedPreferences mSharedPreferences;
  private boolean mHttpsUpdate = false, mHttpUpdate = false;

  Date mLastModified;
  MHttpRequestHandlerRegistry mRegistry;
  Context mContext;
  
  /** Be careful: those callbacks won't necessarily be called from the ui thread ! */
  public interface CallbackListener {

    /** Called when an error occurs. */
    void onError(TinyHttpServer server, Exception e, int error);
    
    void onMessage(TinyHttpServer server, int message);    

  }

  /**
   * See {@link TinyHttpServer.CallbackListener} to check out what events will be fired once you set up a listener.
   * @param listener The listener
   */
  public void addCallbackListener(CallbackListener listener) {
    synchronized (mListeners) {
      mListeners.add(listener);      
    }
  }

  /**
   * Removes the listener.
   * @param listener The listener
   */
  public void removeCallbackListener(CallbackListener listener) {
    synchronized (mListeners) {
      mListeners.remove(listener);        
    }
  }  
  
  /** 
   * You may add some HttpRequestHandler to modify the default behavior of the server.
   * @param pattern Patterns may have three formats: * or *<uri> or <uri>*
   * @param handler A HttpRequestHandler
   */ 
  protected void addRequestHandler(String pattern, HttpRequestHandler handler) {
    mRegistry.register(pattern, handler);
  }
  
  /**
   * Sets the port for the HTTP server to use.
   * @param port The port to use
   */
  public void setHttpPort(int port) {
    Editor editor = mSharedPreferences.edit();
    editor.putString(KEY_HTTP_PORT, String.valueOf(port));
    editor.commit();
  }

  /**
   * Sets the port for the HTTPS server to use.
   * @param port The port to use
   */
  public void setHttpsPort(int port) {
    Editor editor = mSharedPreferences.edit();
    editor.putString(KEY_HTTPS_PORT, String.valueOf(port));
    editor.commit();
  }

  /** Enables the HTTP server. */
  public void setHttpEnabled(boolean enable) {
    Editor editor = mSharedPreferences.edit();
    editor.putBoolean(KEY_HTTP_ENABLED, enable);
    editor.commit();
  }

  /** Enables the HTTPS server. */  
  public void setHttpsEnabled(boolean enable) {
    Editor editor = mSharedPreferences.edit();
    editor.putBoolean(KEY_HTTPS_ENABLED, enable);
    editor.commit();
  }

  /** Returns the port used by the HTTP server. */  
  public int getHttpPort() {
    return mHttpPort;
  }

  /** Returns the port used by the HTTPS server. */
  public int getHttpsPort() {
    return mHttpsPort;
  }

  /** Indicates whether or not the HTTP server is enabled. */
  public boolean isHttpEnabled() {
    return mHttpEnabled;
  }

  /** Indicates whether or not the HTTPS server is enabled. */
  public boolean isHttpsEnabled() {
    return mHttpsEnabled;
  }  

  /** Starts (or restart if needed) the HTTP server. */
  public void start() {

    // Stops the HTTP server if it has been disabled or if it needs to be restarted
    if ((!mHttpEnabled || mHttpUpdate) && mHttpRequestListener != null) {
      mHttpRequestListener.kill();
      mHttpRequestListener = null;
    }
    // Stops the HTTPS server if it has been disabled or if it needs to be restarted
    if ((!mHttpsEnabled || mHttpsUpdate) && mHttpsRequestListener != null) {
      mHttpsRequestListener.kill();
      mHttpsRequestListener = null;
    }
    // Starts the HTTP server if needed
    if (mHttpEnabled && mHttpRequestListener == null) {
      try {
        mHttpRequestListener = new HttpRequestListener(mHttpPort);
      } catch (Exception e) {
        mHttpRequestListener = null;
      }
    }
    // Starts the HTTPS server if needed
    if (mHttpsEnabled && mHttpsRequestListener == null) {
      try {
        mHttpsRequestListener = new HttpsRequestListener(mHttpsPort);
      } catch (Exception e) {
        mHttpsRequestListener = null;
      }
    }

    mHttpUpdate = false;
    mHttpsUpdate = false;

  }

  /** Stops the HTTP server and/or the HTTPS server but not the Android service. */
  public void stop() {
    if (mHttpRequestListener != null) {
      // Stops the HTTP server
      mHttpRequestListener.kill();
      mHttpRequestListener = null;
    }
    if (mHttpsRequestListener != null) {
      // Stops the HTTPS server
      mHttpsRequestListener.kill();
      mHttpsRequestListener = null;
    }
  }
  
  @Override
  public void onCreate() {
    super.onCreate();

    mContext = getApplicationContext();
    mRegistry = new MHttpRequestHandlerRegistry();
    mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);

    mParams = new BasicHttpParams();
    mParams
    .setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000)
    .setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
    .setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
    .setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
    .setParameter(CoreProtocolPNames.ORIGIN_SERVER, "MajorKernelPanic HTTP Server");

    // Set up the HTTP protocol processor
    mHttpProcessor = new BasicHttpProcessor();
    mHttpProcessor.addInterceptor(new ResponseDate());
    mHttpProcessor.addInterceptor(new ResponseServer());
    mHttpProcessor.addInterceptor(new ResponseContent());
    mHttpProcessor.addInterceptor(new ResponseConnControl());

    // Will be used in the "Last-Modifed" entity-header field
    try {
      String packageName = mContext.getPackageName();
      mLastModified = new Date(mContext.getPackageManager().getPackageInfo(packageName, 0).lastUpdateTime);
    } catch (NameNotFoundException e) {
      mLastModified = new Date(0);
    }

    // Restores the state of the service 
    mHttpPort = Integer.parseInt(mSharedPreferences.getString(KEY_HTTP_PORT, String.valueOf(mHttpPort)));
    mHttpsPort = Integer.parseInt(mSharedPreferences.getString(KEY_HTTPS_PORT, String.valueOf(mHttpsPort)));
    mHttpEnabled = mSharedPreferences.getBoolean(KEY_HTTP_ENABLED, mHttpEnabled);
    mHttpsEnabled = mSharedPreferences.getBoolean(KEY_HTTPS_ENABLED, mHttpsEnabled);

    // If the configuration is modified, the server will adjust
    mSharedPreferences.registerOnSharedPreferenceChangeListener(mOnSharedPreferenceChangeListener);

    // Loads plugins available in the package net.majorkernelpanic.http
    for (int i=0; i<MODULES.length; i++) {
      try {
        Class<?> pluginClass = Class.forName(TinyHttpServer.class.getPackage().getName()+"."+MODULES[i]);
        Constructor<?> pluginConstructor = pluginClass.getConstructor(new Class[]{TinyHttpServer.class});
        addRequestHandler((String) pluginClass.getField("PATTERN").get(null), (HttpRequestHandler)pluginConstructor.newInstance(this));
      } catch (ClassNotFoundException ignore) {
        // Module disabled
      } catch (Exception e) {
        Log.e(TAG, "Bad module: "+MODULES[i]);
        e.printStackTrace();
      }
    }
    
    start();
    
  }

  @Override
  public void onDestroy() {
    stop();
    mSharedPreferences.unregisterOnSharedPreferenceChangeListener(mOnSharedPreferenceChangeListener);
  }
  
  @Override
  public int onStartCommand (Intent intent, int flags, int startId) {
    //Log.d(TAG,"TinyServerHttp started !");
    return START_STICKY;
  }
  
  private OnSharedPreferenceChangeListener mOnSharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {

      if (key.equals(KEY_HTTP_PORT)) {
        int port = Integer.parseInt(sharedPreferences.getString(KEY_HTTP_PORT, String.valueOf(mHttpPort)));
        if (port != mHttpPort) {
          mHttpPort = port;
          mHttpUpdate = true;
          start();
        }
      }

      else if (key.equals(KEY_HTTPS_PORT)) {
        int port = Integer.parseInt(sharedPreferences.getString(KEY_HTTPS_PORT, String.valueOf(mHttpsPort)));
        if (port != mHttpsPort) {
          mHttpsPort = port;
          mHttpsUpdate = true;
          start();
        }
      }

      else if (key.equals(KEY_HTTPS_ENABLED)) {
        mHttpsEnabled = sharedPreferences.getBoolean(KEY_HTTPS_ENABLED, true);
        start();
      }      

      else if (key.equals(KEY_HTTP_ENABLED)) {
        mHttpEnabled = sharedPreferences.getBoolean(KEY_HTTP_ENABLED, true);
        start();
      }
    }
  };

  /** The Binder you obtain when a connection with the Service is established. */
  public class LocalBinder extends Binder {
    public TinyHttpServer getService() {
      return TinyHttpServer.this;
    }
  }

  /** See {@link TinyHttpServer.LocalBinder}. */
  @Override
  public IBinder onBind(Intent intent) {
    return mBinder;
  }

  private final IBinder mBinder = new LocalBinder();

  protected void postError(Exception exception, int id) {
    synchronized (mListeners) {
      if (mListeners.size() > 0) {
        for (CallbackListener cl : mListeners) {
          cl.onError(this, exception, id);
        }
      }      
    }
  }
  
  protected void postMessage(int id) {
    synchronized (mListeners) {
      if (mListeners.size() > 0) {
        for (CallbackListener cl : mListeners) {
          cl.onMessage(this, id);
        }
      }      
    }
  }  
  
  protected class HttpRequestListener extends RequestListener {

    public HttpRequestListener(final int port) throws Exception {
      try {
        ServerSocket serverSocket = new ServerSocket(port);
        construct(serverSocket);
        Log.i(TAG,"HTTP server listening on port " + serverSocket.getLocalPort());
      } catch (BindException e) {
        postError(e, ERROR_HTTP_BIND_FAILED);
        throw e;
      }
    }

    protected void kill() {
      super.kill();
      Log.i(TAG,"HTTP server stopped !");
    }

  }

  protected class HttpsRequestListener extends RequestListener {

    private X509KeyManager mKeyManager = null;
    private char[] mPassword;
    private boolean mNotSupported = false;

    private final String mClasspath = TinyHttpServer.class.getPackage().getName()+".ModSSL$X509KeyManager";

    public HttpsRequestListener(final int port) throws Exception {

      SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(TinyHttpServer.this);
      
      if (!settings.contains(KEY_PASSWORD)) {
        // Generates a password for the keystore
        // TODO: entropy of Math.random() ?
        String password = Integer.toString((int) (Math.random() * Integer.MAX_VALUE), 36);
        Editor editor = settings.edit();
        editor.putString(KEY_PASSWORD, password);
        editor.commit();
        mPassword = password.toCharArray();
        mContext.deleteFile(KEYSTORE_FILE_NAME);
      } else {
        mPassword = settings.getString(KEY_PASSWORD, "XX").toCharArray();
      }

      // We create the X509KeyManager through reflexion so that SSL support can easily be removed if not needed
      try {
        Class<?> X509KeyManager = Class.forName(mClasspath);
        Method loadFromKeyStore = X509KeyManager.getDeclaredMethod("loadFromKeyStore", InputStream.class, char[].class);

        try {
          InputStream is = mContext.openFileInput(KEYSTORE_FILE_NAME);
          mKeyManager = (X509KeyManager) loadFromKeyStore.invoke(null, is, mPassword);
        } catch (FileNotFoundException e) {
        } catch (Exception e) {
          Log.e(TAG,"Could not open keystore, a new one will be created...");
          e.printStackTrace();
        }

        if (mKeyManager == null) {
          Constructor<?> constructor = X509KeyManager.getConstructor(new Class[]{char[].class, String.class});
          mKeyManager = (javax.net.ssl.X509KeyManager) constructor.newInstance(mPassword, mCACommonName);          
        }
        
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(new KeyManager[] {mKeyManager}, null, null);
        SSLServerSocket serverSocket = (SSLServerSocket) sslContext.getServerSocketFactory().createServerSocket(port);
        
        serverSocket.setUseClientMode(false);
        serverSocket.setEnableSessionCreation(true);
        serverSocket.setWantClientAuth(false);
        
        Log.d(TAG, "Protocol: "+sslContext.getProtocol());
        Log.d(TAG, "Provider: "+sslContext.getProvider());
        Log.d(TAG, "Cipher suites: "+arrToString(serverSocket.getEnabledCipherSuites()));
        Log.d(TAG, "Protocols enabled: "+arrToString(serverSocket.getEnabledProtocols()));
        
        serverSocket.setEnabledProtocols(new String[]{"TLSv1"});
        serverSocket.setEnabledCipherSuites(new String[]{"TLS_RSA_WITH_AES_128_CBC_SHA"});
        Log.d(TAG, "Cipher suites: "+arrToString(serverSocket.getEnabledCipherSuites()));
        Log.d(TAG, "Protocols enabled: "+arrToString(serverSocket.getEnabledProtocols()));
        
        construct(serverSocket);
        Log.i(TAG,"HTTPS server listening on port " + serverSocket.getLocalPort());

      } catch (NoSuchMethodException e) {
        // HTTPS support disabled !
        Log.e(TAG,"HTTPS not supported !");
        postError(e, ERROR_HTTPS_NOT_SUPPORTED);
        throw e;
      } catch (BindException e) {
        postError(e, ERROR_HTTPS_BIND_FAILED);
        throw e;
      } catch (Exception e) {
        Log.e(TAG,"HTTPS server crashed !");
        e.printStackTrace();
        postError(e, ERROR_HTTPS_SERVER_CRASHED);
        throw e;
      }

    }

    private String arrToString(String[] list) {
      String str="";
      for (int i=0;i<list.length;i++) {
        str+=list[i]+";";
      }
      return str;
    }
    
    /** Stops the {@link TinyHttpServer.RequestListener} */
    protected void kill() {
      if (!mNotSupported) {
        super.kill();
        // Saves all the certificates generated by the our KeyManager in a keystore
        try {
          Method saveToKeyStore = Class.forName(mClasspath).getDeclaredMethod("saveToKeyStore", OutputStream.class, char[].class);
          // Prevents concurrent write operation in the keystore  
          OutputStream os = mContext.openFileOutput(KEYSTORE_FILE_NAME, Context.MODE_PRIVATE); 
          saveToKeyStore.invoke(mKeyManager, os, mPassword);
        } catch (NoSuchMethodException e) {
          // HTTPS support disabled !
          Log.e(TAG,"HTTPS not supported !");
          postError(e, ERROR_HTTPS_NOT_SUPPORTED);
        } catch (Exception e) {
          System.out.println("An error occured while saving the KeyStore");
          e.printStackTrace();
        }
        Log.i(TAG,"HTTPS server stopped !");
      }
    }

  }

  private class RequestListener extends Thread {

    private ServerSocket mServerSocket;
    private final org.apache.http.protocol.HttpService mHttpService;

    protected RequestListener() throws Exception {

      mHttpService = new org.apache.http.protocol.HttpService(
          mHttpProcessor, 
          new DefaultConnectionReuseStrategy(), 
          new DefaultHttpResponseFactory());
      mHttpService.setHandlerResolver(mRegistry);
      mHttpService.setParams(mParams);

    }

    protected void construct(ServerSocket serverSocket) {
      mServerSocket = serverSocket;
      start();
    }

    protected void kill() {
      try {
        mServerSocket.close();
      } catch (IOException ignore) {}
      try {
        this.join();
      } catch (InterruptedException ignore) {}
    }

    public void run() {
      while (!Thread.interrupted()) {
        try {
          // Set up HTTP connection
          Socket socket = this.mServerSocket.accept();
          DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
          Log.d(TAG,"Incoming connection from " + socket.getInetAddress());
          conn.bind(socket, mParams);

          // Start worker thread
          Thread t = new WorkerThread(this.mHttpService, conn, socket);
          t.setDaemon(true);
          t.start();
        } catch (SocketException e) {
          break;
        } catch (InterruptedIOException ex) {
          Log.e(TAG,"Interrupted !");
          break;
        } catch (IOException e) {
          Log.d(TAG,"I/O error initialising connection thread: " + e.getMessage());
          break;
        }
      }
    }
  }

  static class WorkerThread extends Thread {

    private final org.apache.http.protocol.HttpService httpservice;
    private final HttpServerConnection conn;
    private final Socket socket;

    public WorkerThread(
        final org.apache.http.protocol.HttpService httpservice, 
        final HttpServerConnection conn,
        final Socket socket) {
      super();
      this.httpservice = httpservice;
      this.conn = conn;
      this.socket = socket;
    }

    public void run() {
      Log.d(TAG,"New connection thread");
      HttpContext context = new MHttpContext(socket);
      try {
        while (!Thread.interrupted() && this.conn.isOpen()) {
          try {
            this.httpservice.handleRequest(this.conn, context);
          } catch (UnsupportedOperationException e) {
            e.printStackTrace();
            // shutdownOutput is not implemented by SSLSocket, and it is called in the implementation
            // of org.apache.http.impl.SocketHttpServerConnection.close().
          }
        }
      } catch (ConnectionClosedException e) {
        Log.d(TAG,"Client closed connection");
        e.printStackTrace();
      } catch (SocketTimeoutException e) {
        Log.d(TAG,"Socket timeout");
      } catch (IOException e) {
        Log.e(TAG,"I/O error: " + e.getMessage());
      } catch (HttpException e) {
        Log.e(TAG,"Unrecoverable HTTP protocol violation: " + e.getMessage());
      } finally {
        try {
          OutputStream sockOutOStream = socket.getOutputStream();
          sockOutOStream.write(new byte[0]);
          sockOutOStream.flush();
          socket.close();
        } catch (IOException e) {
        }
        try {
          this.conn.shutdown();
        } catch (Exception ignore) {}
      }
    }
  }

  /** Little modification of BasicHttpContext to add access to the underlying tcp socket. */
  public static class MHttpContext extends BasicHttpContext {

    private Socket socket;

    public MHttpContext(Socket socket) {
      super(null);
      this.socket = socket;
    }

    /** Returns a reference to the underlying socket of the connection. */
    public Socket getSocket() {
      return socket;
    }

  }

  /**
   * A slightly modified version of org.apache.http.protocol.HttpRequestHandlerRegistry 
   * that allows the registry to be modified while some threads may be using it.
   * Recent versions of this file are thread-safe, but Android seems to be using an old version. 
   */
  public static class MHttpRequestHandlerRegistry extends HttpRequestHandlerRegistry {

    private final UriPatternMatcher matcher;

    public MHttpRequestHandlerRegistry() {
      matcher = new UriPatternMatcher();
    }

    public synchronized void register(final String pattern, final HttpRequestHandler handler) {
      matcher.register(pattern, handler);
    }

    public synchronized void unregister(final String pattern) {
      matcher.unregister(pattern);
    }

    public synchronized void setHandlers(@SuppressWarnings("rawtypes") final Map map) {
      matcher.setHandlers(map);
    }

    public synchronized HttpRequestHandler lookup(final String requestURI) {
      // This is the only function that will often be called by threads of the HTTP server
      // and it seems like a rather small crtical section to me, so it should not slow things down much
      return (HttpRequestHandler) matcher.lookup(requestURI);
    }

  }

}




Java Source Code List

net.majorkernelpanic.http.ModAssetServer.java
net.majorkernelpanic.http.ModInternationalization.java
net.majorkernelpanic.http.ModSSL.java
net.majorkernelpanic.http.TinyHttpServer.java
net.majorkernelpanic.spydroid.SpydroidApplication.java
net.majorkernelpanic.spydroid.Utilities.java
net.majorkernelpanic.spydroid.api.CustomHttpServer.java
net.majorkernelpanic.spydroid.api.CustomRtspServer.java
net.majorkernelpanic.spydroid.api.RequestHandler.java
net.majorkernelpanic.spydroid.ui.AboutFragment.java
net.majorkernelpanic.spydroid.ui.HandsetFragment.java
net.majorkernelpanic.spydroid.ui.OptionsActivity.java
net.majorkernelpanic.spydroid.ui.PreviewFragment.java
net.majorkernelpanic.spydroid.ui.SpydroidActivity.java
net.majorkernelpanic.spydroid.ui.TabletFragment.java
net.majorkernelpanic.streaming.MediaStream.java
net.majorkernelpanic.streaming.SessionBuilder.java
net.majorkernelpanic.streaming.Session.java
net.majorkernelpanic.streaming.Stream.java
net.majorkernelpanic.streaming.audio.AACStream.java
net.majorkernelpanic.streaming.audio.AMRNBStream.java
net.majorkernelpanic.streaming.audio.AudioQuality.java
net.majorkernelpanic.streaming.audio.AudioStream.java
net.majorkernelpanic.streaming.exceptions.CameraInUseException.java
net.majorkernelpanic.streaming.exceptions.ConfNotSupportedException.java
net.majorkernelpanic.streaming.exceptions.InvalidSurfaceException.java
net.majorkernelpanic.streaming.exceptions.StorageUnavailableException.java
net.majorkernelpanic.streaming.gl.SurfaceManager.java
net.majorkernelpanic.streaming.gl.SurfaceView.java
net.majorkernelpanic.streaming.gl.TextureManager.java
net.majorkernelpanic.streaming.hw.CodecManager.java
net.majorkernelpanic.streaming.hw.EncoderDebugger.java
net.majorkernelpanic.streaming.hw.NV21Convertor.java
net.majorkernelpanic.streaming.mp4.MP4Config.java
net.majorkernelpanic.streaming.mp4.MP4Parser.java
net.majorkernelpanic.streaming.rtcp.SenderReport.java
net.majorkernelpanic.streaming.rtp.AACADTSPacketizer.java
net.majorkernelpanic.streaming.rtp.AACLATMPacketizer.java
net.majorkernelpanic.streaming.rtp.AMRNBPacketizer.java
net.majorkernelpanic.streaming.rtp.AbstractPacketizer.java
net.majorkernelpanic.streaming.rtp.H263Packetizer.java
net.majorkernelpanic.streaming.rtp.H264Packetizer.java
net.majorkernelpanic.streaming.rtp.MediaCodecInputStream.java
net.majorkernelpanic.streaming.rtp.RtpSocket.java
net.majorkernelpanic.streaming.rtsp.RtspClient.java
net.majorkernelpanic.streaming.rtsp.RtspServer.java
net.majorkernelpanic.streaming.rtsp.UriParser.java
net.majorkernelpanic.streaming.video.CodecManager.java
net.majorkernelpanic.streaming.video.H263Stream.java
net.majorkernelpanic.streaming.video.H264Stream.java
net.majorkernelpanic.streaming.video.VideoQuality.java
net.majorkernelpanic.streaming.video.VideoStream.java