Package org.xmlBlaster.util.http

Source Code of org.xmlBlaster.util.http.HttpIORServer

/*------------------------------------------------------------------------------
Name:      HttpIORServer.java
Project:   xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
Comment:   Delivering the Authentication Service IOR over HTTP
Version:   $Id: HttpIORServer.java 14813 2006-03-04 23:02:48Z laghi $
------------------------------------------------------------------------------*/
package org.xmlBlaster.util.http;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;

import java.util.logging.Logger;
import java.util.logging.Level;
import org.xmlBlaster.util.Global;


/**
* Delivering the Authentication Service IOR over HTTP.
* <p />
* This tiny HTTP server is always running in the xmlBlaster server on the
* default bootstrapPort 3412.<br />
* Clients may access through this bootstrap port the AuthServer IOR if they
* don't want to use a naming service
* <p />
* You may specify on command line -bootstrapPort <port> and -bootstrapHostname <host>
* to choose another bootstrap port or to choose a server IP address on
* multi homed hosts.
* <p />
* Change code to be a generic HTTP server, not only for CORBA bootstrapping
* @version $Revision: 1.31 $
* @author $Author: laghi $
*/
public class HttpIORServer extends Thread implements I_HttpRequest
{
   private String ME = "HttpServer";
   private final Global glob;
   private static Logger log = Logger.getLogger(HttpIORServer.class.getName());
   private String ip_addr = null;
   private final int HTTP_PORT;
   private ServerSocket listen = null;
   private boolean running = true;

   private String icoMimeType = "image/ico";
   private String icoRequestFile = "favicon.ico";
   private String icoRequestUrlPath = "/"+icoRequestFile;

   private String fishMimeType = "image/gif";
   private String fishRequestFile = "rainbowfish200.gif";
   private String fishRequestUrlPath = "/"+fishRequestFile;

   private Hashtable knownRequests = new Hashtable();

   /**
    * Create a little web server.
    * <p />
    * @param ip_addr The string representation like "192.168.1.1", useful if multihomed computer
    * @param bootstrapPort    The bootstrap port where we publish the IOR
    */
   public HttpIORServer(Global glob, String ip_addr, int port)
   {
      super("XmlBlaster.HttpIORServer");
      this.glob = glob;

      this.ip_addr = ip_addr;
      this.HTTP_PORT = port;
      this.ME +=  this.glob.getLogPrefixDashed();
      if (this.HTTP_PORT <= 0) {
         if (log.isLoggable(Level.FINER)) log.finer("Internal HttpServer not started, as -bootstrapPort is " + this.HTTP_PORT);
         return;
      }

      registerRequest(icoRequestUrlPath, this);
      registerRequest(fishRequestUrlPath, this);

      if (log.isLoggable(Level.FINER)) log.finer("Creating new HttpServer on IP=" + this.ip_addr + " bootstrap port=" + this.HTTP_PORT);
      setDaemon(true);
      start();
   }

   /**
    * If you want to provide some information over http, register it here.
    * @param urlPath The access path which the client uses to access your data, for example "/AuthenticationService.ior"
    * @param data The data you want to deliver to the client e.g. the CORBA IOR string
    */
   public void registerRequest(String urlPath, String data)
   {
      if (log.isLoggable(Level.FINE)) log.fine("Registering urlPath: " + urlPath + "=" + data);
      knownRequests.put(urlPath.trim(), data);
   }

   /**
    * If you want to provide some information over http, register it here.
    * @param urlPath The access path which the client uses to access your data,
    *        for example "/monitor/index.html" or "/favicon.ico"
    * @param data The data you want to deliver to the client e.g. the CORBA IOR string
    */
   public void registerRequest(String urlPath, I_HttpRequest cb)
   {
      if (log.isLoggable(Level.FINE)) log.fine("Registering urlPath: " + urlPath);
      knownRequests.put(urlPath.trim(), cb);
   }
  

   /**
    * Unregister your http listener.
    * @param urlPath The access path which the client uses to access your data
    *        for example "/monitor/index.html" or "/favicon.ico"
    */
   public void removeRequest(String urlPath)
   {
      knownRequests.remove(urlPath.trim());
   }

