/*
* Copyright (c) 2012 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 org.eclipse.jetty.spdy.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationSupport;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.spdy.api.BytesDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.ReplyInfo;
import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.StringDataInfo;
import org.eclipse.jetty.spdy.api.SynInfo;
import org.junit.Assert;
import org.junit.Test;
public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
{
@Test
public void testSimpleGET() throws Exception
{
final String path = "/foo";
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
Assert.assertEquals("GET", httpRequest.getMethod());
Assert.assertEquals(path, target);
Assert.assertEquals(path, httpRequest.getRequestURI());
Assert.assertEquals("localhost:" + connector.getLocalPort(), httpRequest.getHeader("host"));
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", path);
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertTrue(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithQueryString() throws Exception
{
final String path = "/foo";
final String query = "p=1";
final String uri = path + "?" + query;
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
Assert.assertEquals("GET", httpRequest.getMethod());
Assert.assertEquals(path, target);
Assert.assertEquals(path, httpRequest.getRequestURI());
Assert.assertEquals(query, httpRequest.getQueryString());
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", uri);
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertTrue(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testHEAD() throws Exception
{
final String path = "/foo";
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
Assert.assertEquals("HEAD", httpRequest.getMethod());
Assert.assertEquals(path, target);
Assert.assertEquals(path, httpRequest.getRequestURI());
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "HEAD");
headers.put("url", path);
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertTrue(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testPOSTWithParameters() throws Exception
{
final String path = "/foo";
final String data = "a=1&b=2";
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
Assert.assertEquals("POST", httpRequest.getMethod());
Assert.assertEquals("1", httpRequest.getParameter("a"));
Assert.assertEquals("2", httpRequest.getParameter("b"));
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "POST");
headers.put("url", path);
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
headers.put("content-type", "application/x-www-form-urlencoded");
final CountDownLatch replyLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
{
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertTrue(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
}).get(5, TimeUnit.SECONDS);
stream.data(new StringDataInfo(data, true));
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testPOSTWithParametersInTwoFramesTwoReads() throws Exception
{
final String path = "/foo";
final String data1 = "a=1&";
final String data2 = "b=2";
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
Assert.assertEquals("POST", httpRequest.getMethod());
Assert.assertEquals("1", httpRequest.getParameter("a"));
Assert.assertEquals("2", httpRequest.getParameter("b"));
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "POST");
headers.put("url", path);
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
headers.put("content-type", "application/x-www-form-urlencoded");
final CountDownLatch replyLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
{
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertTrue(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
}).get(5, TimeUnit.SECONDS);
// Sleep between the data frames so that they will be read in 2 reads
stream.data(new StringDataInfo(data1, false));
Thread.sleep(1000);
stream.data(new StringDataInfo(data2, true));
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testPOSTWithParametersInTwoFramesOneRead() throws Exception
{
final String path = "/foo";
final String data1 = "a=1&";
final String data2 = "b=2";
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
Assert.assertEquals("POST", httpRequest.getMethod());
Assert.assertEquals("1", httpRequest.getParameter("a"));
Assert.assertEquals("2", httpRequest.getParameter("b"));
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "POST");
headers.put("url", path);
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
headers.put("content-type", "application/x-www-form-urlencoded");
final CountDownLatch replyLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
{
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertTrue(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.toString(), replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
}).get(5, TimeUnit.SECONDS);
// Send the data frames consecutively, so the server reads both frames in one read
stream.data(new StringDataInfo(data1, false));
stream.data(new StringDataInfo(data2, true));
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithSmallResponseContent() throws Exception
{
final String data = "0123456789ABCDEF";
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
httpResponse.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream output = httpResponse.getOutputStream();
output.write(data.getBytes("UTF-8"));
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertFalse(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
Assert.assertTrue(dataInfo.isClose());
Assert.assertEquals(data, dataInfo.asString("UTF-8", true));
dataLatch.countDown();
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithOneByteResponseContent() throws Exception
{
final char data = 'x';
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
httpResponse.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream output = httpResponse.getOutputStream();
output.write(data);
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertFalse(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
Assert.assertTrue(dataInfo.isClose());
byte[] bytes = dataInfo.asBytes(true);
Assert.assertEquals(1, bytes.length);
Assert.assertEquals(data, bytes[0]);
dataLatch.countDown();
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithSmallResponseContentInTwoChunks() throws Exception
{
final String data1 = "0123456789ABCDEF";
final String data2 = "FEDCBA9876543210";
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
httpResponse.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream output = httpResponse.getOutputStream();
output.write(data1.getBytes("UTF-8"));
output.flush();
output.write(data2.getBytes("UTF-8"));
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(2);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
private final AtomicInteger replyFrames = new AtomicInteger();
private final AtomicInteger dataFrames = new AtomicInteger();
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertEquals(1, replyFrames.incrementAndGet());
Assert.assertFalse(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
int data = dataFrames.incrementAndGet();
Assert.assertTrue(data >= 1 && data <= 2);
if (data == 1)
Assert.assertEquals(data1, dataInfo.asString("UTF8", true));
else
Assert.assertEquals(data2, dataInfo.asString("UTF8", true));
dataLatch.countDown();
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithBigResponseContentInOneWrite() throws Exception
{
final byte[] data = new byte[128 * 1024];
Arrays.fill(data, (byte)'x');
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
httpResponse.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream output = httpResponse.getOutputStream();
output.write(data);
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
private final AtomicInteger contentBytes = new AtomicInteger();
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertFalse(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
contentBytes.addAndGet(dataInfo.asByteBuffer(true).remaining());
if (dataInfo.isClose())
{
Assert.assertEquals(data.length, contentBytes.get());
dataLatch.countDown();
}
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithBigResponseContentInTwoWrites() throws Exception
{
final byte[] data = new byte[128 * 1024];
Arrays.fill(data, (byte)'y');
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
httpResponse.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream output = httpResponse.getOutputStream();
output.write(data);
output.write(data);
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
private final AtomicInteger contentBytes = new AtomicInteger();
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertFalse(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
contentBytes.addAndGet(dataInfo.asByteBuffer(true).remaining());
if (dataInfo.isClose())
{
Assert.assertEquals(2 * data.length, contentBytes.get());
dataLatch.countDown();
}
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithOutputStreamFlushedAndClosed() throws Exception
{
final String data = "0123456789ABCDEF";
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
httpResponse.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream output = httpResponse.getOutputStream();
output.write(data.getBytes("UTF-8"));
output.flush();
output.close();
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertFalse(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
ByteBuffer byteBuffer = dataInfo.asByteBuffer(true);
while (byteBuffer.hasRemaining())
buffer.write(byteBuffer.get());
if (dataInfo.isClose())
{
Assert.assertEquals(data, new String(buffer.toByteArray(), Charset.forName("UTF-8")));
dataLatch.countDown();
}
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithResponseResetBuffer() throws Exception
{
final String data1 = "0123456789ABCDEF";
final String data2 = "FEDCBA9876543210";
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
httpResponse.setStatus(HttpServletResponse.SC_OK);
ServletOutputStream output = httpResponse.getOutputStream();
// Write some
output.write(data1.getBytes("UTF-8"));
// But then change your mind and reset the buffer
httpResponse.resetBuffer();
output.write(data2.getBytes("UTF-8"));
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertFalse(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
ByteBuffer byteBuffer = dataInfo.asByteBuffer(true);
while (byteBuffer.hasRemaining())
buffer.write(byteBuffer.get());
if (dataInfo.isClose())
{
Assert.assertEquals(data2, new String(buffer.toByteArray(), Charset.forName("UTF-8")));
dataLatch.countDown();
}
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithRedirect() throws Exception
{
final String suffix = "/redirect";
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
String location = httpResponse.encodeRedirectURL(String.format("%s://%s:%d%s",
request.getScheme(), request.getLocalAddr(), request.getLocalPort(), target + suffix));
httpResponse.sendRedirect(location);
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
private final AtomicInteger replies = new AtomicInteger();
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertEquals(1, replies.incrementAndGet());
Assert.assertTrue(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("302"));
Assert.assertTrue(replyHeaders.get("location").value().endsWith(suffix));
replyLatch.countDown();
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithSendError() throws Exception
{
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
private final AtomicInteger replies = new AtomicInteger();
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertEquals(1, replies.incrementAndGet());
Assert.assertFalse(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("404"));
replyLatch.countDown();
}
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
Assert.assertTrue(dataInfo.isClose());
dataLatch.countDown();
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithException() throws Exception
{
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
throw new NullPointerException("thrown_explicitly_by_the_test");
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
private final AtomicInteger replies = new AtomicInteger();
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertEquals(1, replies.incrementAndGet());
Assert.assertTrue(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("500"));
replyLatch.countDown();
}
});
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithSmallResponseChunked() throws Exception
{
final String pangram1 = "the quick brown fox jumps over the lazy dog";
final String pangram2 = "qualche vago ione tipo zolfo, bromo, sodio";
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
httpResponse.setHeader("Transfer-Encoding", "chunked");
ServletOutputStream output = httpResponse.getOutputStream();
output.write(pangram1.getBytes("UTF-8"));
httpResponse.setHeader("EXTRA", "X");
output.flush();
output.write(pangram2.getBytes("UTF-8"));
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(2);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
private final AtomicInteger replyFrames = new AtomicInteger();
private final AtomicInteger dataFrames = new AtomicInteger();
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertEquals(1, replyFrames.incrementAndGet());
Assert.assertFalse(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
Assert.assertTrue(replyHeaders.get("extra").value().contains("X"));
replyLatch.countDown();
}
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
int count = dataFrames.incrementAndGet();
if (count == 1)
{
Assert.assertFalse(dataInfo.isClose());
Assert.assertEquals(pangram1, dataInfo.asString("UTF-8", true));
}
else if (count == 2)
{
Assert.assertTrue(dataInfo.isClose());
Assert.assertEquals(pangram2, dataInfo.asString("UTF-8", true));
}
dataLatch.countDown();
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testGETWithMediumContentByPassed() throws Exception
{
final byte[] data = new byte[2048];
final CountDownLatch handlerLatch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
// We use this trick that's present in Jetty code: if we add a request attribute
// called "org.eclipse.jetty.server.sendContent", then it will trigger the
// content bypass that we want to test
request.setAttribute("org.eclipse.jetty.server.sendContent", new ByteArrayBuffer(data));
handlerLatch.countDown();
}
}), null);
Headers headers = new Headers();
headers.put("method", "GET");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
private final AtomicInteger replyFrames = new AtomicInteger();
private final AtomicInteger dataFrames = new AtomicInteger();
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertEquals(1, replyFrames.incrementAndGet());
Assert.assertFalse(replyInfo.isClose());
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
Assert.assertEquals(1, dataFrames.incrementAndGet());
Assert.assertTrue(dataInfo.isClose());
Assert.assertArrayEquals(data, dataInfo.asBytes(true));
dataLatch.countDown();
}
});
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testPOSTThenSuspendRequestThenReadOneChunkThenComplete() throws Exception
{
final byte[] data = new byte[2000];
final CountDownLatch latch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
final Continuation continuation = ContinuationSupport.getContinuation(request);
continuation.suspend();
new Thread()
{
@Override
public void run()
{
try
{
InputStream input = request.getInputStream();
byte[] buffer = new byte[512];
int read = 0;
while (read < data.length)
read += input.read(buffer);
continuation.complete();
latch.countDown();
}
catch (IOException x)
{
x.printStackTrace();
}
}
}.start();
}
}), null);
Headers headers = new Headers();
headers.put("method", "POST");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
{
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
}).get(5, TimeUnit.SECONDS);
stream.data(new BytesDataInfo(data, true));
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testPOSTThenSuspendRequestThenReadTwoChunksThenComplete() throws Exception
{
final byte[] data = new byte[2000];
final CountDownLatch latch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
final Continuation continuation = ContinuationSupport.getContinuation(request);
continuation.suspend();
new Thread()
{
@Override
public void run()
{
try
{
InputStream input = request.getInputStream();
byte[] buffer = new byte[512];
int read = 0;
while (read < 2 * data.length)
read += input.read(buffer);
continuation.complete();
latch.countDown();
}
catch (IOException x)
{
x.printStackTrace();
}
}
}.start();
}
}), null);
Headers headers = new Headers();
headers.put("method", "POST");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
{
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
replyLatch.countDown();
}
}).get(5, TimeUnit.SECONDS);
stream.data(new BytesDataInfo(data, false));
stream.data(new BytesDataInfo(data, true));
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
}
@Test
public void testPOSTThenSuspendRequestThenResumeThenRespond() throws Exception
{
final byte[] data = new byte[1000];
final CountDownLatch latch = new CountDownLatch(1);
Session session = startClient(startHTTPServer(new AbstractHandler()
{
@Override
public void handle(String target, final Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException
{
request.setHandled(true);
final Continuation continuation = ContinuationSupport.getContinuation(request);
if (continuation.isInitial())
{
InputStream input = request.getInputStream();
byte[] buffer = new byte[256];
int read = 0;
while (read < data.length)
read += input.read(buffer);
continuation.suspend();
new Thread()
{
@Override
public void run()
{
try
{
TimeUnit.SECONDS.sleep(1);
continuation.resume();
latch.countDown();
}
catch (InterruptedException x)
{
x.printStackTrace();
}
}
}.start();
}
else
{
OutputStream output = httpResponse.getOutputStream();
output.write(data);
}
}
}), null);
Headers headers = new Headers();
headers.put("method", "POST");
headers.put("url", "/foo");
headers.put("version", "HTTP/1.1");
headers.put("host", "localhost:" + connector.getLocalPort());
final CountDownLatch responseLatch = new CountDownLatch(2);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
{
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Headers replyHeaders = replyInfo.getHeaders();
Assert.assertTrue(replyHeaders.get("status").value().contains("200"));
responseLatch.countDown();
}
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
if (dataInfo.isClose())
responseLatch.countDown();
}
}).get(5, TimeUnit.SECONDS);
stream.data(new BytesDataInfo(data, true));
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
}
}