com.heliosapm.tsdblite.handlers.http.TSDBHttpRequest.java Source code

Java tutorial

Introduction

Here is the source code for com.heliosapm.tsdblite.handlers.http.TSDBHttpRequest.java

Source

/*
 * Copyright 2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.heliosapm.tsdblite.handlers.http;

import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import com.google.common.net.HttpHeaders;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
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.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;

/**
 * <p>Title: TSDBHttpRequest</p>
 * <p>Description: Wraps up a channel and the http request it is carrying</p> 
 * <p>Company: Helios Development Group LLC</p>
 * @author Whitehead (nwhitehead AT heliosdev DOT org)
 * <p><code>com.heliosapm.tsdblite.handlers.TSDBHttpRequest</code></p>
 */

public class TSDBHttpRequest {
    /** The incoming HTTP request */
    protected final HttpRequest request;
    /** The channel the request came in on */
    protected final Channel channel;
    /** The channel handler context of the http request router */
    protected final ChannelHandlerContext ctx;
    /** The request path */
    protected final String path;
    /** The request path elements */
    protected final String[] pathElements;

    /** The routing key */
    protected final String route;
    /** The lazilly created Query decoder */
    protected volatile QueryStringDecoder decoder = null;
    /** The decoded parameters */
    protected volatile Map<String, List<String>> params = null;

    /** The path splitter regex */
    public static final Pattern PATH_SPLIT = Pattern.compile("/|\\?");
    /** An empty buffer const */
    public static final ByteBuf EMPTY_BUFF = Unpooled.EMPTY_BUFFER;
    /** UTF8 Character Set */
    public static final Charset UTF8 = Charset.forName("UTF8");

    /**
     * Creates a new TSDBHttpRequest
     * @param request The incoming HTTP request
     * @param channel The channel the request came in on
     * @param ctx The http request router's channel handler context
     */
    protected TSDBHttpRequest(final HttpRequest request, final Channel channel, final ChannelHandlerContext ctx) {
        this.request = request;
        this.channel = channel;
        this.ctx = ctx;
        final QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
        path = decoder.path();
        final StringBuilder b = new StringBuilder("/api/");
        pathElements = PATH_SPLIT.split(path);
        for (String part : pathElements) {
            if (part == null || part.trim().isEmpty() || "api".equals(part)) {
                continue;
            }
            b.append(part);
            break;
        }
        route = b.toString();
    }

    /**
     * Decodes the uri
     */
    protected void decode() {
        if (decoder == null) {
            decoder = new QueryStringDecoder(request.uri());
        }
    }

    /**
     * Returns the query parameters
     * @return the query parameters
     */
    public Map<String, List<String>> getParameters() {
        if (params == null) {
            decode();
            params = decoder.parameters();
        }
        return params;
    }

    /**
     * Returns the named parameter
     * @param key The parameter name
     * @return The parameter value or null if not found
     */
    public String getParameter(final String key) {
        if (key == null || key.trim().isEmpty())
            throw new IllegalArgumentException("The passed key was null or empty");
        final List<String> list = getParameters().get(key.trim());
        if (list == null)
            return null;
        return list.get(0);
    }

    /**
     * Returns the HTTP request
     * @return the HTTP request
     */
    public HttpRequest getRequest() {
        return request;
    }

    /**
     * Returns the channel
     * @return the channel
     */
    public Channel getChannel() {
        return channel;
    }

    /**
     * Returns the path 
     * @return the path
     */
    public String getPath() {
        return path;
    }

    /**
     * Returns the path elements
     * @return the path elements
     */
    public String[] getPathElements() {
        return pathElements;
    }

    /**
     * Returns the route
     * @return the route
     */
    public String getRoute() {
        return route;
    }

    /**
     * Sends a 404 (Not Found) response
     * @return the write completion future
     */
    public ChannelFuture send404() {
        return sendResponse(response(HttpResponseStatus.NOT_FOUND, "No route for [", route, "]"));
    }

    /**
     * Sends a 40o (Bad Request) response
     * @param msgs An optional array of message segments to be concatenated and set as the response body
     * @return the write completion future
     */
    public ChannelFuture send400(final String... msgs) {
        return sendResponse(response(HttpResponseStatus.BAD_REQUEST, msgs));
    }

    /**
     * Sends a 204 (No Content) response
     * @return the write completion future
     */
    public ChannelFuture send204() {
        return sendResponse(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NO_CONTENT));
    }

    private static HttpResponse response(final HttpResponseStatus status, final String... msgs) {
        final ByteBuf buf = join(msgs);
        if (buf.readableBytes() == 0) {
            return new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
        }
        final DefaultFullHttpResponse resp = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, buf);
        resp.headers().setInt(HttpHeaders.CONTENT_LENGTH, buf.readableBytes());
        resp.headers().set(HttpHeaders.CONTENT_TYPE, "text/plain");
        return resp;
    }

    private static ByteBuf join(final String... msgs) {
        final String s;
        if (msgs.length == 0 || msgs[0] == null) {
            return EMPTY_BUFF;
        }
        if (msgs.length == 1) {
            s = msgs[0];
        } else {
            final StringBuilder b = new StringBuilder();
            for (String msg : msgs) {
                b.append(msg);
            }
            s = b.toString();
        }
        return Unpooled.copiedBuffer(s.getBytes(UTF8));
    }

    /**
     * Indicates if the request has a readable content body
     * @return true if the request has a readable content body, false otherwise
     */
    public boolean hasContent() {
        if (request instanceof FullHttpRequest) {
            final ByteBuf content = ((FullHttpRequest) request).content();
            if (content == null)
                return false;
            if (content.readableBytes() < 1)
                return false;
            return true;
        }
        return false;
    }

    /**
     * Returns the http request content
     * @return the http request content
     */
    public ByteBuf getContent() {
        if (request instanceof FullHttpRequest) {
            final ByteBuf bb = ((FullHttpRequest) request).content();
            return bb == null ? EMPTY_BUFF : bb;
        }
        return EMPTY_BUFF;
    }

    /**
     * Sends an HTTP response to the caller
     * @param response The response to send
     * @return the response write completion future
     */
    public ChannelFuture sendResponse(final HttpResponse response) {
        if (response == null) {
            ctx.writeAndFlush(
                    new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR));
            throw new IllegalArgumentException("The passed HTTP response was null");
        }

        return ctx.writeAndFlush(response);
    }

    /**
     * Returns the channel handler context
     * @return the channel handler context
     */
    public ChannelHandlerContext context() {
        return ctx;
    }

}