   /**
    * Unregister your http listener.
    * @param cb Remove all registered pathes of this registrar.
    *        for example "/monitor/index.html" or "/favicon.ico"
    */
   public void removeRequest(I_HttpRequest cb)
   {
      Iterator it = knownRequests.keySet().iterator();
      ArrayList list = new ArrayList();
      while (it.hasNext()) {
         Object obj = it.next();
         if (obj instanceof I_HttpRequest) {
            I_HttpRequest tmp = (I_HttpRequest)obj;
            if (cb == tmp) {
               list.add(tmp);
            }
         }
      }
      for (int i=0; i<list.size(); i++) {
         knownRequests.remove(list.get(i));
      }
   }

   /**
    */
   public void run()
   {
      try {
         int backlog = glob.getProperty().get("http.backlog", 50); // queue for max 50 incoming connection request
         this.listen = new ServerSocket(HTTP_PORT, backlog, InetAddress.getByName(ip_addr));
         while (running) {
            Socket accept = this.listen.accept();
            log.fine("New incoming request on bootstrapPort=" + HTTP_PORT + " ...");
            if (!running) {
               log.info("Closing http server bootstrapPort=" + HTTP_PORT + ".");
               break;
            }
            new HandleRequest(glob, log, accept, knownRequests);
         }
      }
      catch (java.net.UnknownHostException e) {
         log.severe("HTTP server problem, IP address '" + ip_addr + "' is invalid: " + e.toString());
      }
      catch (java.net.BindException e) {
         log.severe("HTTP server problem, bootstrapPort " + ip_addr + ":" + HTTP_PORT + " is not available: " + e.toString());
      }
      catch (java.net.SocketException e) {
         log.info("Socket " + ip_addr + ":" + HTTP_PORT + " closed successfully: " + e.toString());
      }
      catch (IOException e) {
         log.severe("HTTP server problem on " + ip_addr + ":" + HTTP_PORT + ": " + e.toString());
      }

      if (this.listen != null) {
         try { this.listen.close(); } catch (java.io.IOException e) { log.warning("this.listen.close()" + e.toString()); }
         this.listen = null;
      }
   }


   /**
    * Close the listener port
    */
   public void shutdown()// throws IOException
   {
      if (log.isLoggable(Level.FINER)) log.finer("Entering shutdown");
      running = false;
      removeRequest(icoRequestUrlPath);
      boolean closeHack = true;
      if (this.listen != null && closeHack) {
         // On some JDKs, listen.close() is not immediate (has a delay for about 1 sec.)
         // force closing by invoking server with this temporary client:
         try {
            java.net.Socket socket = new Socket(this.listen.getInetAddress(), HTTP_PORT);
            socket.close();
         } catch (java.io.IOException e) {
            log.warning("shutdown problem: " + e.toString());
         }
      }

      try {
         if (this.listen != null) {
            this.listen.close();
            this.listen = null;
         }
      } catch (java.io.IOException e) {
         log.warning("shutdown problem: " + e.toString());
      }
   }

   /**
    * A HTTP request needs to be processed
    * @param urlPath The url path like "/monitor" which triggered this call
    * @param properties The key values from the browser
    * @return The HTML page to return
    */
   public HttpResponse service(String urlPath, Map properties) {
      if (urlPath.indexOf(icoRequestFile) != -1) {
         // set the application icon "favicon.ico"
         byte[] img = Global.getFromClasspath(icoRequestFile, this);
         if (log.isLoggable(Level.FINE)) log.fine("Serving urlPath '" + urlPath + "'");
         return new HttpResponse(img, icoMimeType);
      }
      else if (urlPath.indexOf(fishRequestFile) != -1) {
         byte[] img = Global.getFromClasspath(fishRequestFile, this);
         if (log.isLoggable(Level.FINE)) log.fine("Serving urlPath '" + urlPath + "'");
         return new HttpResponse(img, fishMimeType);
      }
      throw new IllegalArgumentException("Can't handle unknown " + urlPath);
   }

   /**
    * Access the server settings for logging.
    * @return The socket <ip>:<port>, for example "server.xmlBlaster.org:3412"
    */
   public String getSocketInfo() {
      StringBuffer sb = new StringBuffer(196);

      if (this.listen == null) {
         if (running) {
            // Wait on thread to startup
            for (int i=0; i<10; i++) {
               try { Thread.sleep(20L); } catch( InterruptedException e) {}
               if (this.listen != null) break;
            }
         }
         if (this.listen == null) {
            return "";
         }
      }
     
      sb.append(listen.getInetAddress().getHostAddress());
      sb.append(":").append(this.HTTP_PORT);
      return sb.toString();
   }
} // class HttpIORServer


