Package com.dbxml.labrador.http

Source Code of com.dbxml.labrador.http.HTTPServerBase$Worker

package com.dbxml.labrador.http;

/*
* The dbXML Labrador Software License, Version 1.0
*
*
* Copyright (c) 2003 The dbXML Group, L.L.C.  All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*       "This product includes software developed by The
*        dbXML Group, L.L.C. (http://www.dbxml.com/)."
*    Alternately, this acknowledgment may appear in the software
*    itself, if and wherever such third-party acknowledgments normally
*    appear.
*
* 4. The names "Labrador" and "dbXML Group" must not be used to
*    endorse or promote products derived from this software without
*    prior written permission. For written permission, please contact
*    info@dbxml.com
*
* 5. Products derived from this software may not be called "Labrador",
*    nor may "Labrador" appear in their name, without prior written
*    permission of The dbXML Group, L.L.C..
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE DBXML GROUP, L.L.C. OR ITS
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* $Id: HTTPServerBase.java,v 1.22 2008/08/27 17:52:59 bradford Exp $
*/

import java.io.*;
import java.util.*;

import com.dbxml.labrador.Endpoint;
import com.dbxml.labrador.Handler;
import com.dbxml.labrador.Headers;
import com.dbxml.labrador.Request;
import com.dbxml.labrador.Response;
import com.dbxml.labrador.broker.Broker;
import com.dbxml.labrador.exceptions.AuthException;
import com.dbxml.labrador.exceptions.HandlerException;
import com.dbxml.labrador.exceptions.InstanceException;
import com.dbxml.labrador.exceptions.RequestException;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLDecoder;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;

/**
* HTTPServerBase serves as the abstract foundation for Labrador HTTP server
* implementations.  Currently, there are two implementations.  One is a
* non-blocking (Java 1.4.1) standard HTTP server.  The other is a blocking
* (Java 1.3.1) secure HTTP server.
* <br /><br />
* This servers are implemented as Daemon Threads, so in order to keep the
* VM alive, you'll have to either call setDaemon(false) or you'll need to
* have some other Thread keeping the VM alive.
*/

public abstract class HTTPServerBase extends Thread implements Endpoint {
   private static final String[] EmptyStrings = new String[0];
   private static final byte[] EmptyBytes = new byte[0];

   private static final int BUFFER_SIZE = 8192;

   protected static final SimpleDateFormat GDF = new SimpleDateFormat("EEE, dd MMM yyyy kk:mm:ss 'GMT'", new DateFormatSymbols(Locale.US));
   protected static final String APP_FORM = "application/x-www-form-urlencoded";

   protected Broker broker;

   protected int port;
   protected String hostName;
   protected int linger = 15;
   protected int timeout = 60000;
   protected int maxPostSize = 32768;

   protected URL url;

   public HTTPServerBase() {
      super();
      setName(getThreadName());
      setDaemon(true);
      port = getDefaultPort();
      broker = Broker.getInstance();
   }

   public HTTPServerBase(String url) {
      try {
         setURL(new URL(url));
      }
      catch ( Exception e ) {
         /** @todo This */
         e.printStackTrace(System.err);
         System.exit(1);
      }
   }

   public HTTPServerBase(URL url) {
      setURL(url);
   }

   protected abstract String getThreadName();
   protected abstract String getProtocol();
   protected abstract int getDefaultPort();
   protected abstract void createServerSocket(InetAddress bindaddr) throws Exception;
   protected abstract void acceptConnections() throws Exception;
   public abstract void shutdown();

   private void setURL(URL url) {
      setHostName(url.getHost());
      setPort(url.getPort());
   }

   public URL getBaseURL() {
      if ( url == null ) {
         StringBuffer sb = new StringBuffer();
         sb.append(getProtocol());
         sb.append("://");
         try {
            if ( hostName != null )
               sb.append(hostName);
            else
               sb.append(InetAddress.getLocalHost().getHostAddress());
            sb.append(':');
            sb.append(port);
            sb.append('/');
            url = new URL(sb.toString());
         }
         catch ( Exception e ) {
            e.printStackTrace(System.err);
            System.exit(1);
         }
      }
      return url;
   }

