Skip to content

Commit

Permalink
yegor256#306: Modify BkBasic for persistent conections according to H…
Browse files Browse the repository at this point in the history
…TTP 1.1
  • Loading branch information
lautarobock committed Jun 8, 2015
1 parent fa66e82 commit 5abbe05
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 6 deletions.
46 changes: 41 additions & 5 deletions src/main/java/org/takes/http/BkBasic.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.util.Iterator;
import lombok.EqualsAndHashCode;
import org.takes.HttpException;
import org.takes.Request;
import org.takes.Response;
import org.takes.Take;
import org.takes.rq.RqHeaders;
import org.takes.rq.RqLive;
import org.takes.rq.RqWithHeaders;
import org.takes.rs.RsPrint;
import org.takes.rs.RsText;
import org.takes.rs.RsWithHeader;
import org.takes.rs.RsWithStatus;

/**
Expand All @@ -57,6 +60,16 @@
@EqualsAndHashCode(of = "take")
public final class BkBasic implements Back {

/**
* Keep alive header key.
*/
private static final String CONNECTION = "Connection";

/**
* Keep alive header value.
*/
private static final String KEEP_ALIVE = "Keep-Alive";

/**
* Take.
*/
Expand All @@ -73,30 +86,53 @@ public BkBasic(final Take tks) {
@Override
public void accept(final Socket socket) throws IOException {
final InputStream input = socket.getInputStream();
final RqLive req = new RqLive(input);
boolean keep = false;
final Iterator<String> values = new RqHeaders.Base(req)
.header(CONNECTION).iterator();
if (values.hasNext()) {
do {
keep = KEEP_ALIVE.equals(values.next());
} while (!keep && values.hasNext());
}
try {
this.print(
BkBasic.addSocketHeaders(
new RqLive(input),
req,
socket
),
new BufferedOutputStream(socket.getOutputStream())
new BufferedOutputStream(socket.getOutputStream()),
keep
);
} finally {
input.close();
if (!keep) {
input.close();
}
}
}

/**
* Print response to output stream, safely.
* @param req Request
* @param output Output
* @param keep Keep connection open
* @throws IOException If fails
*/
@SuppressWarnings("PMD.AvoidCatchingThrowable")
private void print(final Request req, final OutputStream output)
private void print(
final Request req, final OutputStream output, final boolean keep
)
throws IOException {
try {
new RsPrint(this.take.act(req)).print(output);
final Response res;
if (keep) {
res = new RsWithHeader(
this.take.act(req), CONNECTION, KEEP_ALIVE
);
} else {
res = this.take.act(req);
}
new RsPrint(res).print(output);
} catch (final HttpException ex) {
new RsPrint(BkBasic.failure(ex, ex.code())).print(output);
// @checkstyle IllegalCatchCheck (7 lines)
Expand Down
76 changes: 75 additions & 1 deletion src/test/java/org/takes/http/BkBasicTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@
* @checkstyle ClassDataAbstractionCouplingCheck (500 lines)
*/
public final class BkBasicTest {

/**
* Keep alive header value.
*/
private static final String KEEP_ALIVE =
"Connection: Keep-Alive";

/**
* BkBasic can handle socket data.
* @throws IOException If some problem inside
Expand All @@ -59,7 +66,7 @@ public void handlesSocket() throws IOException {
final Socket socket = Mockito.mock(Socket.class);
Mockito.when(socket.getInputStream()).thenReturn(
new ByteArrayInputStream(
Joiner.on("\r\n").join(
this.joiner().join(
"GET / HTTP/1.1",
"Host:localhost",
"Content-Length: 2",
Expand All @@ -85,6 +92,64 @@ public void handlesSocket() throws IOException {
);
}

/**
* BkBasic supports HTTP persistent connections.
* @throws IOException If some problem inside
*/
@Test
public void persistentConnection() throws IOException {
final Socket socket = Mockito.mock(Socket.class);
final ByteArrayInputStream input = Mockito.spy(
new ByteArrayInputStream(
this.joiner().join(
"GET /keep HTTP/1.1",
"Host:localhost2",
"Content-Length: 5",
KEEP_ALIVE,
"",
"hello"
).getBytes()
)
);
Mockito.when(socket.getInputStream()).thenReturn(input);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
Mockito.when(socket.getOutputStream()).thenReturn(baos);
new BkBasic(new TkText("Hello!")).accept(socket);
Mockito.verify(input, Mockito.never()).close();
MatcherAssert.assertThat(
baos.toString(),
Matchers.containsString(KEEP_ALIVE)
);
}

/**
* BkBasic not supports HTTP persistent connections.
* @throws IOException If some problem inside
*/
@Test
public void notPersistentConnection() throws IOException {
final Socket socket = Mockito.mock(Socket.class);
final ByteArrayInputStream input = Mockito.spy(
new ByteArrayInputStream(
this.joiner().join(
"GET /close HTTP/1.1",
"Host:localhost3",
"Content-Length: 6",
"",
"hi all"
).getBytes()
)
);
Mockito.when(socket.getInputStream()).thenReturn(input);
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
Mockito.when(socket.getOutputStream()).thenReturn(baos);
new BkBasic(new TkText("Hello")).accept(socket);
Mockito.verify(input, Mockito.times(1)).close();
MatcherAssert.assertThat(
baos.toString(),
Matchers.not(Matchers.containsString(KEEP_ALIVE))
);
}
/**
* BkBasic can return HTTP status 404 when accessing invalid URL.
* @throws IOException if any I/O error occurs.
Expand All @@ -108,4 +173,13 @@ public void exec(final URI home) throws IOException {
}
);
}

/**
* Create a joiner for a header.
* @return Joiner
*/
private Joiner joiner() {
return Joiner.on("\r\n");
}

}

0 comments on commit 5abbe05

Please sign in to comment.