/**
* Handles a request from a client, delivering the AuthServer IOR
*/
class HandleRequest extends Thread
{
   private final String ME;
   private final Global glob;
   private static Logger log = Logger.getLogger(HttpIORServer.class.getName());
   private final Socket sock;
   private final Hashtable knownRequests;
   private final String CRLF = "\r\n";
   private final String VERSION = "1.0";


   /**
    */
   public HandleRequest(Global glob, Logger log, Socket sock, Hashtable knownRequests)
   {
      this.glob = glob;
      this.ME = "HandleRequest" + this.glob.getLogPrefixDashed();
      this.log = log;
      this.sock = sock;
      this.knownRequests = knownRequests;
      start();
   }

   /**
    * TODO: The HTTP/1.1 spec states that we should return the "Date:" header as well.
    * <p />
    * Test with "telnet <host> 3412"<br />
    *   GET /AuthenticationService.ior HTTP/1.0
    */
   public void run()
   {
      if (log.isLoggable(Level.FINER)) log.finer("Handling client request, accessing AuthServer IOR ...");
      BufferedReader iStream = null;
      DataOutputStream oStream = null;
      String clientRequest = "";
      boolean first = true;
      try {
         iStream = new BufferedReader(new InputStreamReader(sock.getInputStream()));
         oStream = new DataOutputStream(sock.getOutputStream());

         clientRequest = iStream.readLine();
         String headerLine; // "\r\n"   carriage return and line feed terminate the http header section
         while (true /*!sock.isClosed() JDK 1.4 only*/) {
            headerLine = iStream.readLine();
            if (log.isLoggable(Level.FINE)) log.fine("Receiving header '" + headerLine + "'");
            if (headerLine == null || headerLine.trim().length() < 1) {
               break;
            }
         }

         if (log.isLoggable(Level.FINER)) log.finer("Request from client " + getSocketInfo());

         if (clientRequest == null) {
            String info = "Empty request ignored " + getSocketInfo();
            errorResponse(oStream, "HTTP/1.1 400 Bad Request", null, true, info);
            log.warning(info);
            return;
         }

         first = false;
         if (log.isLoggable(Level.FINE)) log.fine("Handling client request '" + clientRequest + "' ...");

         StringTokenizer toks = new StringTokenizer(clientRequest);
         if (toks.countTokens() != 3) {
            String info = "Wrong syntax in client request: '" + clientRequest + "', closing " + getSocketInfo() + " connection.";
            errorResponse(oStream, "HTTP/1.1 400 Bad Request", null, true, info);
            log.warning(info);
            return;
         }

         String method = toks.nextToken();   // "GET"
         String resource = toks.nextToken(); // "/AuthenticationService.ior"
//         String version =
       toks.nextToken()// "HTTP/1.0"

/*
         if (false) { // TEST ONLY:
            Uri uri = null;
            try {
               // TODO: use UriAuthority to parse the request and forward it to CommandManager
               //UriAuthority uriAuthority = new UriAuthority(resource);

               // To test a telnet with
               // GET http://joe:mypasswd@develop:3412/admin/?key=XX HTTP/1.0

               // !! From browser we only get "/admin/?key=XX" -> 'joe:mypasswd' is not delivered!!

               uri = new Uri(glob, resource);
               if (log.isLoggable(Level.FINER)) log.call(ME, "Request is" + uri.toXml());
            }
            catch (XmlBlasterException e) {
               String info = getSocketInfo() + ": " + e.toString();
               log.call(ME, info);
               errorResponse(oStream, "HTTP/1.1 400 Bad Request", null, true, info);
               return;
            }
            finally {
               if (log.isLoggable(Level.FINER)) {
                  while (true) {
                     String req = iStream.readLine();
                     if (req == null)
                        break;
                     if (log.isLoggable(Level.FINER)) log.call(ME, req);
                  }
               }
            }
         }
*/

         // RFC 2068 enforces minimum implementation GET and HEAD
         if (!method.equalsIgnoreCase("GET") && !method.equalsIgnoreCase("HEAD")) {
            String info = "Invalid method in client " + getSocketInfo() + " request: '" + clientRequest + "'";
            errorResponse(oStream, "HTTP/1.1 501 Method Not Implemented", "Allow: GET", true, info);
            log.warning(info);
            return;
         }

         // lookup if request is registered
         resource = resource.trim();

        
         Object obj = knownRequests.get(resource);
         if(log.isLoggable(Level.FINE)) log.fine("1. Resource: " + resource + " => " + obj);
         if (obj == null) {
            Iterator it = knownRequests.keySet().iterator();
            while (it.hasNext()) {
               String key = (String)it.next();
               if (resource.startsWith(key)) {
                  obj = knownRequests.get(key);
                  break;
               }
            }
            if (obj == null) {
               String info = "Ignoring unknown data '" + resource + "' from client " + getSocketInfo() + " request: '" + clientRequest + "'";
               errorResponse(oStream, "HTTP/1.1 404 Not Found", null, true, info);
               log.warning(info);
               return;
            }
         }
         if(log.isLoggable(Level.FINE)) log.fine("2. Resource: " + resource + " => " + obj);

         HttpResponse httpResponse;
         if (obj instanceof String) {
            httpResponse = new HttpResponse((String)obj, "text/plain"); // CORBA IOR
         }
         else {
            I_HttpRequest httpRequest = (I_HttpRequest)obj;             // Registered plugins
            httpResponse = httpRequest.service(resource, new TreeMap());
         }

         // java.net.HttpURLConnection.HTTP_OK:
         errorResponse(oStream, "HTTP/1.1 200 OK", null, false, null);
         String length = "Content-Length: " + httpResponse.getContent().length;
         oStream.write((length+CRLF).getBytes());
         //oStream.write(("Transfer-Encoding: chunked"+CRLF).getBytes()); // java.io.IOException: Bogus chunk size
         oStream.write(("Content-Type: "+httpResponse.getMimeType()+"; charset=utf-8"+CRLF).getBytes());
         if (!method.equalsIgnoreCase("HEAD")) {
            oStream.write(CRLF.getBytes());
            oStream.write(httpResponse.getContent());
         }
         oStream.flush();
      }
      catch (Throwable e) {
         if (clientRequest == null && first) {
            if (log.isLoggable(Level.FINE)) log.fine("Ignoring connect/disconnect attempt, probably a xmlBlaster client detecting its IP to use");
         } else {
            log.warning("Problems with sending response for '" + clientRequest + "' to client " + getSocketInfo() + ": " + e.toString());
         }
         // throw new XmlBlasterException(ME, "Problems with sending IOR to client: " + e.toString());
      }
      finally {
         try { if (iStream != null) iStream.close(); } catch (IOException e) { }
         try { if (oStream != null) oStream.close(); } catch (IOException e) { }
         try { sock.close()} catch (IOException e) { }
      }
   }