   /**
    * getHostName returns the host name that this server will bind
    * to when it is started.  This value may return null if the
    * server is set to bind to all local interfaces.
    *
    * @return The binding host name
    */
   public String getHostName() {
      return hostName;
   }

   /**
    * setHostName sets the host name that this server will bind
    * to when it is started.  This value may return null if the
    * server is set to bind to all local interfaces.
    *
    * @param hostName The binding host name
    */
   public void setHostName(String hostName) {
      this.hostName = hostName;
      url = null;
   }

   /**
    * setPort returns the port that this server will bind to when it
    * is started.  By default, this value is 1980.
    *
    * @return The binding port number
    */
   public int getPort() {
      return port;
   }

   /**
    * setPort sets the port that this server will bind to when it
    * is started.  By default, this value is 1980.
    *
    * @param port The binding port
    */
   public void setPort(int port) {
      this.port = port;
      url = null;
   }

   /**
    * getTimeout returns the length of the inactivity timeout for
    * connections.  This value is in milliseconds, and the default
    * value is 60000 (1 minute).
    *
    * @return The inactivity timeout
    */
   public int getTimeout() {
      return timeout;
   }

   /**
    * setTimeout sets the length of the inactivity timeout for
    * connections.  This value is in milliseconds, and the default
    * value is 60000 (1 minute).
    *
    * @param timeout The inactivity timeout
    */
   public void setTimeout(int timeout) {
      this.timeout = timeout;
   }

   /**
    * getLinger returns the linger timeout for connections.  The
    * linger timeout is the amount of time that the connection is
    * kept open after it is closed.  This value is in seconds,
    * and the default value is 15.
    *
    * @return The linger timeout
    */
   public int getLinger() {
      return linger;
   }

   /**
    * setLinger sets the linger timeout for connections.  The
    * linger timeout is the amount of time that the connection is
    * kept open after it is closed.  This value is in seconds,
    * and the default value is 15.
    *
    * @param linger The linger timeout
    */
   public void setLinger(int linger) {
      this.linger = linger;
   }

   /**
    * getMaxPostSize returns the maximum size of posted data that
    * cam remain in memory.  Any post larger than this treshhold is
    * streamed to a temporary file.
    *
    * @return The maximum post size
    */
   public int getMaxPostSize() {
      return maxPostSize;
   }

   /**
    * setMaxPostSize sets the maximum size of posted data that
    * cam remain in memory.  Any post larger than this treshhold is
    * streamed to a temporary file.
    *
    * @param maxPostSize The maximum post size
    */
   public void setMaxPostSize(int maxPostSize) {
      this.maxPostSize = maxPostSize;
   }

   public synchronized void run() {
      try {
         InetAddress bindaddr;
         if ( hostName != null && hostName.length() > 0 ) {
            bindaddr = InetAddress.getByName(hostName);
            createServerSocket(bindaddr);
         }
         else {
            bindaddr = InetAddress.getLocalHost();
            hostName = bindaddr.getHostName();
            createServerSocket(null);
         }

         notify();

         acceptConnections();
      }
      catch ( Exception e ) {
         Broker.printerr("Error Starting Labrador HTTP Server");
         e.printStackTrace(System.err);
      }
   }


   /**
    * Worker
    */

   protected abstract class Worker extends Thread implements Request, Response {
      protected byte[] postdata;
      protected File postfile;
      protected String version;

      protected String requestMethod;
      protected String pathInfo;
      protected String queryString;
      protected String remoteHost;
      protected boolean keepalive;

      protected Map resHdr = new HashMap();
      protected Map reqHdr = new HashMap();
      protected Map values = new HashMap();
      protected Map properties = Collections.synchronizedMap(new HashMap());

      protected ByteArrayOutputStream content = new ByteArrayOutputStream(2048);

      public Worker() {
         super();
         setName(getThreadName());
         setDaemon(true);
      }

