More tests for the generation of responses.
This commit is contained in:
parent
4c691c628b
commit
df47105da1
|
@ -287,6 +287,26 @@ public class HTTPSPDYAsyncConnection extends AbstractHttpConnection implements A
|
||||||
return buffer.length();
|
return buffer.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void commitResponse(boolean last) throws IOException
|
||||||
|
{
|
||||||
|
// Keep the original behavior since it just delegates to the generator
|
||||||
|
super.commitResponse(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flushResponse() throws IOException
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void completeResponse() throws IOException
|
||||||
|
{
|
||||||
|
// Keep the original behavior since it just delegates to the generator
|
||||||
|
super.completeResponse();
|
||||||
|
}
|
||||||
|
|
||||||
private String parseHost(String url)
|
private String parseHost(String url)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -365,10 +385,19 @@ public class HTTPSPDYAsyncConnection extends AbstractHttpConnection implements A
|
||||||
@Override
|
@Override
|
||||||
public void send1xx(int code) throws IOException
|
public void send1xx(int code) throws IOException
|
||||||
{
|
{
|
||||||
Headers headers = new Headers();
|
throw new UnsupportedOperationException();
|
||||||
headers.put("status", String.valueOf(code));
|
}
|
||||||
headers.put("version", "HTTP/1.1");
|
|
||||||
stream.reply(new ReplyInfo(headers, false));
|
@Override
|
||||||
|
public void sendResponse(Buffer response) throws IOException
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendError(int code, String reason, String content, boolean close) throws IOException
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -388,20 +417,58 @@ public class HTTPSPDYAsyncConnection extends AbstractHttpConnection implements A
|
||||||
headers.put(field.getName(), field.getValue());
|
headers.put(field.getName(), field.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stream.reply(new ReplyInfo(headers, allContentAdded));
|
|
||||||
|
// We have to query the HttpGenerator and its _buffer to know
|
||||||
|
// whether there is content buffered; if so, send the data frame
|
||||||
|
boolean close = _buffer == null || _buffer.length() == 0;
|
||||||
|
stream.reply(new ReplyInfo(headers, close));
|
||||||
|
if (!close)
|
||||||
|
{
|
||||||
|
ByteBuffer buffer = ((NIOBuffer)_buffer).getByteBuffer();
|
||||||
|
buffer.limit(_buffer.putIndex());
|
||||||
|
buffer.position(_buffer.getIndex());
|
||||||
|
// Update HttpGenerator fields so that they remain consistent
|
||||||
|
_buffer.clear();
|
||||||
|
_state = HttpGenerator.STATE_CONTENT;
|
||||||
|
// Send the data frame
|
||||||
|
stream.data(new ByteBufferDataInfo(buffer, allContentAdded));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addContent(byte b) throws IOException
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addContent(Buffer content, boolean last) throws IOException
|
public void addContent(Buffer content, boolean last) throws IOException
|
||||||
{
|
{
|
||||||
ByteBuffer buffer = ByteBuffer.wrap(content.asArray());
|
|
||||||
stream.data(new ByteBufferDataInfo(buffer, last));
|
// TODO: we need to avoid that the HttpParser chunks the content
|
||||||
|
// otherwise we're sending bad data... so perhaps we need to do our own buffering here
|
||||||
|
|
||||||
|
// Keep the original behavior since adding content will
|
||||||
|
// just accumulate bytes until the response is committed.
|
||||||
|
super.addContent(content, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int flushBuffer() throws IOException
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void blockForOutput(long maxIdleTime) throws IOException
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void complete() throws IOException
|
public void complete() throws IOException
|
||||||
{
|
{
|
||||||
// Nothing to do
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,13 @@ package org.eclipse.jetty.spdy.http;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletOutputStream;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
@ -31,6 +35,7 @@ import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
import org.eclipse.jetty.spdy.SPDYClient;
|
import org.eclipse.jetty.spdy.SPDYClient;
|
||||||
import org.eclipse.jetty.spdy.SPDYServerConnector;
|
import org.eclipse.jetty.spdy.SPDYServerConnector;
|
||||||
|
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||||
import org.eclipse.jetty.spdy.api.Headers;
|
import org.eclipse.jetty.spdy.api.Headers;
|
||||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||||
import org.eclipse.jetty.spdy.api.SPDY;
|
import org.eclipse.jetty.spdy.api.SPDY;
|
||||||
|
@ -418,4 +423,225 @@ public class HTTPSPDYTest
|
||||||
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
|
Assert.assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
|
||||||
Assert.assertTrue(replyLatch.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", "http://localhost:" + connector.getLocalPort() + "/foo");
|
||||||
|
headers.put("version", "HTTP/1.1");
|
||||||
|
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||||
|
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||||
|
session.syn(SPDY.V2, new SynInfo(headers, true), new Stream.FrameListener.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());
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(dataInfo.getBytesCount());
|
||||||
|
dataInfo.getBytes(buffer);
|
||||||
|
buffer.flip();
|
||||||
|
Assert.assertEquals(data, Charset.forName("UTF-8").decode(buffer).toString());
|
||||||
|
dataLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(handlerLatch.await(500, TimeUnit.SECONDS));
|
||||||
|
Assert.assertTrue(replyLatch.await(500, 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", "http://localhost:" + connector.getLocalPort() + "/foo");
|
||||||
|
headers.put("version", "HTTP/1.1");
|
||||||
|
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||||
|
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||||
|
session.syn(SPDY.V2, new SynInfo(headers, true), new Stream.FrameListener.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());
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(dataInfo.getBytesCount());
|
||||||
|
dataInfo.getBytes(buffer);
|
||||||
|
buffer.flip();
|
||||||
|
Assert.assertEquals(data, Charset.forName("UTF-8").decode(buffer).toString());
|
||||||
|
dataLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(handlerLatch.await(500, TimeUnit.SECONDS));
|
||||||
|
Assert.assertTrue(replyLatch.await(500, TimeUnit.SECONDS));
|
||||||
|
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGETWithBigResponseContentInTwoWrites() throws Exception
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
Assert.fail();
|
||||||
|
|
||||||
|
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", "http://localhost:" + connector.getLocalPort() + "/foo");
|
||||||
|
headers.put("version", "HTTP/1.1");
|
||||||
|
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||||
|
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||||
|
session.syn(SPDY.V2, new SynInfo(headers, true), new Stream.FrameListener.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());
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(dataInfo.getBytesCount());
|
||||||
|
dataInfo.getBytes(buffer);
|
||||||
|
buffer.flip();
|
||||||
|
Assert.assertEquals(data, Charset.forName("UTF-8").decode(buffer).toString());
|
||||||
|
dataLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(handlerLatch.await(500, TimeUnit.SECONDS));
|
||||||
|
Assert.assertTrue(replyLatch.await(500, 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", "http://localhost:" + connector.getLocalPort() + "/foo");
|
||||||
|
headers.put("version", "HTTP/1.1");
|
||||||
|
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||||
|
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||||
|
session.syn(SPDY.V2, new SynInfo(headers, true), new Stream.FrameListener.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());
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(dataInfo.getBytesCount());
|
||||||
|
dataInfo.getBytes(buffer);
|
||||||
|
buffer.flip();
|
||||||
|
Assert.assertEquals(data1, Charset.forName("UTF-8").decode(buffer).toString());
|
||||||
|
dataLatch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(handlerLatch.await(500, TimeUnit.SECONDS));
|
||||||
|
Assert.assertTrue(replyLatch.await(500, TimeUnit.SECONDS));
|
||||||
|
Assert.assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add tests for chunked content
|
||||||
|
|
||||||
|
// Note that I do not care much about the state of the generator, as long as I can avoid
|
||||||
|
// that the generator writes, that SPDY writes chunked bytes, and - if possible - data copying
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue