Package org.apache.axis.transport.http

Source Code of org.apache.axis.transport.http.SimpleAxisServer

/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2000 The Apache Software Foundation.  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
*        Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Axis" and "Apache Software Foundation" must
*    not be used to endorse or promote products derived from this
*    software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    nor may "Apache" appear in their name, without prior written
*    permission of the Apache Software Foundation.
*
* 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 APACHE SOFTWARE FOUNDATION 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.axis.transport.http ;

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

import org.apache.axis.* ;
import org.apache.axis.encoding.Base64;
import org.apache.axis.configuration.*;
import org.apache.axis.server.* ;
import org.apache.axis.utils.* ;

import org.apache.axis.session.Session;
import org.apache.axis.session.SimpleSession;
import org.apache.log4j.Category;
import org.apache.log4j.Priority;

import org.w3c.dom.Document;

/**
* This is a single threaded implementation of an HTTP server for processing
* SOAP requests via Apache's xml-axis.  This is not intended for production
* use.  Its intended uses are for demos, debugging, and performance
* profiling.
*
* @author Sam Ruby (ruby@us.ibm.com)
* @author Rob Jellinghaus (robj@unrealities.com)
*/
public class SimpleAxisServer implements Runnable {
    static Category category =
            Category.getInstance(SimpleAxisServer.class.getName());


    // session state.
    // This table maps session keys (random numbers) to SimpleAxisSession objects.
    //
    // There is NO CLEANUP of this table at present, and if clients are not
    // passing cookies, then a new session will be created for *every* request.
    // This is the biggest impediment to any kind of real SimpleAxisServer use.
    // So, if this becomes objectionable, we will implement some simpleminded
    // cleanup (perhaps just a cap on max # of sessions, and some kind of LRU
    // cleanup policy).
    private Hashtable sessions = new Hashtable();
   
    // Are we doing sessions?
    // Set this to false if you don't want any session overhead.
    public static boolean doSessions = true;
   
    // What is our current session index?
    // This is a monotonically increasing, non-thread-safe integer
    // (thread safety not considered crucial here)
    public static int sessionIndex = 0;

    // Configuration provider
    private static FileProvider provider = new FileProvider("server-config.xml");
   
    // Another example of configuration (AdminService only) might look like this...
    //private static XMLStringProvider provider = new XMLStringProvider("<engineConfig><handlers><handler name=\"MsgDispatcher\" class=\"org.apache.axis.providers.java.MsgProvider\"/></handlers><services><service name=\"AdminService\" pivot=\"MsgDispatcher\"><option name=\"className\" value=\"org.apache.axis.utils.Admin\"/><option name=\"methodName\" value=\"AdminService\"/><option name=\"enableRemoteAdmin\" value=\"false\"/></service></services></engineConfig>");
   
    // Axis server (shared between instances)
    private static AxisServer myAxisServer = null;
    private static synchronized AxisServer getAxisServer() {
        if (myAxisServer == null) {
            myAxisServer = new AxisServer(provider);
        }
        return myAxisServer;
    }

    // HTTP prefix
    private static byte HTTP[]   = "HTTP/1.0 ".getBytes();

    // HTTP status codes
    private static byte OK[]     = "200 OK".getBytes();
    private static byte UNAUTH[] = "401 Unauthorized".getBytes();
    private static byte ISE[]    = "500 Internal Server Error".getBytes();

    // Standard MIME headers for XML payload
    private static byte XML_MIME_STUFF[] =
        ( "\nContent-Type: text/xml; charset=utf-8\n" +
          "Content-Length: ").getBytes();

    // Standard MIME headers for HTML payload
    private static byte HTML_MIME_STUFF[] =
        ( "\nContent-Type: text/html; charset=utf-8\n" +
          "Content-Length: ").getBytes();

    // Mime/Content separator
    private static byte SEPARATOR[] = "\n\n".getBytes();
   
    // Tiddly little response
    private static final String responseStr =
            "<html><head><title>SimpleAxisServer</title></head>" +
            "<body><h1>SimpleAxisServer</h1>" +
            "You've reached the SimpleAxisServer." +
            "</html>";
    private static byte cannedHTMLResponse[] = responseStr.getBytes();

    // Axis specific constants
    private static String transportName = "SimpleHTTP";
   
    // are we stopped?
    // latch to true if stop() is called
    private boolean stopped = false;