      protected abstract String getThreadName();
      protected abstract void handleConnection();
      public abstract void send(int code, String message);

      public void run() {
         handleConnection();
      }

      protected void initRequest() {
         resHdr.clear();
         reqHdr.clear();
         values.clear();

         content.reset();

         postdata = null;
         postfile = null;

         resHdr.put(HTTP.HEADER_DATE, GDF.format(new Date()));
         resHdr.put(HTTP.HEADER_SERVER, "Labrador." + Broker.PRODUCT_VERSION);
         resHdr.put(HTTP.HEADER_CONTENT_TYPE, Headers.TYPE_TEXT_HTML);
         resHdr.put(HTTP.HEADER_CACHE_CONTROL, HTTP.VALUE_NO_CACHE);
      }

      protected void readRequest(InputStream is) throws Exception {
         // Parse the header out
         DataInputStream dis = new DataInputStream(new BufferedInputStream(is, BUFFER_SIZE));
         String rline;  // Line read from Socket Input Stream

         // Get the first line of the request
         rline = dis.readLine().replace('\t', ' ').trim();

         int idx = rline.indexOf(' ');
         if ( idx != -1 ) {
            requestMethod = rline.substring(0, idx).trim();
            rline = rline.substring(idx + 1);
         }
         else {
            requestMethod = rline;
            rline = "";
         }

         idx = rline.lastIndexOf(' ');
         if ( idx != -1 ) {
            version = rline.substring(idx + 1).trim().toUpperCase();
            if ( version.startsWith("HTTP/") )
               rline = rline.substring(0, idx);
            else
               version = "";
         }
         else
            version = "";

         idx = rline.indexOf('?');
         if ( idx != -1 ) {
            queryString = rline.substring(idx + 1).trim();
            rline = rline.substring(0, idx);
         }
         else
            queryString = "";

         pathInfo = java.net.URLDecoder.decode(rline.trim());

         if ( version.length() != 0 ) {
            // Get the header values, parse them and put them into the Client table
            String line;
            do {
               line = dis.readLine();
               if ( line == null )
                  line = "";
               idx = line.indexOf(':');
               if ( idx != -1 ) {
                  String key = line.substring(0, idx).trim();
                  String value = line.substring(idx + 1).trim();
                  reqHdr.put(key.toUpperCase(), value);
               }
            }
            while ( line.length() > 0 );
         }

         properties.put(HTTP.REQUEST_METHOD, requestMethod);
         properties.put(HTTP.REMOTE_HOST,  remoteHost);
         properties.put(HTTP.QUERY_STRING, queryString);

         if ( version.length() == 0 || version.equals("HTTP/1.0") )
            keepalive = getHeader(HTTP.HEADER_CONNECTION).equalsIgnoreCase(HTTP.VALUE_KEEP_ALIVE);
         else
            keepalive = !getHeader(HTTP.HEADER_CONNECTION).equalsIgnoreCase(HTTP.VALUE_CLOSE);

         if ( keepalive )
            resHdr.put(HTTP.HEADER_CONNECTION, HTTP.VALUE_KEEP_ALIVE);
         else
            resHdr.put(HTTP.HEADER_CONNECTION, HTTP.VALUE_CLOSE);

         // Parse out the form variables from the QUERY_STRING variable
         parseBuffer(values, queryString);

         // Everything after this point will be post data if there is any
         String cl = getHeader(HTTP.HEADER_CONTENT_LENGTH);
         if ( cl.length() > 0 ) {
            int postlen = Integer.parseInt(cl);
            if ( postlen > 0 ) {
               boolean form = getHeader(HTTP.HEADER_CONTENT_TYPE).equalsIgnoreCase(APP_FORM);
               if ( form || postlen <= maxPostSize ) {
                  // Store the post data in memory
                  byte[] buffer = new byte[postlen];

                  int count = 0;
                  int totalCount = 0;
                  while ( totalCount < postlen ) {
                     count = dis.read(buffer, totalCount, postlen-totalCount);
                     totalCount += count;
                  }

                  if ( form )
                     parseBuffer(values, new String(buffer));
                  else
                     postdata = buffer;
               }
               else {
                  // Store the post data in a temporary file
                  postfile = File.createTempFile("labrador", "tmp");

                  int count = 0;
                  int totalCount = 0;
                  byte[] buffer = new byte[maxPostSize];
                  FileOutputStream os = new FileOutputStream(postfile);
                  while ( totalCount < postlen ) {
                     count = dis.read(buffer);
                     if ( count > 0 ) {
                        os.write(buffer, 0, count);
                        totalCount += count;
                     }
                  }
                  os.flush();
                  os.close();
               }
            }
         }
      }

