org.mule.providers.http.HttpMessageReceiver.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.providers.http.HttpMessageReceiver.java

Source

/*
 * Copyright (c) SymphonySoft Limited. All rights reserved.
 * http://www.symphonysoft.com
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE-MULE.txt file.
 */

package org.mule.providers.http;

import org.apache.commons.httpclient.ChunkedInputStream;
import org.mule.config.MuleProperties;
import org.mule.config.i18n.Message;
import org.mule.config.i18n.Messages;
import org.mule.impl.*;
import org.mule.providers.AbstractMessageReceiver;
import org.mule.providers.ConnectException;
import org.mule.providers.tcp.TcpMessageReceiver;
import org.mule.umo.UMOComponent;
import org.mule.umo.UMOMessage;
import org.mule.umo.endpoint.UMOEndpoint;
import org.mule.umo.lifecycle.InitialisationException;
import org.mule.umo.provider.UMOConnector;
import org.mule.umo.provider.UMOMessageAdapter;
import org.mule.util.PropertiesHelper;
import org.mule.util.monitor.Expirable;

import javax.resource.spi.work.Work;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.Properties;
import java.util.StringTokenizer;

/**
 * <code>HttpMessageReceiver</code> is a simple http server that can be used
 * to listen for http requests on a particular port
 *
 * @author <a href="mailto:ross.mason@symphonysoft.com">Ross Mason</a>
 * @version $Revision: 1.46 $
 */
public class HttpMessageReceiver extends TcpMessageReceiver {
    //private ExpiryMonitor keepAliveMonitor;

    public HttpMessageReceiver(UMOConnector connector, UMOComponent component, UMOEndpoint endpoint)
            throws InitialisationException {
        super(connector, component, endpoint);
        //        if (((HttpConnector) connector).isKeepAlive()) {
        //            keepAliveMonitor = new ExpiryMonitor(1000);
        //        }
    }

    protected Work createWork(Socket socket) throws SocketException {
        return new HttpWorker(socket);
    }

    public void doConnect() throws ConnectException {
        //If we already have an endpoint listening on this socket don't try and
        //start another serversocket
        if (shouldConnect()) {
            super.doConnect();
        }
    }

    protected boolean shouldConnect() {
        StringBuffer requestUri = new StringBuffer();
        requestUri.append(endpoint.getProtocol()).append("://");
        requestUri.append(endpoint.getEndpointURI().getHost());
        requestUri.append(":").append(endpoint.getEndpointURI().getPort());
        requestUri.append("*");
        AbstractMessageReceiver[] temp = connector.getReceivers(requestUri.toString());
        for (int i = 0; i < temp.length; i++) {
            AbstractMessageReceiver abstractMessageReceiver = temp[i];
            if (abstractMessageReceiver.isConnected()) {
                return false;
            }
        }
        return true;
    }

    public void doDispose() {
        //        if (keepAliveMonitor != null) {
        //            keepAliveMonitor.dispose();
        //        }
        super.doDispose();
    }

    private class HttpWorker extends TcpWorker implements Expirable {

        public HttpWorker(Socket socket) throws SocketException {
            super(socket);
            boolean keepAlive = ((HttpConnector) connector).isKeepAlive();
            if (keepAlive) {
                socket.setKeepAlive(true);
                socket.setSoTimeout(((HttpConnector) connector).getKeepAliveTimeout());
            }

        }

