// ========================================================================
// Copyright (c) 2004-2009 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.
// ========================================================================
/*
* Created on 9/01/2004
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package org.eclipse.jetty.server;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpHeaders;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StdErrLog;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
*
*/
public class HttpConnectionTest
{
private static final Logger LOG = Log.getLogger(HttpConnectionTest.class);
private Server server;
private LocalConnector connector;
@Before
public void init() throws Exception
{
server = new Server();
connector = new LocalConnector();
server.addConnector(connector);
connector.setRequestHeaderSize(1024);
connector.setResponseHeaderSize(1024);
server.setHandler(new DumpHandler());
server.start();
}
@After
public void destroy() throws Exception
{
server.stop();
server.join();
}
@Test
public void testFragmentedChunk()
{
String response=null;
try
{
int offset=0;
// Chunk last
offset=0;
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain\n"+
"\015\012"+
"5;\015\012"+
"12345\015\012"+
"0;\015\012\015\012");
offset = checkContains(response,offset,"HTTP/1.1 200");
offset = checkContains(response,offset,"/R1");
offset = checkContains(response,offset,"12345");
offset = 0;
response=connector.getResponses("GET /R2 HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain\n"+
"\015\012"+
"5;\015\012"+
"ABCDE\015\012"+
"0;\015\012\015\012");
offset = checkContains(response,offset,"HTTP/1.1 200");
offset = checkContains(response,offset,"/R2");
offset = checkContains(response,offset,"ABCDE");
}
catch(Exception e)
{
e.printStackTrace();
assertTrue(false);
if (response!=null)
System.err.println(response);
}
}
@Test
public void testEmpty() throws Exception
{
String response=connector.getResponses("GET /R1 HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain\n"+
"\015\012"+
"0\015\012\015\012");
int offset=0;
offset = checkContains(response,offset,"HTTP/1.1 200");
checkContains(response,offset,"/R1");
}
@Test
public void testHead() throws Exception
{
String responsePOST=connector.getResponses("POST /R1 HTTP/1.1\015\012"+
"Host: localhost\015\012"+
"\015\012");
String responseHEAD=connector.getResponses("HEAD /R1 HTTP/1.1\015\012"+
"Host: localhost\015\012"+
"\015\012");
assertTrue(responsePOST.startsWith(responseHEAD.substring(0,responseHEAD.length()-2)));
assertTrue(responsePOST.length()>responseHEAD.length());
responsePOST=connector.getResponses("POST /R1 HTTP/1.1\015\012"+
"Host: localhost\015\012"+
"\015\012");
assertTrue(responsePOST.startsWith(responseHEAD.substring(0,responseHEAD.length()-2)));
assertTrue(responsePOST.length()>responseHEAD.length());
}
@Test
public void testBad() throws Exception
{
try
{
((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(true);
String response;
response=connector.getResponses("GET % HTTP/1.1\n"+
"Host: localhost\n"+
"\015\012");
checkContains(response,0,"HTTP/1.1 400");
response=connector.getResponses("GET http://localhost:WRONG/ HTTP/1.1\n"+
"Host: localhost\n"+
"\015\012");
checkContains(response,0,"HTTP/1.1 400");
response=connector.getResponses("GET /bad/encoding%1 HTTP/1.1\n"+
"Host: localhost\n"+
"\015\012");
checkContains(response,0,"HTTP/1.1 400");
response=connector.getResponses("GET /foo/bar%c0%00 HTTP/1.1\n"+
"Host: localhost\n"+
"\015\012");
checkContains(response,0,"HTTP/1.1 400");
response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\n"+
"Host: localhost\n"+
"\015\012");
checkContains(response,0,"HTTP/1.1 400");
}
finally
{
((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(false);
}
}
@Test
public void testAutoFlush() throws Exception
{
String response=null;
int offset=0;
offset=0;
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain\n"+
"\015\012"+
"5;\015\012"+
"12345\015\012"+
"0;\015\012\015\012");
offset = checkContains(response,offset,"HTTP/1.1 200");
checkNotContained(response,offset,"IgnoreMe");
offset = checkContains(response,offset,"/R1");
offset = checkContains(response,offset,"12345");
}
@Test
public void testCharset()
{
String response=null;
try
{
int offset=0;
offset=0;
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain; charset=utf-8\n"+
"\015\012"+
"5;\015\012"+
"12345\015\012"+
"0;\015\012\015\012");
offset = checkContains(response,offset,"HTTP/1.1 200");
offset = checkContains(response,offset,"/R1");
offset = checkContains(response,offset,"encoding=UTF-8");
offset = checkContains(response,offset,"12345");
offset=0;
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain; charset = iso-8859-1 ; other=value\n"+
"\015\012"+
"5;\015\012"+
"12345\015\012"+
"0;\015\012\015\012");
offset = checkContains(response,offset,"HTTP/1.1 200");
offset = checkContains(response,offset,"encoding=iso-8859-1");
offset = checkContains(response,offset,"/R1");
offset = checkContains(response,offset,"12345");
offset=0;
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain; charset=unknown\n"+
"\015\012"+
"5;\015\012"+
"12345\015\012"+
"0;\015\012\015\012");
offset = checkContains(response,offset,"HTTP/1.1 200");
offset = checkContains(response,offset,"encoding=unknown");
offset = checkContains(response,offset,"/R1");
offset = checkContains(response,offset,"UnsupportedEncodingException");
}
catch(Exception e)
{
e.printStackTrace();
assertTrue(false);
if (response!=null)
System.err.println(response);
}
}
@Test
public void testUnconsumedError() throws Exception
{
String response=null;
String requests=null;
int offset=0;
offset=0;
requests="GET /R1?read=1&error=500 HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain; charset=utf-8\n"+
"\015\012"+
"5;\015\012"+
"12345\015\012"+
"5;\015\012"+
"67890\015\012"+
"0;\015\012\015\012"+
"GET /R2 HTTP/1.1\n"+
"Host: localhost\n"+
"Content-Type: text/plain; charset=utf-8\n"+
"Content-Length: 10\n"+
"\n"+
"abcdefghij\n";
response=connector.getResponses(requests);
offset = checkContains(response,offset,"HTTP/1.1 500");
offset = checkContains(response,offset,"HTTP/1.1 200");
offset = checkContains(response,offset,"/R2");
offset = checkContains(response,offset,"encoding=UTF-8");
offset = checkContains(response,offset,"abcdefghij");
}
@Test
public void testUnconsumedException() throws Exception
{
String response=null;
String requests=null;
int offset=0;
offset=0;
requests="GET /R1?read=1&ISE=true HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain; charset=utf-8\n"+
"\015\012"+
"5;\015\012"+
"12345\015\012"+
"5;\015\012"+
"67890\015\012"+
"0;\015\012\015\012"+
"GET /R2 HTTP/1.1\n"+
"Host: localhost\n"+
"Content-Type: text/plain; charset=utf-8\n"+
"Content-Length: 10\n"+
"\n"+
"abcdefghij\n";
Logger logger=null;
try
{
((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(true);
response=connector.getResponses(requests);
offset = checkContains(response,offset,"HTTP/1.1 500");
offset = checkContains(response,offset,"Connection: close");
checkNotContained(response,offset,"HTTP/1.1 200");
}
finally
{
((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(false);
}
}
@Test
public void testConnection()
{
String response=null;
try
{
int offset=0;
offset=0;
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
"Host: localhost\n"+
"Connection: TE, close\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain; charset=utf-8\n"+
"\015\012"+
"5;\015\012"+
"12345\015\012"+
"0;\015\012\015\012");
checkContains(response,offset,"Connection: close");
}
catch (Exception e)
{
e.printStackTrace();
assertTrue(false);
if (response!=null)
System.err.println(response);
}
}
/**
* Creates a request header over 1k in size, by creating a single header entry with an huge value.
*/
@Test
public void testOversizedBuffer() throws Exception
{
String response = null;
try
{
int offset = 0;
String cookie = "thisisastringthatshouldreachover1kbytes";
for (int i=0;i<100;i++)
cookie+="xxxxxxxxxxxx";
response = connector.getResponses("GET / HTTP/1.1\n"+
"Host: localhost\n" +
"Cookie: "+cookie+"\n"+
"\015\012"
);
checkContains(response, offset, "HTTP/1.1 413");
}
catch(Exception e)
{
if(response != null)
System.err.println(response);
throw e;
}
}
/**
* Creates a request header with over 1000 entries.
*/
@Test
public void testExcessiveHeader() throws Exception
{
String response = null;
int offset = 0;
StringBuilder request = new StringBuilder();
request.append("GET / HTTP/1.1\n");
request.append("Host: localhost\n");
request.append("Cookie: thisisastring\n");
for(int i=0; i<1000; i++) {
request.append(String.format("X-Header-%04d: %08x\n", i, i));
}
request.append("\015\012");
response = connector.getResponses(request.toString());
checkContains(response, offset, "HTTP/1.1 413");
}
@Test
public void testOversizedResponse() throws Exception
{
String str = "thisisastringthatshouldreachover1kbytes-";
for (int i=0;i<500;i++)
str+="xxxxxxxxxxxx";
final String longstr = str;
String response = null;
server.stop();
server.setHandler(new DumpHandler()
{
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
try
{
baseRequest.setHandled(true);
response.setHeader(HttpHeaders.CONTENT_TYPE,MimeTypes.TEXT_HTML);
response.setHeader("LongStr", longstr);
PrintWriter writer = response.getWriter();
writer.write("<html><h1>FOO</h1></html>");
writer.flush();
if (!writer.checkError())
throw new RuntimeException("SHOULD NOT GET HERE");
}
catch(Exception e)
{
LOG.debug(e);
LOG.info("correctly ignored "+e);
}
}
});
server.start();
try
{
int offset = 0;
response = connector.getResponses("GET / HTTP/1.1\n"+
"Host: localhost\n" +
"\015\012"
);
checkContains(response, offset, "HTTP/1.1 500");
}
catch(Exception e)
{
e.printStackTrace();
if(response != null)
System.err.println(response);
fail("Exception");
}
}
@Test
public void testAsterisk()
{
String response = null;
try
{
int offset=0;
response=connector.getResponses("OPTIONS * HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain; charset=utf-8\n"+
"\015\012"+
"5;\015\012"+
"12345\015\012"+
"0;\015\012\015\012");
offset = checkContains(response,offset,"HTTP/1.1 200");
offset = checkContains(response,offset,"*");
// to prevent the DumpHandler from picking this up and returning 200 OK
server.stop();
server.setHandler(null);
server.start();
offset=0;
response=connector.getResponses("GET * HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain; charset=utf-8\n"+
"\015\012"+
"5;\015\012"+
"12345\015\012"+
"0;\015\012\015\012");
offset = checkContains(response,offset,"HTTP/1.1 404 Not Found");
offset=0;
response=connector.getResponses("GET ** HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain; charset=utf-8\n"+
"\015\012"+
"5;\015\012"+
"12345\015\012"+
"0;\015\012\015\012");
offset = checkContains(response,offset,"HTTP/1.1 400 Bad Request");
}
catch (Exception e)
{
e.printStackTrace();
assertTrue(false);
if (response!=null)
System.err.println(response);
}
}
@Test
public void testCONNECT()
{
String response = null;
try
{
int offset=0;
response=connector.getResponses("CONNECT www.webtide.com:8080 HTTP/1.1\n"+
"Host: myproxy:8888\015\012"+
"\015\012");
checkContains(response,offset,"HTTP/1.1 200");
}
catch (Exception e)
{
e.printStackTrace();
assertTrue(false);
if (response!=null)
System.err.println(response);
}
}
private int checkContains(String s,int offset,String c)
{
int o=s.indexOf(c,offset);
if (o<offset)
{
System.err.println("FAILED");
System.err.println("'"+c+"' not in:");
System.err.println(s.substring(offset));
System.err.flush();
System.out.println("--\n"+s);
System.out.flush();
assertTrue(false);
}
return o;
}
private void checkNotContained(String s,int offset,String c)
{
int o=s.indexOf(c,offset);
if (o>=offset)
{
System.err.println("FAILED");
System.err.println("'"+c+"' IS in:");
System.err.println(s.substring(offset));
System.err.flush();
System.out.println("--\n"+s);
System.out.flush();
assertTrue(false);
}
}
}