      protected void processRequest() {
         try {
            broker.processRequest(this, this);
            send(HTTP.CODE_OK, HTTP.STATUS_OK);
         }
         catch ( HandlerException e ) {
            sendError(HTTP.CODE_NOT_FOUND, Headers.STATUS_NO_HANDLER, e);
         }
         catch ( InstanceException e ) {
            sendError(HTTP.CODE_NOT_FOUND, Headers.STATUS_NO_INSTANCE, e);
         }
         catch ( AuthException e ) {
            setHeader(HTTP.HEADER_WWW_AUTHENTICATE, e.getType()+" realm=\""+e.getRealm()+"\"");
            sendError(HTTP.CODE_AUTHORIZATION_REQUIRED, HTTP.STATUS_AUTHORIZATION_REQUIRED);
         }
         catch ( HTTPException e ) {
            Properties hdrs = e.getHeaders();
            if ( hdrs != null ) {
               Iterator iter = hdrs.keySet().iterator();
               while ( iter.hasNext() ) {
                  String name = (String)iter.next();
                  String value = hdrs.getProperty(name);
                  setHeader(name, value);
               }
            }
            if ( e.getMessage() != null )
               sendError(e.getCode(), e.getStatus(), e);
            else
               sendEmptyError(e.getCode(), e.getStatus());
         }
         catch ( RequestException e ) {
            sendError(HTTP.CODE_INTERNAL_SERVER_ERROR, Headers.STATUS_PROCESSING_ERROR, e);
         }
         catch ( Exception e ) {
            Broker.printwarning("Unhandled Exception");
            e.printStackTrace(System.err);
            sendError(HTTP.CODE_INTERNAL_SERVER_ERROR, Headers.STATUS_EXECUTION_ERROR, e);
         }
         finally {
            if ( postfile != null ) {
               try {
                  postfile.delete();
               }
               catch ( Exception e ) {
                  e.printStackTrace(System.err);
               }
            }
         }
      }

      public Map getProperties() {
         return properties;
      }

      public Endpoint getEndpoint() {
         return HTTPServerBase.this;
      }

      public void close() {
         // NOOP
      }

      public String getPath() {
         return pathInfo;
      }

      public String getMethod() {
         return requestMethod;
      }

      public String getQueryString() {
         return queryString;
      }

      public String getHostAddress() {
         return remoteHost;
      }

      public boolean hasContent() {
         return postfile != null || postdata != null;
      }

      public String getValue(String name) {
         List vals = (List)values.get(name.toUpperCase());
         if ( vals != null )
            return (String)vals.get(0);
         else
            return null;
      }

      public String[] getValues(String name) {
         List vals = (List)values.get(name.toUpperCase());
         if ( vals != null )
            return (String[])vals.toArray(EmptyStrings);
         else
            return null;
      }

      public void setHeader(String name, String value) {
         resHdr.put(name, value);
      }

      public String getHeader(String name) {
         String value = (String)reqHdr.get(name.toUpperCase());
         if ( value != null )
            return value;
         else
            return "";
      }

      public OutputStream getOutputStream() {
         return content;
      }

      public InputStream getInputStream() {
         try {
            if ( postfile != null ) {
               FileInputStream fis = new FileInputStream(postfile);
               return new BufferedInputStream(fis, BUFFER_SIZE);
            }
            else if ( postdata != null )
               return new ByteArrayInputStream(postdata);
            else
               return new ByteArrayInputStream(EmptyBytes);
         }
         catch ( Exception e ) {
            return null;
         }
      }

