Package com.noelios.restlet.http

Source Code of com.noelios.restlet.http.HttpServerCall

/*
* Copyright 2005-2007 Noelios Consulting.
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License (the "License"). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the license at
* http://www.opensource.org/licenses/cddl1.txt See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each file and
* include the License file at http://www.opensource.org/licenses/cddl1.txt If
* applicable, add the following below this CDDL HEADER, with the fields
* enclosed by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*/

package com.noelios.restlet.http;

import com.noelios.restlet.util.HeaderReader;
import org.restlet.Server;
import org.restlet.data.*;
import org.restlet.resource.InputRepresentation;
import org.restlet.resource.ReadableRepresentation;
import org.restlet.resource.Representation;
import org.restlet.service.ConnectorService;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Abstract HTTP server connector call.
*
* @author Jerome Louvel (contact@noelios.com)
*/
public abstract class HttpServerCall extends HttpCall {
    /** Indicates if the "host" header was already parsed. */
    private boolean hostParsed;

    /**
     * Constructor.
     *
     * @param server
     *                The parent server connector.
     */
    public HttpServerCall(Server server) {
        this(server.getLogger(), server.getAddress(), server.getPort());
    }

    /**
     * Constructor.
     *
     * @param logger
     *                The logger.
     * @param serverAddress
     *                The server IP address.
     * @param serverPort
     *                The server port.
     */
    public HttpServerCall(Logger logger, String serverAddress, int serverPort) {
        setLogger(logger);
        setServerAddress(serverAddress);
        setServerPort(serverPort);
        this.hostParsed = false;
    }

    /**
     * Returns the request entity channel if it exists.
     *
     * @return The request entity channel if it exists.
     */
    public abstract ReadableByteChannel getRequestChannel();

    /**
     * Returns the request entity stream if it exists.
     *
     * @return The request entity stream if it exists.
     */
    public abstract InputStream getRequestStream();

    /**
     * Returns the response channel if it exists.
     *
     * @return The response channel if it exists.
     */
    public abstract WritableByteChannel getResponseChannel();

    /**
     * Returns the response stream if it exists.
     *
     * @return The response stream if it exists.
     */
    public abstract OutputStream getResponseStream();

    /**
     * Returns the request entity if available.
     *
     * @return The request entity if available.
     */
    public Representation getRequestEntity() {
        Representation result = null;
        InputStream requestStream = getRequestStream();
        ReadableByteChannel requestChannel = getRequestChannel();

        if (((requestStream != null) || (requestChannel != null))) {
            // Extract the header values
            MediaType contentMediaType = null;
            long contentLength = Representation.UNKNOWN_SIZE;

            if (requestStream != null) {
                result = new InputRepresentation(requestStream,
                        contentMediaType, contentLength);
            } else {
                result = new ReadableRepresentation(requestChannel,
                        contentMediaType, contentLength);
            }

            for (Parameter header : getRequestHeaders()) {
                if (header.getName().equalsIgnoreCase(
                        HttpConstants.HEADER_CONTENT_ENCODING)) {
                    HeaderReader hr = new HeaderReader(header.getValue());
                    String value = hr.readValue();
                    while (value != null) {
                        Encoding encoding = Encoding.valueOf(value);
                        if (!encoding.equals(Encoding.IDENTITY)) {
                            result.getEncodings().add(encoding);
                        }
                        value = hr.readValue();
                    }
                } else if (header.getName().equalsIgnoreCase(
                        HttpConstants.HEADER_CONTENT_LANGUAGE)) {
                    HeaderReader hr = new HeaderReader(header.getValue());
                    String value = hr.readValue();
                    while (value != null) {
                        result.getLanguages().add(Language.valueOf(value));
                        value = hr.readValue();
                    }
                } else if (header.getName().equalsIgnoreCase(
                        HttpConstants.HEADER_CONTENT_TYPE)) {
                    ContentType contentType = new ContentType(header.getValue());
                    result.setMediaType(contentType.getMediaType());
                    result.setCharacterSet(contentType.getCharacterSet());

                } else if (header.getName().equalsIgnoreCase(
                        HttpConstants.HEADER_CONTENT_LENGTH)) {
                    try {
                        contentLength = Long.parseLong(header.getValue());
                    } catch (NumberFormatException e) {
                        contentLength = Representation.UNKNOWN_SIZE;
                    }
                    result.setSize(contentLength);
                }
            }
        }

        return result;
    }

    /**
     * Returns the host domain name.
     *
     * @return The host domain name.
     */
    public String getHostDomain() {
        if (!hostParsed)
            parseHost();
        return super.getHostDomain();
    }

    /**
     * Returns the host port.
     *
     * @return The host port.
     */
    public int getHostPort() {
        if (!hostParsed)
            parseHost();
        return super.getHostPort();
    }

    /**
     * Parses the "host" header to set the server host and port properties.
     */
    private void parseHost() {
        String host = getRequestHeaders().getFirstValue(
                HttpConstants.HEADER_HOST, true);
        if (host != null) {
            int colonIndex = host.indexOf(':');

            if (colonIndex != -1) {
                super.setHostDomain(host.substring(0, colonIndex));
                super.setHostPort(Integer.valueOf(host
                        .substring(colonIndex + 1)));
            } else {
                super.setHostDomain(host);
                super.setHostPort(getProtocol().getDefaultPort());
            }
        } else {
            getLogger().info(
                    "Couldn't find the mandatory \"Host\" HTTP header.");
        }

        this.hostParsed = true;
    }

