org.restlet.ext.netty.internal.NettyServerCall.java Source code

Java tutorial

Introduction

Here is the source code for org.restlet.ext.netty.internal.NettyServerCall.java

Source

/**
 * Copyright 2005-2014 Restlet
 * 
 * The contents of this file are subject to the terms of one of the following
 * open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
 * select the license that you prefer but you may not use this file except in
 * compliance with one of these Licenses.
 * 
 * You can obtain a copy of the Apache 2.0 license at
 * http://www.opensource.org/licenses/apache-2.0
 * 
 * You can obtain a copy of the EPL 1.0 license at
 * http://www.opensource.org/licenses/eclipse-1.0
 * 
 * See the Licenses for the specific language governing permissions and
 * limitations under the Licenses.
 * 
 * Alternatively, you can obtain a royalty free commercial license with less
 * limitations, transferable or non-transferable, directly at
 * http://restlet.com/products/restlet-framework
 * 
 * Restlet is a registered trademark of Restlet S.A.S.
 */

package org.restlet.ext.netty.internal;

import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpChunkedInput;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.stream.ChunkedStream;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.logging.Level;

import javax.net.ssl.SSLEngine;

import org.restlet.Response;
import org.restlet.Server;
import org.restlet.data.Header;
import org.restlet.engine.adapter.ServerCall;
import org.restlet.representation.Representation;
import org.restlet.util.Series;

/**
 * 
 * @author Jerome Louvel
 */
public class NettyServerCall extends ServerCall {

    private final ChannelHandlerContext nettyContext;

    private volatile HttpContentInputStream nettyEntityStream;

    private final HttpRequest nettyRequest;

    private volatile HttpResponse nettyResponse;

    /** Indicates if the request headers were parsed and added. */
    private volatile boolean requestHeadersAdded;

    public NettyServerCall(Server server, ChannelHandlerContext nettyContext, HttpRequest httpRequest) {
        super(server);
        this.nettyContext = nettyContext;
        this.nettyRequest = httpRequest;
        this.nettyResponse = null;
        this.requestHeadersAdded = false;
    }

    @Override
    public boolean abort() {
        try {
            getNettyContext().close().sync();
            return true;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public void complete() {
        getNettyContext().flush();
    }

    @Override
    public void flushBuffers() throws IOException {
        getNettyContext().flush();
    }

    @Override
    public String getClientAddress() {
        InetSocketAddress isa = (InetSocketAddress) getNettyContext().channel().remoteAddress();
        return isa.getHostString();
    }

    @Override
    public int getClientPort() {
        InetSocketAddress isa = (InetSocketAddress) getNettyContext().channel().remoteAddress();
        return isa.getPort();
    }

    @Override
    public String getMethod() {
        return getNettyRequest().method().name();
    }

    protected ChannelHandlerContext getNettyContext() {
        return nettyContext;
    }

    protected HttpContentInputStream getNettyEntityStream() {
        if (this.nettyEntityStream == null) {
            this.nettyEntityStream = new HttpContentInputStream(getNettyContext());
        }

        return this.nettyEntityStream;
    }

    protected HttpRequest getNettyRequest() {
        return nettyRequest;
    }

    protected HttpResponse getNettyResponse() {
        return nettyResponse;
    }

    @Override
    public InputStream getRequestEntityStream(long size) {
        return getNettyEntityStream();
    }

    @Override
    public Series<Header> getRequestHeaders() {
        final Series<Header> result = super.getRequestHeaders();

        if (!this.requestHeadersAdded) {
            final Iterable<Map.Entry<String, String>> headers = getNettyRequest().headers();

            for (Map.Entry<String, String> header : headers) {
                result.add(header.getKey(), header.getValue());
            }

            this.requestHeadersAdded = true;
        }

        return result;
    }

    @Override
    public InputStream getRequestHeadStream() {
        return null;
    }

    @Override
    public String getRequestUri() {
        return getNettyRequest().uri();
    }

    @Override
    public OutputStream getResponseEntityStream() {
        return null;
    }

    @Override
    protected SSLEngine getSslEngine() {
        // TODO
        return null;
    }

    @Override
    public String getVersion() {
        String result = null;
        final int index = getNettyRequest().protocolVersion().text().indexOf('/');

        if (index != -1) {
            result = getNettyRequest().protocolVersion().text().substring(index + 1);
        }

        return result;
    }

    public void onContent(HttpContent httpContent) throws IOException {
        ByteBuf content = httpContent.content();

        if (content.isReadable()) {
            getNettyEntityStream().onContent(content, httpContent instanceof LastHttpContent);
        }
    }

    protected void setNettyResponse(HttpResponse nettyResponse) {
        this.nettyResponse = nettyResponse;
    }

    @Override
    protected void writeResponseBody(Representation responseEntity) throws IOException {
        try {
            // Send the entity to the client
            InputStream is = responseEntity.getStream();
            getNettyContext().write(new HttpChunkedInput(new ChunkedStream(is)));
            getNettyContext().writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
        } catch (IOException ioe) {
            // The stream was probably already closed by the
            // connector. Probably OK, low message priority.
            getLogger().log(Level.FINE, "Exception while writing the entity stream.", ioe);
        }
    }

    @Override
    public void writeResponseHead(org.restlet.Response restletResponse) throws IOException {
        setNettyResponse(
                new DefaultHttpResponse(HTTP_1_1, new HttpResponseStatus(getStatusCode(), getReasonPhrase())));
        HttpHeaders headers = getNettyResponse().headers();

        // this.response.clear();
        for (Header header : getResponseHeaders()) {
            headers.add(header.getName(), header.getValue());
        }

        // Decide whether to close the connection or not.
        if (isKeepAlive()) {
            headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
            getNettyContext().write(getNettyResponse());
        } else {
            getNettyContext().writeAndFlush(getNettyResponse()).addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    protected void writeResponseTail(Response response) {
        if (!isKeepAlive()) {
            // Close the connection once the content is fully written.
            getNettyContext().writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
        }
    }

}