blob: 8e077ef90aea9cd9252488840e7d355b36df8719 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.resource.Resource;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
*
*/
public class HttpOutputTest
{
private Server _server;
private LocalConnector _connector;
private ContentHandler _handler;
@Before
public void init() throws Exception
{
_server = new Server();
HttpConnectionFactory http = new HttpConnectionFactory();
http.getHttpConfiguration().setRequestHeaderSize(1024);
http.getHttpConfiguration().setResponseHeaderSize(1024);
http.getHttpConfiguration().setOutputBufferSize(4096);
_connector = new LocalConnector(_server,http,null);
_server.addConnector(_connector);
_handler=new ContentHandler();
_server.setHandler(_handler);
_server.start();
}
@After
public void destroy() throws Exception
{
_server.stop();
_server.join();
}
@Test
public void testSimple() throws Exception
{
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
}
@Test
public void testByteUnknown() throws Exception
{
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
}
@Test
public void testSendArray() throws Exception
{
byte[] buffer=new byte[16*1024];
Arrays.fill(buffer,0,4*1024,(byte)0x99);
Arrays.fill(buffer,4*1024,12*1024,(byte)0x58);
Arrays.fill(buffer,12*1024,16*1024,(byte)0x66);
_handler._content=ByteBuffer.wrap(buffer);
_handler._content.limit(12*1024);
_handler._content.position(4*1024);
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("\r\nXXXXXXXXXXXXXXXXXXXXXXXXXXX"));
for (int i=0;i<4*1024;i++)
assertEquals("i="+i,(byte)0x99,buffer[i]);
for (int i=12*1024;i<16*1024;i++)
assertEquals("i="+i,(byte)0x66,buffer[i]);
}
@Test
public void testSendInputStreamSimple() throws Exception
{
Resource simple = Resource.newClassPathResource("simple/simple.txt");
_handler._contentInputStream=simple.getInputStream();
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length: 11"));
}
@Test
public void testSendInputStreamBig() throws Exception
{
Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._contentInputStream=big.getInputStream();
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
}
@Test
public void testSendInputStreamBigChunked() throws Exception
{
Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._contentInputStream= new FilterInputStream(big.getInputStream())
{
@Override
public int read(byte[] b, int off, int len) throws IOException
{
int filled= super.read(b,off,len>2000?2000:len);
return filled;
}
};
String response=_connector.getResponses(
"GET / HTTP/1.1\nHost: localhost:80\n\n"+
"GET / HTTP/1.1\nHost: localhost:80\nConnection: close\n\n"
);
response=response.substring(0,response.lastIndexOf("HTTP/1.1 200 OK"));
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Transfer-Encoding: chunked"));
assertThat(response,containsString("400\tThis is a big file"));
assertThat(response,containsString("\r\n0\r\n"));
}
@Test
public void testSendChannelSimple() throws Exception
{
Resource simple = Resource.newClassPathResource("simple/simple.txt");
_handler._contentChannel=simple.getReadableByteChannel();
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length: 11"));
}
@Test
public void testSendChannelBig() throws Exception
{
Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._contentChannel=big.getReadableByteChannel();
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testSendBigDirect() throws Exception
{
Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._content=BufferUtil.toBuffer(big,true);
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length"));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testSendBigInDirect() throws Exception
{
Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._content=BufferUtil.toBuffer(big,false);
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length"));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testSendChannelBigChunked() throws Exception
{
Resource big = Resource.newClassPathResource("simple/big.txt");
final ReadableByteChannel channel = big.getReadableByteChannel();
_handler._contentChannel=new ReadableByteChannel()
{
@Override
public boolean isOpen()
{
return channel.isOpen();
}
@Override
public void close() throws IOException
{
channel.close();
}
@Override
public int read(ByteBuffer dst) throws IOException
{
int filled=0;
if (dst.position()==0 && dst.limit()>2000)
{
int limit=dst.limit();
dst.limit(2000);
filled=channel.read(dst);
dst.limit(limit);
}
else
filled=channel.read(dst);
return filled;
}
};
String response=_connector.getResponses(
"GET / HTTP/1.1\nHost: localhost:80\n\n"+
"GET / HTTP/1.1\nHost: localhost:80\nConnection: close\n\n"
);
response=response.substring(0,response.lastIndexOf("HTTP/1.1 200 OK"));
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Transfer-Encoding: chunked"));
assertThat(response,containsString("\r\n0\r\n"));
}
@Test
public void testWriteByte() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[1];
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testWriteSmall() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[8];
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testWriteMed() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[4000];
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testWriteLarge() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[8192];
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testWriteByteKnown() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=true;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[1];
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length"));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testWriteSmallKnown() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=true;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[8];
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length"));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testWriteMedKnown() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=true;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[4000];
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length"));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testWriteLargeKnown() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=true;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[8192];
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length"));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testWriteHugeKnown() throws Exception
{
_handler._writeLengthIfKnown=true;
_handler._content=BufferUtil.allocate(4*1024*1024);
_handler._content.limit(_handler._content.capacity());
for (int i=_handler._content.capacity();i-->0;)
_handler._content.put(i,(byte)'x');
_handler._arrayBuffer=new byte[8192];
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length"));
}
@Test
public void testWriteBufferSmall() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._byteBuffer=BufferUtil.allocate(8);
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testWriteBufferMed() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._byteBuffer=BufferUtil.allocate(4000);
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testWriteBufferLarge() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._byteBuffer=BufferUtil.allocate(8192);
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testAsyncWriteByte() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[1];
_handler._async=true;
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testAsyncWriteSmall() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[8];
_handler._async=true;
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testAsyncWriteMed() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[4000];
_handler._async=true;
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testAsyncWriteLarge() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[8192];
_handler._async=true;
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testAsyncWriteHuge() throws Exception
{
_handler._writeLengthIfKnown=true;
_handler._content=BufferUtil.allocate(4*1024*1024);
_handler._content.limit(_handler._content.capacity());
for (int i=_handler._content.capacity();i-->0;)
_handler._content.put(i,(byte)'x');
_handler._arrayBuffer=new byte[8192];
_handler._async=true;
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length"));
}
@Test
public void testAsyncWriteBufferSmall() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._byteBuffer=BufferUtil.allocate(8);
_handler._async=true;
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testAsyncWriteBufferMed() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._byteBuffer=BufferUtil.allocate(4000);
_handler._async=true;
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testAsyncWriteBufferLarge() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._byteBuffer=BufferUtil.allocate(8192);
_handler._async=true;
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,containsString("400\tThis is a big file"));
}
@Test
public void testAsyncWriteBufferLargeHEAD() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/big.txt");
_handler._writeLengthIfKnown=false;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._byteBuffer=BufferUtil.allocate(8192);
_handler._async=true;
int start=_handler._owp.get();
String response=_connector.getResponses("HEAD / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(_handler._owp.get()-start,Matchers.greaterThan(0));
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,Matchers.not(containsString("Content-Length")));
assertThat(response,Matchers.not(containsString("400\tThis is a big file")));
}
@Test
public void testAsyncWriteSimpleKnown() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/simple.txt");
_handler._async=true;
_handler._writeLengthIfKnown=true;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[4000];
String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length: 11"));
assertThat(response,containsString("simple text"));
}
@Test
public void testAsyncWriteSimpleKnownHEAD() throws Exception
{
final Resource big = Resource.newClassPathResource("simple/simple.txt");
_handler._async=true;
_handler._writeLengthIfKnown=true;
_handler._content=BufferUtil.toBuffer(big,false);
_handler._arrayBuffer=new byte[4000];
int start=_handler._owp.get();
String response=_connector.getResponses("HEAD / HTTP/1.0\nHost: localhost:80\n\n");
assertThat(_handler._owp.get()-start,Matchers.equalTo(1));
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("Content-Length: 11"));
assertThat(response,Matchers.not(containsString("simple text")));
}
static class ContentHandler extends AbstractHandler
{
AtomicInteger _owp = new AtomicInteger();
boolean _writeLengthIfKnown=true;
boolean _async;
ByteBuffer _byteBuffer;
byte[] _arrayBuffer;
InputStream _contentInputStream;
ReadableByteChannel _contentChannel;
ByteBuffer _content;
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
response.setContentType("text/plain");
final HttpOutput out = (HttpOutput) response.getOutputStream();
if (_contentInputStream!=null)
{
out.sendContent(_contentInputStream);
_contentInputStream=null;
return;
}
if (_contentChannel!=null)
{
out.sendContent(_contentChannel);
_contentChannel=null;
return;
}
if (_content!=null && _writeLengthIfKnown)
response.setContentLength(_content.remaining());
if (_arrayBuffer!=null)
{
if (_async)
{
final AsyncContext async = request.startAsync();
out.setWriteListener(new WriteListener()
{
@Override
public void onWritePossible() throws IOException
{
_owp.incrementAndGet();
while (out.isReady())
{
Assert.assertTrue(out.isReady());
int len=_content.remaining();
if (len>_arrayBuffer.length)
len=_arrayBuffer.length;
if (len==0)
{
async.complete();
break;
}
_content.get(_arrayBuffer,0,len);
if (len==1)
out.write(_arrayBuffer[0]);
else
out.write(_arrayBuffer,0,len);
}
// Assert.assertFalse(out.isReady());
}
@Override
public void onError(Throwable t)
{
t.printStackTrace();
async.complete();
}
});
return;
}
while(BufferUtil.hasContent(_content))
{
int len=_content.remaining();
if (len>_arrayBuffer.length)
len=_arrayBuffer.length;
_content.get(_arrayBuffer,0,len);
if (len==1)
out.write(_arrayBuffer[0]);
else
out.write(_arrayBuffer,0,len);
}
return;
}
if (_byteBuffer!=null)
{
if (_async)
{
final AsyncContext async = request.startAsync();
out.setWriteListener(new WriteListener()
{
@Override
public void onWritePossible() throws IOException
{
_owp.incrementAndGet();
while (out.isReady())
{
Assert.assertTrue(out.isReady());
if(BufferUtil.isEmpty(_content))
{
async.complete();
break;
}
BufferUtil.clearToFill(_byteBuffer);
BufferUtil.put(_content,_byteBuffer);
BufferUtil.flipToFlush(_byteBuffer,0);
out.write(_byteBuffer);
}
}
@Override
public void onError(Throwable t)
{
t.printStackTrace();
async.complete();
}
});
return;
}
while(BufferUtil.hasContent(_content))
{
BufferUtil.clearToFill(_byteBuffer);
BufferUtil.put(_content,_byteBuffer);
BufferUtil.flipToFlush(_byteBuffer,0);
out.write(_byteBuffer);
}
return;
}
if (_content!=null)
{
if (_content.hasArray())
out.write(_content.array(),_content.arrayOffset()+_content.position(),_content.remaining());
else
out.sendContent(_content);
_content=null;
return;
}
}
}
}