      protected void writeHeaderContent(PrintStream ps, int code, String message) {
         try {
            if ( version.length() != 0 ) {
               ps.print(version);
               ps.print(' ');
               ps.print(code);
               if ( message != null ) {
                  ps.print(' ');
                  ps.print(message);
               }
               ps.println();
            }
            Iterator iter = resHdr.keySet().iterator();
            while ( iter.hasNext() ) {
               String key = (String)iter.next();
               ps.print(key);
               ps.print(": ");
               ps.print(resHdr.get(key));
               ps.println();
            }
            ps.println();
         }
         catch ( Exception e ) {
            // No Exception Should Occur Here
         }
      }

      protected void addFooter(PrintWriter out) {
         out.println("<hr align=\"center\" size=\"1\" noshade>");
         out.println("<p><font face=\"Arial, Helvetica, sans-serif\"><b>" + Broker.PRODUCT_NAME + "</b> " + Broker.PRODUCT_VERSION + "<br />");
         out.println("&copy; " + Broker.COPYRIGHT_YEAR + " <a href=\"" + Broker.AUTHOR_URL + "\" target=\"_new\">" + Broker.AUTHOR_NAME + "</a><br />");
         out.println("All Rights Reserved</font></p>");
      }

      protected void send(OutputStream os, int code, String message) throws Exception {
         byte[] b = content.toByteArray();
         resHdr.put(HTTP.HEADER_CONTENT_LENGTH, "" + b.length);
         BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE);
         PrintStream ps = new PrintStream(bos);
         writeHeaderContent(ps, code, message);
         if ( !getMethod().equals(HTTP.METHOD_HEAD) )
            ps.write(b);
         ps.flush();
      }

      protected void sendEmptyError(int code, String message) {
         content.reset();
         send(code, message);
      }

      protected void sendError(int code, String message, Exception e) {
         content.reset();

         resHdr.put(HTTP.HEADER_CONTENT_TYPE, Headers.TYPE_TEXT_HTML);
         resHdr.put(HTTP.HEADER_CACHE_CONTROL, HTTP.VALUE_NO_CACHE);

         Handler handler = broker.getBrokerContext().getHandler();
         if ( handler != null ) {
            try {
               handler.processError(this, String.valueOf(code), message);
               if ( content.size() > 0 ) {
                  send(code, message);
                  return;
               }
            }
            catch ( RequestException ex ) {
               content.reset();
            }
         }

         // Otherwise fall through to the generic form
         PrintWriter out = new PrintWriter(getOutputStream());
         out.println("<html><head><title>Labrador Error</title></head><body bgcolor=\"#FFFFFF\">");

         String exMsg;
         if ( e != null && e.getMessage() != null )
            exMsg = message + ": "+ e.getMessage();
         else
            exMsg = message;

         out.println("<p><font face=\"Arial, Helvetica, sans-serif\">" + exMsg + "<br /></font></p>");
         addFooter(out);
         out.println("</body></html>");
         out.flush();
         send(code, message);
      }

      protected void sendError(int code, String message) {
         sendError(code, message, null);
      }

      protected void parseBuffer(Map retprops, String inbuffer) {
         StringTokenizer st = new StringTokenizer(inbuffer, "&?;", false);
         while ( st.hasMoreTokens() ) {
            String line = st.nextToken();
            int idx = line.indexOf('=');
            if ( idx != -1 ) {
               String key = line.substring(0, idx).trim().toUpperCase();
               String value;
               try {
                  value = URLDecoder.decode(line.substring(idx + 1).trim());
               }
               catch ( Exception e ) {
                  value = line.substring(idx + 1).trim();
               }

               List values = (List)retprops.get(key);
               if ( values == null ) {
                  values = new ArrayList();
                  retprops.put(key, values);
               }
               values.add(value);
            }
         }
      }
   }
}
TOP

Related Classes of com.dbxml.labrador.http.HTTPServerBase$Worker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.