/*
* TeleStax, Open Source Cloud Communications
* Copyright 2011-2015, Telestax Inc and individual contributors
* by the @authors tag.
*
* This program is free software: you can redistribute it and/or modify
* under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package org.mobicents.tools.http.balancer;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.Cookie;
import org.jboss.netty.handler.codec.http.CookieDecoder;
import org.jboss.netty.handler.codec.http.CookieEncoder;
import org.jboss.netty.handler.codec.http.DefaultHttpChunk;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
/**
* @author Konstantin Nosach (kostyantyn.nosach@telestax.com)
*/
public class HttpServerRequestHandler extends SimpleChannelUpstreamHandler {
private static final String F_NAME = "fname";
private static final String L_NAME = "lname";
private static final int PARAM_NAME_IDX = 0;
private static final int PARAM_VALUE_IDX = 1;
private static final String AND_DELIMITER = "&";
private static final String EQUAL_DELIMITER = "=";
private AtomicInteger requestCount;
private volatile boolean readingChunks;
private HttpRequest request;
private List <String> requests;
private boolean chunk = false;
private boolean badServer = false;
public HttpServerRequestHandler(AtomicInteger requestCount,List <String> requests)
{
this.requestCount = requestCount;
this.requests = requests;
}
public HttpServerRequestHandler(AtomicInteger requestCount,List <String> requests, boolean chunk, boolean badServer)
{
this(requestCount, requests);
this.chunk = chunk;
this.badServer = badServer;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
Thread.sleep(2000);
Object msg = e.getMessage();
if ((msg instanceof HttpRequest) || (msg instanceof DefaultHttpChunk)) {
handle(ctx, e);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
}
public void handle(ChannelHandlerContext ctx,MessageEvent e) throws IOException {
if (!readingChunks) {
request = (HttpRequest) e.getMessage();
requests.add(request.getUri());
if (request.isChunked())
readingChunks = true;
else
{
requestCount.incrementAndGet();
try
{
String response = createResponseFromQueryParams(new URI(request.getUri()));
if(!badServer)
writeResponse(e, HttpResponseStatus.OK, response);
else
writeResponse(e, HttpResponseStatus.BAD_REQUEST, response);
}
catch(Exception ex)
{
}
}
}
else
{
HttpChunk chunk = (HttpChunk) e.getMessage();
if (chunk.isLast())
readingChunks = false;
}
}
@SuppressWarnings("deprecation")
private void writeResponse(MessageEvent e, HttpResponseStatus status, String responseString) {
// Convert the response content to a ChannelBuffer.
if(chunk)
for(int i = 0; i < 1000; i++)
responseString+="HOW MUCH IS THE FISH";
ChannelBuffer buf = ChannelBuffers.copiedBuffer(responseString, Charset.forName("UTF-8"));
// Decide whether to close the connection or not.
boolean close =
HttpHeaders.Values.CLOSE.equalsIgnoreCase(request.getHeader(HttpHeaders.Names.CONNECTION)) ||
request.getProtocolVersion().equals(HttpVersion.HTTP_1_0) &&
!HttpHeaders.Values.KEEP_ALIVE.equalsIgnoreCase(request.getHeader(HttpHeaders.Names.CONNECTION));
// Build the response object.
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
if(!chunk)
response.setContent(buf);
response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8");
if(chunk)
response.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, "chunked");
if(!chunk)
if (!close) {
// There's no need to add 'Content-Length' header
// if this is the last response.
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buf.readableBytes()));
}
String cookieString = request.getHeader(HttpHeaders.Names.COOKIE);
if (cookieString != null) {
CookieDecoder cookieDecoder = new CookieDecoder();
Set<Cookie> cookies = cookieDecoder.decode(cookieString);
if(!cookies.isEmpty()) {
// Reset the cookies if necessary.
CookieEncoder cookieEncoder = new CookieEncoder(true);
for (Cookie cookie : cookies) {
cookieEncoder.addCookie(cookie);
}
response.addHeader(HttpHeaders.Names.SET_COOKIE, cookieEncoder.encode());
}
}
// Write the response.
ChannelFuture future = e.getChannel().write(response);
if(chunk)
{
while(buf.readableBytes()>0)
{
int maxBytes=1000;
if(buf.readableBytes()<1000)
maxBytes=buf.readableBytes();
HttpChunk currChunk=new DefaultHttpChunk(buf.readBytes(maxBytes));
future=e.getChannel().write(currChunk);
}
HttpChunk currChunk=new DefaultHttpChunk(buf);
future=e.getChannel().write(currChunk);
}
// Close the connection after the write operation is done if necessary.
if (close) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
/**
* Creates the response from query params.
*
* @param uri the uri
* @return the string
*/
private String createResponseFromQueryParams(URI uri) {
String fName = "";
String lName = "";
//Get the request query
String query = uri.getQuery();
if (query != null) {
String[] queryParams = query.split(AND_DELIMITER);
if (queryParams.length > 0) {
for (String qParam : queryParams) {
String[] param = qParam.split(EQUAL_DELIMITER);
if (param.length > 0) {
for (int i = 0; i < param.length; i++) {
if (F_NAME.equalsIgnoreCase(param[PARAM_NAME_IDX])) {
fName = param[PARAM_VALUE_IDX];
}
if (L_NAME.equalsIgnoreCase(param[PARAM_NAME_IDX])) {
lName = param[PARAM_VALUE_IDX];
}
}
}
}
}
}
return "Hello, " + fName + " " + lName;
}
}