    /**
     * Reads the HTTP request head (request line and headers).
     *
     * @throws IOException
     */
    protected void readRequestHead(InputStream headStream) throws IOException {
        StringBuilder sb = new StringBuilder();

        // Parse the request method
        int next = headStream.read();
        while ((next != -1) && !HttpUtils.isSpace(next)) {
            sb.append((char) next);
            next = headStream.read();
        }

        if (next == -1) {
            throw new IOException(
                    "Unable to parse the request method. End of stream reached too early.");
        } else {
            setMethod(sb.toString());
            sb.delete(0, sb.length());

            // Parse the request URI
            next = headStream.read();
            while ((next != -1) && !HttpUtils.isSpace(next)) {
                sb.append((char) next);
                next = headStream.read();
            }

            if (next == -1) {
                throw new IOException(
                        "Unable to parse the request URI. End of stream reached too early.");
            } else {
                setRequestUri(sb.toString());
                sb.delete(0, sb.length());

                // Parse the HTTP version
                next = headStream.read();
                while ((next != -1) && !HttpUtils.isCarriageReturn(next)) {
                    sb.append((char) next);
                    next = headStream.read();
                }

                if (next == -1) {
                    throw new IOException(
                            "Unable to parse the HTTP version. End of stream reached too early.");
                } else {
                    next = headStream.read();

                    if (HttpUtils.isLineFeed(next)) {
                        setVersion(sb.toString());
                        sb.delete(0, sb.length());

                        // Parse the headers
                        Parameter header = HttpUtils.readHeader(headStream, sb);
                        while (header != null) {
                            getRequestHeaders().add(header);
                            header = HttpUtils.readHeader(headStream, sb);
                        }
                    } else {
                        throw new IOException(
                                "Unable to parse the HTTP version. The carriage return must be followed by a line feed.");
                    }
                }
            }
        }
    }

    /**
     * Sends the response back to the client. Commits the status, headers and
     * optional entity and send them over the network. The default
     * implementation only writes the response entity on the reponse stream or
     * channel. Subclasses will probably also copy the response headers and
     * status.
     *
     * @param response
     *                The high-level response.
     */
    public void sendResponse(Response response) throws IOException {
        if (response != null) {
            writeResponseHead(response);
            Representation entity = response.getEntity();

            if ((entity != null)
                    && !response.getRequest().getMethod().equals(Method.HEAD)
                    && !response.getStatus().equals(Status.SUCCESS_NO_CONTENT)
                    && !response.getStatus().equals(
                            Status.SUCCESS_RESET_CONTENT)) {
                // Get the connector service to callback
                ConnectorService connectorService = getConnectorService(response
                        .getRequest());
                if (connectorService != null)
                    connectorService.beforeSend(entity);

                writeResponseBody(entity);

                if (connectorService != null)
                    connectorService.afterSend(entity);
            }

            if (getResponseStream() != null) {
                try {
                    getResponseStream().flush();
                } catch (IOException ioe) {
                    // The stream was probably already closed by the
                    // connector. Probably ok, low message priority.
                    getLogger()
                            .log(
                                    Level.FINE,
                                    "Exception while flushing and closing the entity stream.",
                                    ioe);
                }
            }
        }
    }

    /**
     * Effectively writes the response body. The entity to write is guaranteed
     * to be non null. Attempts to write the entity on the response channel or
     * response stream by default.
     *
     * @param entity
     *                The representation to write as entity of the body.
     * @throws IOException
     */
    public void writeResponseBody(Representation entity) throws IOException {
        // Send the entity to the client
        if (getResponseChannel() != null) {
            entity.write(getResponseChannel());
        } else if (getResponseStream() != null) {
            entity.write(getResponseStream());
        }
    }

    /**
     * Writes the response status line and headers. Does nothing by default.
     *
     * @param response
     *                The response.
     * @throws IOException
     */
    public void writeResponseHead(Response response) throws IOException {
        // Do nothing by default
    }

    /**
     * Writes the response head to the given output stream.
     *
     * @param headStream
     *                The output stream to write to.
     * @throws IOException
     */
    protected void writeResponseHead(OutputStream headStream)
            throws IOException {
        // Write the status line
        headStream.write(getVersion().getBytes());
        headStream.write(' ');
        headStream.write(Integer.toString(getStatusCode()).getBytes());
        headStream.write(' ');
        headStream.write(getReasonPhrase().getBytes());
        headStream.write(13); // CR
        headStream.write(10); // LF

        // We don't support persistent connections yet
        getResponseHeaders()
                .set(HttpConstants.HEADER_CONNECTION, "close", true);

        // Write the response headers
        for (Parameter header : getResponseHeaders()) {
            HttpUtils.writeHeader(header, headStream);
        }

        // Write the end of the headers section
        headStream.write(13); // CR
        headStream.write(10); // LF
    }

}
TOP

Related Classes of com.noelios.restlet.http.HttpServerCall

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.