        public void run() {
            boolean keepAlive = ((HttpConnector) connector).isKeepAlive();
            try {
                dataIn = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
                dataOut = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
                do {
                    Properties headers = new Properties();
                    Object payload = parseRequest(dataIn, headers);
                    if (payload == null) {
                        break;
                    }
                    UMOMessageAdapter adapter = connector.getMessageAdapter(new Object[] { payload, headers });
                    //Removed the keep alive monitoring stuff for now
                    //nstead just wait for the client to disconnect
                    //keepAlive = adapter.getBooleanProperty(HttpConstants.HEADER_KEEP_ALIVE, keepAlive);
                    //                    if (keepAlive && !keepAliveRegistered) {
                    //                        keepAliveRegistered = true;
                    //                        if (keepAliveMonitor != null) {
                    //                            keepAliveMonitor.addExpirable(((HttpConnector) connector).getKeepAliveTimeout(), this);
                    //                        } else {
                    //                            logger.info("Request has Keep alive set but the HttpConnector has keep alive disables");
                    //                            keepAlive = false;
                    //                        }
                    //                    }

                    UMOMessage message = new MuleMessage(adapter);

                    if (logger.isDebugEnabled()) {
                        logger.debug(message.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY));
                    }
                    OutputStream os = new ResponseOutputStream(dataOut, socket);

                    //determine if the request path on this request denotes a different receiver
                    AbstractMessageReceiver receiver = getTargetReceiver(message, endpoint);

                    UMOMessage returnMessage = null;
                    //the respone only needs to be transformed explicitly if A) the request was not served or a null result was returned
                    boolean transformResponse = false;
                    if (receiver != null) {
                        returnMessage = receiver.routeMessage(message, endpoint.isSynchronous(), os);
                        if (returnMessage == null) {
                            returnMessage = new MuleMessage("");
                            transformResponse = true;
                            RequestContext.rewriteEvent(returnMessage);
                        }
                    } else {
                        transformResponse = true;
                        String failedPath = endpoint.getEndpointURI().getScheme() + "://"
                                + endpoint.getEndpointURI().getHost() + ":" + endpoint.getEndpointURI().getPort()
                                + getRequestPath(message);

                        logger.debug("Failed to bind to " + failedPath);
                        returnMessage = new MuleMessage(
                                new Message(Messages.CANNOT_BIND_TO_ADDRESS_X, failedPath).toString());
                        returnMessage.setIntProperty(HttpConnector.HTTP_STATUS_PROPERTY,
                                HttpConstants.SC_NOT_FOUND);
                        RequestContext.setEvent(new MuleEvent(returnMessage, endpoint, new MuleSession(), true));
                    }
                    Object response = returnMessage.getPayload();
                    if (transformResponse) {
                        response = connector.getDefaultResponseTransformer().transform(response);
                    }

                    if (response instanceof byte[]) {
                        dataOut.write((byte[]) response);
                    } else {
                        dataOut.write(response.toString().getBytes());
                    }
                    dataOut.flush();
                    //                        if (keepAliveMonitor != null) {
                    //                            keepAliveMonitor.resetExpirable(this);
                    //                        }
                } while (!socket.isClosed() && !disposing.get() && keepAlive);
                if (logger.isDebugEnabled() && socket.isClosed()) {
                    logger.debug("Peer closed connection");
                }
            } catch (Exception e) {
                handleException(e);
            } finally {
                //                if (keepAliveMonitor != null) {
                //                    keepAliveMonitor.removeExpirable(this);
                //                }
                dispose();
            }
        }