    /**
     * The main workhorse method.
     *
     * Accept requests from a given TCP port and send them through the
     * Axis engine for processing.
     */
    public void run() {

        // create an Axis server
        AxisServer engine = getAxisServer();
        engine.init();
       
        // create and initialize a message context
        MessageContext msgContext = new MessageContext(engine);
        Message        requestMsg;

        // Reusuable, buffered, content length controlled, InputStream
        NonBlockingBufferedInputStream is =
            new NonBlockingBufferedInputStream();

        // buffers for the headers we care about
        StringBuffer soapAction = new StringBuffer();
        StringBuffer httpRequest = new StringBuffer();
        StringBuffer fileName = new StringBuffer();
        StringBuffer cookie = new StringBuffer();
        StringBuffer cookie2 = new StringBuffer();
        StringBuffer authInfo = new StringBuffer();

        // Accept and process requests from the socket
        while (!stopped) {
            Socket socket = null;

            // prepare request (do as much as possible while waiting for the
            // next connection).  Note the next two statements are commented
            // out.  Uncomment them if you experience any problems with not
            // resetting state between requests:
            //   msgContext = new MessageContext();
            //   requestMsg = new Message("", "String");
            msgContext.setServiceDescription(null);
            msgContext.setTargetService(null);
            msgContext.setResponseMessage(null);
            msgContext.reset();
            //msgContext.setProperty("transport", "HTTPTransport");
            msgContext.setTransportName(transportName);
           
            try {
                try {
                    socket = serverSocket.accept();
                } catch (IOException ioe) {
                    break;
                }

                // assume the best
                byte[] status = OK;

                // assume we're not getting WSDL
                boolean doWsdl = false;

                // cookie for this session, if any
                String cooky = null;
               
                try {
                    // wipe cookies if we're doing sessions
                    if (doSessions) {
                        cookie.delete(0, cookie.length());
                        cookie2.delete(0, cookie.length());
                    }
                    authInfo.delete(0, authInfo.length());

                    // read headers
                    is.setInputStream(socket.getInputStream());
                    // parse all headers into hashtable
                    int contentLength = parseHeaders(is, soapAction, httpRequest,
                        fileName, cookie, cookie2, authInfo);
                    is.setContentLength(contentLength);

                    int paramIdx = fileName.toString().indexOf('?');
                    if (paramIdx != -1) {
                        // Got params
                        String params = fileName.substring(paramIdx + 1);
                        fileName.setLength(paramIdx);
                        category.debug("Filename is " + fileName.toString());
                        category.debug("Params is " + params);
                        if ("wsdl".equalsIgnoreCase(params))
                            doWsdl = true;
                    }

                    msgContext.setProperty(Constants.MC_REALPATH,
                                           fileName.toString());

                    // !!! Fix string concatenation
                    String url = "http://localhost:" +
                            this.getServerSocket().getLocalPort() + "/" +
                            fileName.toString();
                    msgContext.setProperty(MessageContext.TRANS_URL, url);

                    if (authInfo.length() > 0) {
                        // Process authentication info
                        //authInfo = new StringBuffer("dXNlcjE6cGFzczE=");
                        byte [] decoded = Base64.decode(authInfo.toString());
                        StringBuffer userBuf = new StringBuffer();
                        StringBuffer pwBuf = new StringBuffer();
                        StringBuffer authBuf = userBuf;
                        for (int i = 0; i < decoded.length; i++) {
                            if ((char)(decoded[i] & 0x7f) == ':') {
                                authBuf = pwBuf;
                                continue;
                            }
                            authBuf.append((char)(decoded[i] & 0x7f));
                        }
                        category.info("Username : " + userBuf.toString());
                        category.info("Password : " + pwBuf.toString());
                        msgContext.setProperty(MessageContext.USERID,
                                               userBuf.toString());
                        msgContext.setProperty(MessageContext.PASSWORD,
                                               pwBuf.toString());
                    }

                    // if get, then return simpleton document as response
                    if (httpRequest.toString().equals("GET")) {
                        OutputStream out = socket.getOutputStream();
                        out.write(HTTP);
                        out.write(status);

                        if (doWsdl) {
                            engine.generateWSDL(msgContext);

                            Document doc = (Document)msgContext.getProperty("WSDL");

                            if (doc != null) {
                                String response = XMLUtils.DocumentToString(doc);
                                byte [] respBytes = response.getBytes();

                                out.write(XML_MIME_STUFF);
                                putInt(out, respBytes.length);
                                out.write(SEPARATOR);
                                out.write(respBytes);
                                out.flush();
                                continue;
                            }
                        }

                        out.write(HTML_MIME_STUFF);
                        putInt(out, cannedHTMLResponse.length);
                        out.write(SEPARATOR);
                        out.write(cannedHTMLResponse);
                        out.flush();
                        continue;
                    }

                    // this may be "" if either SOAPAction: "" or if no SOAPAction at all.
                    // for now, do not complain if no SOAPAction at all
                    String soapActionString = soapAction.toString();
                    msgContext.setProperty(HTTPConstants.MC_HTTP_SOAPACTION,
                                           soapActionString);
                    requestMsg = new Message(is);
                    msgContext.setRequestMessage(requestMsg);

                    // set up session, if any
                    if (doSessions) {
                        // did we get a cookie?
                        if (cookie.length() > 0) {
                            cooky = cookie.toString().trim();
                        } else if (cookie2.length() > 0) {
                            cooky = cookie2.toString().trim();
                        }

                        // if cooky is null, cook up a cooky
                        if (cooky == null) {
                            // fake one up!
                            // make it be an arbitrarily increasing number
                            // (no this is not thread safe because ++ isn't atomic)
                            int i = sessionIndex++;
                            cooky = "" + i;
                        }

                        // is there a session already?
                        Session session = null;
                        if (sessions.containsKey(cooky)) {
                            session = (Session)sessions.get(cooky);
                        } else {
                            // no session for this cooky, bummer
                            session = new SimpleSession();

                            // ADD CLEANUP LOGIC HERE if needed
                            sessions.put(cooky, session);
                        }

                        msgContext.setSession(session);
                    }

                    // invoke the Axis engine
                    engine.invoke(msgContext);

                } catch( AxisFault af ) {
                    category.error("HTTP server fault", af);

                    if ("Server.Unauthorized".equals(af.getFaultCode())) {
                        status = UNAUTH; // SC_UNAUTHORIZED
                    } else {
                        status = ISE; // SC_INTERNAL_SERVER_ERROR
                    }

                    msgContext.setResponseMessage(new Message(af));

                } catch( Exception e ) {
                    status = ISE; // SC_INTERNAL_SERVER_ERROR
                    msgContext.setResponseMessage(new Message(new AxisFault(e)));
                }


                // Retrieve the response from Axis
                Message responseMsg = msgContext.getResponseMessage();
                byte[] response = (byte[]) responseMsg.getAsBytes();

                // Send it on its way...
                OutputStream out = socket.getOutputStream();
                out.write(HTTP);
                out.write(status);
                out.write(XML_MIME_STUFF);
                putInt(out, response.length);

                if (doSessions) {
                    // write cookie headers, if any
                    // don't sweat efficiency *too* badly
                    // optimize at will
                    StringBuffer cookieOut = new StringBuffer();
                    cookieOut.append("\r\nSet-Cookie: ")
                        .append(cooky)
                        .append("\r\nSet-Cookie2: ")
                        .append(cooky);
                    // OH, THE HUMANITY!  yes this is inefficient.
                    out.write(cookieOut.toString().getBytes());
                }

                out.write(SEPARATOR);
                out.write(response);
                out.flush();

                if (msgContext.getProperty(msgContext.QUIT_REQUESTED) != null) {
                    // why then, quit!
                    this.stop();
                }

            } catch (InterruptedIOException iie) {
                break;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (socket!=null) socket.close();
                } catch (Exception e) {
                }
            }
        }
        System.out.println("SimpleAxisServer quitting.");
    }

    // ASCII character mapping to lower case
    private static final byte[] toLower = new byte[256];

    static {
        for (int i = 0; i < 256; i++) {
            toLower[i] = (byte)i;
        }

        for (int lc = 'a'; lc <= 'z'; lc++) {
            toLower[lc + 'A' - 'a']=(byte)lc;
        }
    }

    // mime header for content length
    private static final byte lenHeader[] = "content-length: ".getBytes();
    private static final int lenLen = lenHeader.length;

    // mime header for soap action
    private static final byte actionHeader[] = "soapaction: ".getBytes();
    private static final int actionLen = actionHeader.length;

    // mime header for cookie
    private static final byte cookieHeader[] = "cookie: ".getBytes();
    private static final int cookieLen = cookieHeader.length;

    // mime header for cookie2
    private static final byte cookie2Header[] = "cookie2: ".getBytes();
    private static final int cookie2Len = cookie2Header.length;

    // HTTP header for authentication
    private static final byte authHeader[] = "authorization: ".getBytes();
    private static final int authLen = authHeader.length;

    // mime header for GET
    private static final byte getHeader[] = "GET".getBytes();

    // mime header for POST
    private static final byte postHeader[] = "POST".getBytes();

    // header ender
    private static final byte headerEnder[] = ": ".getBytes();

    // buffer for IO
    private static final int BUFSIZ = 4096;
    private byte buf[] = new byte[BUFSIZ];

    // "Basic" auth string
    private static final byte basicAuth[] = "basic ".getBytes();

    /**
     * Read a single line from the input stream
     * @param is        inputstream to read from
     * @param b         byte array to read into
     * @param off       starting offset into the byte array
     * @param len       maximum number of bytes to read
     */
    private int readLine(InputStream is, byte[] b, int off, int len)
        throws IOException
    {
        int count = 0, c;

        while ((c = is.read()) != -1) {
            b[off++] = (byte)c;
            count++;
            if (c == '\n' || count == len) break;
        }
        return count > 0 ? count : -1;
    }

    /**
     * Read all mime headers, returning the value of Content-Length and
     * SOAPAction.
     * @param is         InputStream to read from
     * @param soapAction StringBuffer to return the soapAction into
     * @param httpRequest StringBuffer for GET / POST
     * @param cookie first cookie header (if doSessions)
     * @param cookie2 second cookie header (if doSessions)
     * @return Content-Length
     */
    private int parseHeaders(InputStream is,
                             StringBuffer soapAction,
                             StringBuffer httpRequest,
                             StringBuffer fileName,
                             StringBuffer cookie,
                             StringBuffer cookie2,
                             StringBuffer authInfo)
      throws IOException
    {
        int n;
        int len = 0;
       
        // parse first line as GET or POST
        n=this.readLine(is, buf, 0, buf.length);
        if (n < 0) {
            // nothing!
            throw new IOException("Unexpected end of stream");
        }
       
        // which does it begin with?
        httpRequest.delete(0, httpRequest.length());
        fileName.delete(0, fileName.length());
       
        if (buf[0] == getHeader[0]) {
            httpRequest.append("GET");
            for (int i = 0; i < n - 5; i++) {
                char c = (char)(buf[i + 5] & 0x7f);
                if (c == ' ')
                    break;
                fileName.append(c);
            }
            category.debug( "SimpleAxisServer: req filename='" + fileName.toString() + "'");
            return 0;
        } else if (buf[0] == postHeader[0]) {
            httpRequest.append("POST");
            for (int i = 0; i < n - 6; i++) {
                char c = (char)(buf[i + 6] & 0x7f);
                if (c == ' ')
                    break;
                fileName.append(c);
            }
            category.debug( "SimpleAxisServer: req filename='" + fileName.toString() + "'");
        } else {
            throw new IOException("Cannot handle non-GET, non-POST request");
        }
       
        while ((n=readLine(is,buf,0,buf.length)) > 0) {
       
            // if we are at the separator blank line, bail right now
            if ((n<=2) && (buf[0]=='\n'||buf[0]=='\r') && (len>0)) break;
           
            // RobJ gutted the previous logic; it was too hard to extend for more headers.
            // Now, all it does is search forwards for ": " in the buf,
            // then do a length / byte compare.
            // Hopefully this is still somewhat efficient (Sam is watching!).
           
            // First, search forwards for ": "
            int endHeaderIndex = 0;
            while (endHeaderIndex < n && toLower[buf[endHeaderIndex]] != headerEnder[0]) {
                endHeaderIndex++;
            }
            endHeaderIndex += 2;
            // endHeaderIndex now points _just past_ the ": ", and is
            // comparable to the various lenLen, actionLen, etc. values
           
            // convenience; i gets pre-incremented, so initialize it to one less
            int i = endHeaderIndex - 1;
           
            // which header did we find?
            if (endHeaderIndex == lenLen && matches(buf, lenHeader)) {
                // parse content length
               
                while ((++i<n) && (buf[i]>='0') && (buf[i]<='9')) {
                    len = (len*10) + (buf[i]-'0');
                }
               
            }
            else if (endHeaderIndex == actionLen
                       && matches(buf, actionHeader))
            {
                          
                soapAction.delete(0,soapAction.length());
                // skip initial '"'
                i++;
                while ((++i<n) && (buf[i]!='"')) {
                    soapAction.append((char)(buf[i] & 0x7f));
                }
                          
            }
            else if (doSessions && endHeaderIndex == cookieLen
                       && matches(buf, cookieHeader))
            {
                          
                // keep everything up to first ;
                while ((++i<n) && (buf[i]!=';') && (buf[i]!='\r') && (buf[i]!='\n')) {
                    cookie.append((char)(buf[i] & 0x7f));
                }
                          
            }
            else if (doSessions && endHeaderIndex == cookie2Len
                       && matches(buf, cookie2Header))
            {
                          
                // keep everything up to first ;
                while ((++i<n) && (buf[i]!=';') && (buf[i]!='\r') && (buf[i]!='\n')) {
                    cookie2.append((char)(buf[i] & 0x7f));
                }
                          
            }
            else if (endHeaderIndex == authLen && matches(buf, authHeader)) {
                if (matches(buf, endHeaderIndex, basicAuth)) {
                    i += basicAuth.length;
                    while (++i<n && (buf[i]!='\r') && (buf[i]!='\n')) {
                        if (buf[i]==' ') continue;
                        authInfo.append((char)(buf[i] & 0x7f));
                    }
                } else {
                    throw new IOException("Bad authentication type (I can only handle Basic).");
                }
            }

        }
        return len;
    }
   
   
    /**
     * does tolower[buf] match the target byte array, up to the target's length?
     */
    public boolean matches (byte[] buf, byte[] target) {
        for (int i = 0; i < target.length; i++) {
            if (toLower[buf[i]] != target[i]) {
                return false;
            }
        }
        return true;
    }

    /**
     * Case-insensitive match of a target byte [] to a source byte [],
     * starting from a particular offset into the source.
     */
    public boolean matches (byte[] buf, int bufIdx, byte[] target) {
        for (int i = 0; i < target.length; i++) {
            if (toLower[buf[bufIdx + i]] != target[i]) {
                return false;
            }
        }
        return true;
    }


    /**
     * output an integer into the output stream
     * @param out       OutputStream to be written to
     * @param value     Integer value to be written.
     */
    private void putInt(OutputStream out, int value)
        throws IOException
    {
        int len = 0;
        int offset=buf.length;

        // negative numbers
        if (value < 0) {
            buf[--offset] = (byte) '-';
            value=-value;
            len++;
        }

        // zero
        if (value == 0) {
            buf[--offset] = (byte) '0';
            len++;
        }

        // positive numbers
        while (value > 0) {
            buf[--offset] = (byte)(value%10 + '0');
            value=value/10;
            len++;
        }

        // write the result
        out.write(buf, offset, len);
    }

    // per thread socket information
    private ServerSocket serverSocket;
    private volatile Thread worker = null;

    /**
     * Obtain the serverSocket that that SimpleAxisServer is listening on.
     */
    public ServerSocket getServerSocket() {
        return serverSocket;
    }

    /**
     * Set the serverSocket this server should listen on.
     * (note : changing this will not affect a running server, but if you
     *  stop() and then start() the server, the new socket will be used).
     */
    public void setServerSocket(ServerSocket serverSocket) {
        this.serverSocket = serverSocket;
    }

    /**
     * Start this server.
     *
     * Spawns a worker thread to listen for HTTP requests.
     *
     * @param daemon a boolean indicating if the thread should be a daemon.
     */
    public void start(boolean daemon) throws Exception {
        worker = new Thread(this);
        worker.setDaemon(daemon);
        worker.start();
    }

    /**
     * Start this server as a NON-daemon.
     */
    public void start() throws Exception {
        start(false);
    }

    /**
     * Stop this server.
     *
     * This will interrupt any pending accept().
     */
    public void stop() throws Exception {
        stopped = true;
        if (worker != null) worker.interrupt();
    }

    /**
     * Server process.
     * @parms args[1] port number (default is 8080)
     */
    public static void main(String args[]) {

        SimpleAxisServer sas = new SimpleAxisServer();

        Options opts = null;
        try {
            opts = new Options(args);
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return;
        }
       
        try {
            int port = opts.getPort();
            ServerSocket ss = new ServerSocket(port);
            sas.setServerSocket(ss);
            sas.run();
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

     }

}
TOP

Related Classes of org.apache.axis.transport.http.SimpleAxisServer

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.