Skip to content

Commit

Permalink
Added BBtoHttpResponse Utility class for DRY
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeff Rose committed Sep 25, 2015
1 parent 4aa7021 commit d1fe682
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 135 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<groupId>com.xjeffrose</groupId>
<artifactId>xio</artifactId>
<version>0.8.2-SNAPSHOT</version>
<version>0.8.3-SNAPSHOT</version>
<packaging>jar</packaging>
<name>xio</name>
<description>High Performance HTTP Server based on Netty and Xio</description>
Expand Down
78 changes: 44 additions & 34 deletions src/main/java/com/xjeffrose/xio/client/HttpClientChannel.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,29 @@

import com.google.common.net.HttpHeaders;
import com.xjeffrose.xio.core.XioException;
import com.xjeffrose.xio.core.XioTransportException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObject;
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.util.Timer;
import java.net.URI;
import java.nio.charset.Charset;
import java.util.Map;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import org.apache.log4j.Logger;

import static io.netty.handler.codec.http.HttpHeaders.Values.CLOSE;
import static io.netty.handler.codec.http.HttpHeaders.Values.GZIP;


@NotThreadSafe
public class HttpClientChannel extends AbstractClientChannel {
private static final Logger log = Logger.getLogger(HttpClientChannel.class.getName());
Expand Down Expand Up @@ -58,7 +61,8 @@ protected ByteBuf extractResponse(Object message) throws XioException {

HttpResponse httpResponse = null;
HttpContent httpContent = null;

ByteBuf content = null;

XioClientChannel xioClientChannel;

if (message instanceof HttpResponse) {
Expand All @@ -67,9 +71,19 @@ protected ByteBuf extractResponse(Object message) throws XioException {

if (message instanceof HttpContent) {
httpContent = (HttpContent) message;

if (httpContent.getDecoderResult() == DecoderResult.SUCCESS) {
content = httpContent.content();
}

if (content != null) {
if (!content.isReadable()) {
return null;
}
}
}

//TODO(JR): Leave out for testing, ADD BACK BEFORE DEPLOYMENT!!!!!
//TODO(JR): Leave out for testing, ADD BACK BEFORE DEPLOYMENT!!!!!
// switch (httpResponse.getStatus().reasonPhrase()) {
// case("Unknown Status"):
// throw wrapException(new XioTransportException("HTTP response had non-OK status: " + httpResponse
Expand All @@ -86,43 +100,36 @@ protected ByteBuf extractResponse(Object message) throws XioException {
// }

// HttpContent httpContent = (HttpContent) httpResponse;
ByteBuf content = null;

if (httpContent != null) {
content = httpContent.content();
}

if (!content.isReadable()) {
return null;
}
String CRLF = "\r\n";
StringBuilder responseHeader = new StringBuilder();
responseHeader
.append(httpResponse.getProtocolVersion())
.append(' ')
.append(httpResponse.getStatus())
.append(CRLF);

String CRLF = "\r\n";
StringBuilder responseHeader = new StringBuilder();
httpResponse.headers().entries().forEach(xs -> {
responseHeader
.append(httpResponse.getProtocolVersion())
.append(' ')
.append(httpResponse.getStatus())
.append(xs.getKey())
.append(": ")
.append(xs.getValue())
.append(CRLF);
});

httpResponse.headers().entries().forEach(xs -> {
responseHeader
.append(xs.getKey())
.append(": ")
.append(xs.getValue())
.append(CRLF);
});

responseHeader.append(CRLF);
responseHeader.append(CRLF);

ByteBuf headerAndBody = getCtx().alloc().buffer();
headerAndBody.writeBytes(responseHeader.toString().getBytes(Charset.defaultCharset()));
headerAndBody.writeBytes(content);
return headerAndBody;
}
ByteBuf headerAndBody = getCtx().alloc().buffer();
headerAndBody.writeBytes(responseHeader.toString().getBytes(Charset.defaultCharset()));
headerAndBody.writeBytes(content);
headerAndBody.writeBytes("\r\n".getBytes());
return headerAndBody;
}

@Override
protected ChannelFuture writeRequest(ByteBuf request) {
HttpRequest httpRequest;
protected ChannelFuture writeRequest(@Nullable ByteBuf request) {
DefaultFullHttpRequest httpRequest;

if (request == Unpooled.EMPTY_BUFFER || request == null) {
httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getPath());
Expand All @@ -138,7 +145,10 @@ protected ChannelFuture writeRequest(ByteBuf request) {
}
}

log.debug(httpRequest);
httpRequest.headers().set(HttpHeaders.CONNECTION, CLOSE);
// httpRequest.headers().set(HttpHeaders.ACCEPT_ENCODING, GZIP);

log.debug("HTTP Request from XIO:\n" + httpRequest);

return underlyingNettyChannel.writeAndFlush(httpRequest);
}
Expand Down
23 changes: 9 additions & 14 deletions src/main/java/com/xjeffrose/xio/client/HttpClientConnector.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import com.xjeffrose.xio.core.XioExceptionLogger;
import com.xjeffrose.xio.core.XioSecurityHandlers;
import com.xjeffrose.xio.server.IdleDisconnectHandler;
import io.airlift.units.Duration;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
Expand All @@ -23,7 +22,6 @@
import java.net.URISyntaxException;
import java.util.concurrent.TimeUnit;


import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
Expand Down Expand Up @@ -103,18 +101,15 @@ protected void initChannel(SocketChannel channel) throws Exception {
cp.addLast("httpClientCodec", new HttpClientCodec());
cp.addLast("chunkAggregator", new HttpObjectAggregator(maxFrameSize));
cp.addLast("defaltor", new HttpContentDecompressor());
// if (def.getIdleTimeout() != null) {
cp.addLast("idleTimeoutHandler", new IdleStateHandler(
20000,
NO_WRITER_IDLE_TIMEOUT,
NO_ALL_IDLE_TIMEOUT,
TimeUnit.MILLISECONDS));
cp.addLast("idleDisconnectHandler", new IdleDisconnectHandler(
2000,
NO_WRITER_IDLE_TIMEOUT,
NO_ALL_IDLE_TIMEOUT));
// }

cp.addLast("idleTimeoutHandler", new IdleStateHandler(
20000,
NO_WRITER_IDLE_TIMEOUT,
NO_ALL_IDLE_TIMEOUT,
TimeUnit.MILLISECONDS));
cp.addLast("idleDisconnectHandler", new IdleDisconnectHandler(
2000,
NO_WRITER_IDLE_TIMEOUT,
NO_ALL_IDLE_TIMEOUT));
cp.addLast("authHandler", securityHandlers.getAuthenticationHandler());
cp.addLast("exceptionLogger", new XioExceptionLogger());
}
Expand Down
20 changes: 2 additions & 18 deletions src/main/java/com/xjeffrose/xio/client/XioClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.google.common.net.HostAndPort;
import com.google.common.net.HttpHeaders;
import com.google.common.util.concurrent.ListenableFuture;
import com.xjeffrose.xio.core.BBtoHttpResponse;
import com.xjeffrose.xio.core.ShutdownUtil;
import com.xjeffrose.xio.core.XioException;
import io.airlift.units.Duration;
Expand Down Expand Up @@ -169,24 +170,7 @@ public ByteBuf getResponse() {
lock.unlock();


// Lets make a HTTP parser cause apparently that's a good idea...
ByteBuf response = listener.getResponse();
String[] headerBody = response.toString(Charset.defaultCharset()).split("\r\n\r\n");
String[] headers = headerBody[0].split("\r\n");
String[] firstLine = headers[0].split("\\s");

// Lets make a HTTP Response object now
DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(
HttpVersion.valueOf(firstLine[0]),
new HttpResponseStatus(Integer.parseInt(firstLine[1]), firstLine[2]),
Unpooled.wrappedBuffer(headerBody[1].getBytes()));

for (int i = 1; i < headers.length; i++) {
String[] xs = headers[i].split(":");
httpResponse.headers().add(xs[0].trim(), xs[1].trim());
}

return httpResponse;
return BBtoHttpResponse.getResponse(listener.getResponse());
}

@SuppressWarnings("unchecked")
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/com/xjeffrose/xio/core/BBtoHttpResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.xjeffrose.xio.core;

import com.google.common.base.Joiner;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import java.nio.charset.Charset;
import java.util.Arrays;
import javax.annotation.Nullable;

public class BBtoHttpResponse {

private BBtoHttpResponse() { }

public static DefaultFullHttpResponse getResponse(ByteBuf byteBuf) {
return BBtoHttpResponse.getResponse(null, byteBuf);
}

public static DefaultFullHttpResponse getResponse(@Nullable ChannelHandlerContext ctx, ByteBuf byteBuf) {
// Lets make a HTTP parser cause apparently that's a good idea...
final ByteBuf response = byteBuf.duplicate();
final Joiner joiner = Joiner.on(" ").skipNulls();
DefaultFullHttpResponse httpResponse;

String[] headerBody = response.toString(Charset.defaultCharset()).split("\r\n\r\n");
String[] headers = headerBody[0].split("\r\n");
String[] firstLine = headers[0].split("\\s");

// Lets make a HTTP Response object now
if (ctx == null) {
httpResponse = new DefaultFullHttpResponse(
HttpVersion.valueOf(firstLine[0]),
new HttpResponseStatus(Integer.parseInt(firstLine[1]), joiner.join(Arrays.copyOfRange(firstLine, 2, 5))),
Unpooled.wrappedBuffer(headerBody[1].getBytes()));
} else {
httpResponse = new DefaultFullHttpResponse(
HttpVersion.valueOf(firstLine[0]),
new HttpResponseStatus(Integer.parseInt(firstLine[1]), joiner.join(Arrays.copyOfRange(firstLine, 2 , 5))),
ctx.alloc().buffer().writeBytes(headerBody[1].getBytes()));
}

for (int i = 1; i < headers.length; i++) {
String[] xs = headers[i].split(":");
httpResponse.headers().add(xs[0].trim(), xs[1].trim());
}

return httpResponse;
}
}
36 changes: 3 additions & 33 deletions src/test/java/com/xjeffrose/xio/client/XioClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.net.HttpHeaders;
import com.google.common.util.concurrent.ListenableFuture;
import com.xjeffrose.xio.core.BBtoHttpResponse;
import com.xjeffrose.xio.core.XioException;
import com.xjeffrose.xio.core.XioNoOpHandler;
import com.xjeffrose.xio.core.XioSecurityFactory;
Expand Down Expand Up @@ -168,22 +169,7 @@ public ByteBuf getResponse() {
waitForFinish.await();
lock.unlock();

// Lets make a HTTP parser cause apparently that's a good idea...
ByteBuf response = listener.getResponse();
String[] headerBody = response.toString(Charset.defaultCharset()).split("\r\n\r\n");
String[] headers = headerBody[0].split("\r\n");
String[] firstLine = headers[0].split("\\s");

// Lets make a HTTP Response object now
DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(
HttpVersion.valueOf(firstLine[0]),
new HttpResponseStatus(Integer.parseInt(firstLine[1]), firstLine[2]),
httpClientChannel.getCtx().alloc().buffer().writeBytes(headerBody[1].getBytes()));

for (int i = 1; i < headers.length; i++) {
String[] xs = headers[i].split(":");
httpResponse.headers().add(xs[0].trim(), xs[1].trim());
}
DefaultFullHttpResponse httpResponse = BBtoHttpResponse.getResponse(listener.getResponse());

//Now we have something that we can actually test ...
assertEquals(HttpResponseStatus.OK, httpResponse.getStatus());
Expand Down Expand Up @@ -257,23 +243,7 @@ public ByteBuf getResponse() {
waitForFinish.await();
lock.unlock();


// Lets make a HTTP parser cause apparently that's a good idea...
ByteBuf response = listener.getResponse();
String[] headerBody = response.toString(Charset.defaultCharset()).split("\r\n\r\n");
String[] headers = headerBody[0].split("\r\n");
String[] firstLine = headers[0].split("\\s");

// Lets make a HTTP Response object now
DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(
HttpVersion.valueOf(firstLine[0]),
new HttpResponseStatus(Integer.parseInt(firstLine[1]), firstLine[2]),
httpClientChannel.getCtx().alloc().buffer().writeBytes(headerBody[1].getBytes()));

for (int i = 1; i < headers.length; i++) {
String[] xs = headers[i].split(":");
httpResponse.headers().add(xs[0].trim(), xs[1].trim());
}
DefaultFullHttpResponse httpResponse = BBtoHttpResponse.getResponse(listener.getResponse());

//Now we have something that we can actually test ...
assertEquals(HttpResponseStatus.OK, httpResponse.getStatus());
Expand Down
31 changes: 31 additions & 0 deletions src/test/java/com/xjeffrose/xio/core/BBtoHttpResponseTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.xjeffrose.xio.core;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.junit.Test;

import static org.junit.Assert.*;

public class BBtoHttpResponseTest {

@Test
public void testGetResponseOK() throws Exception {
ByteBuf bb = Unpooled.wrappedBuffer("HTTP/1.1 200 OK\r\nServer: xio\r\n\r\n\r\n".getBytes());
DefaultFullHttpResponse response = BBtoHttpResponse.getResponse(bb);

assertEquals(HttpResponseStatus.OK, response.getStatus());
assertEquals("xio", response.headers().get("Server"));

}

@Test
public void testGetResponseServerError() throws Exception {
ByteBuf bb = Unpooled.wrappedBuffer("HTTP/1.1 500 Internal Server Error\r\nServer: xio\r\n\r\n\r\n".getBytes());
DefaultFullHttpResponse response = BBtoHttpResponse.getResponse(bb);

assertEquals(HttpResponseStatus.INTERNAL_SERVER_ERROR, response.getStatus());
assertEquals("xio", response.headers().get("Server"));
}
}
Loading

0 comments on commit d1fe682

Please sign in to comment.