   private void errorResponse(DataOutputStream oStream, String code, String extra, boolean body, String info) throws IOException
   {
      oStream.write((code+CRLF).getBytes());
      oStream.write(("Server: XmlBlaster HttpServer/"+VERSION+CRLF).getBytes());
      if (extra != null) oStream.write((extra+CRLF).getBytes());
      oStream.write(("Connection: close"+CRLF).getBytes());
      if (body) {
        oStream.write((CRLF+"<html><head><title>"+code+"</title></head><body>" +
                      "<h2>XmlBlaster HTTP server " + VERSION + "</h2>" +
                      "<p>" + code + "</p>" +
                      "<p>" + info + "</p>" +
                      "<p><a href='" + glob.getProperty().get("http.info.url", "http://www.xmlBlaster.org") + "'>Info</a></p>" +
                      "</body></html>").getBytes());
      }
   }

   private String getSocketInfo() {
      StringBuffer sb = new StringBuffer(196);
      if (sock == null)
         return "";
      sb.append(sock.getInetAddress().getHostAddress());
      sb.append(":").append(sock.getPort());
      sb.append(" -> ");
      sb.append(sock.getLocalAddress().getHostAddress());
      sb.append(":").append(sock.getLocalPort());
      return sb.toString();
   }
} // class HandleRequest
TOP

Related Classes of org.xmlBlaster.util.http.HttpIORServer

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.