        public void expired() {
            logger.debug("Keep alive timed out");
            dispose();
        }
    }

    protected String getRequestPath(UMOMessage message) {
        String path = (String) message.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY);
        int i = path.indexOf("?");
        if (i > -1)
            path = path.substring(0, i);
        return path;
    }

    protected AbstractMessageReceiver getTargetReceiver(UMOMessage message, UMOEndpoint endpoint)
            throws ConnectException {

        String path = (String) message.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY);
        int i = path.indexOf("?");
        if (i > -1)
            path = path.substring(0, i);

        StringBuffer requestUri = new StringBuffer();
        requestUri.append(endpoint.getProtocol()).append("://");
        requestUri.append(endpoint.getEndpointURI().getHost());
        requestUri.append(":").append(endpoint.getEndpointURI().getPort());
        //first check there is a receive on the root address
        AbstractMessageReceiver receiver = connector.getReceiver(requestUri.toString());
        //If no receiver on the root and there is a request path, look up the received based on the
        //root plus request path
        if (receiver == null && !"/".equals(path)) {
            //remove anything after the last '/'
            int x = path.lastIndexOf("/");
            if (x > 1 && path.indexOf(".") > x) {
                requestUri.append(path.substring(0, x));
            } else {
                requestUri.append(path);
            }
            receiver = connector.getReceiver(requestUri.toString());
        }
        return receiver;
    }

    protected Object parseRequest(InputStream is, Properties p) throws IOException {
        RequestInputStream req = new RequestInputStream(is);
        Object payload = null;
        String startLine = null;
        do {
            try {
                startLine = req.readline();
            } catch (IOException e) {
                logger.debug(e.getMessage());
            }
            if (startLine == null)
                return null;
        } while (startLine.trim().length() == 0);

        StringTokenizer tokenizer = new StringTokenizer(startLine);
        String method = tokenizer.nextToken();
        String request = tokenizer.nextToken();
        String httpVersion = tokenizer.nextToken();

        p.setProperty(HttpConnector.HTTP_METHOD_PROPERTY, method);
        p.setProperty(HttpConnector.HTTP_REQUEST_PROPERTY, request);
        p.setProperty(HttpConnector.HTTP_VERSION_PROPERTY, httpVersion);

        // Read headers from the request as set them as properties on the event
        readHeaders(req, p);

        if (method.equals(HttpConstants.METHOD_GET)) {
            payload = request.getBytes();
        } else {
            boolean multipart = p.getProperty(HttpConstants.HEADER_CONTENT_TYPE, "")
                    .indexOf("multipart/related") > -1;
            String contentLengthHeader = p.getProperty(HttpConstants.HEADER_CONTENT_LENGTH, null);
            String chunkedString = PropertiesHelper.getStringProperty(p, HttpConstants.HEADER_TRANSFER_ENCODING,
                    null);
            boolean chunked = "chunked".equalsIgnoreCase(chunkedString);
            if (contentLengthHeader == null && !chunked) {
                throw new IllegalStateException(HttpConstants.HEADER_CONTENT_LENGTH + " header must be set");
            }

            if (chunked) {
                byte[] buffer = new byte[1024 * 32];
                int totalLength = 0;
                int length = 0;
                ChunkedInputStream cis = new ChunkedInputStream(req);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                //ast: change the chunked method to avoid original mule limitation of 32768 bytes
                do {
                    length = cis.read(buffer, 0, buffer.length);
                    if (length >= 0) {
                        baos.write(buffer, 0, length);
                        totalLength += length;
                    }
                } while (length >= 0);

                if ((length == -1) && (totalLength == 0)) {
                    return null;
                }

                payload = baos.toByteArray();
            } else {
                int contentLength = Integer.parseInt(contentLengthHeader);

                if (multipart) {
                    byte[] buffer = new byte[1024];

                    payload = File.createTempFile("mime", ".att");
                    ((File) payload).deleteOnExit();
                    FileOutputStream os = new FileOutputStream((File) payload);

                    int length = -1;
                    int offset = 0;
                    while (offset != contentLength) {
                        buffer = new byte[1024];
                        length = is.read(buffer);
                        if (length != -1) {
                            os.write(buffer, 0, length);
                            offset += length;
                        }
                    }
                    os.close();
                } else {
                    byte[] buffer = new byte[contentLength];

                    int length = -1;
                    int offset = req.read(buffer);
                    while (offset >= 0 && offset < buffer.length) {
                        length = req.read(buffer, offset, buffer.length - offset);
                        if (length == -1) {
                            break;
                        }
                        offset += length;
                    }
                    payload = buffer;
                }
            }
        }
        return payload;
    }

    private void readHeaders(RequestInputStream is, Properties p) throws IOException {
        String currentKey = null;
        while (true) {
            String line = is.readline();
            if ((line == null) || (line.length() == 0)) {
                break;
            }

            if (!Character.isSpaceChar(line.charAt(0))) {
                int index = line.indexOf(':');
                if (index >= 0) {
                    currentKey = line.substring(0, index).trim();
                    if (currentKey.startsWith("X-" + MuleProperties.PROPERTY_PREFIX)) {
                        currentKey = currentKey.substring(2);
                    } else {
                        // normalize incoming header if necessary
                        String normalizedKey = (String) HttpConstants.ALL_HEADER_NAMES.get(currentKey);
                        if (normalizedKey != null) {
                            currentKey = normalizedKey;
                        }
                    }
                    String value = line.substring(index + 1).trim();
                    p.setProperty(currentKey, value);
                }
            } else if (currentKey != null) {
                String value = p.getProperty(currentKey);
                p.setProperty(currentKey, value + "\r\n\t" + line.trim());
            }
        }

    }
}