| // |
| // ======================================================================== |
| // 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.hamcrest.Matchers.is; |
| import static org.hamcrest.Matchers.startsWith; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNotSame; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertSame; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.io.BufferedReader; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.io.UnsupportedEncodingException; |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Enumeration; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicInteger; |
| |
| import javax.servlet.DispatcherType; |
| import javax.servlet.MultipartConfigElement; |
| import javax.servlet.ServletException; |
| import javax.servlet.ServletInputStream; |
| import javax.servlet.ServletRequestEvent; |
| import javax.servlet.http.Cookie; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| import javax.servlet.http.Part; |
| |
| import org.eclipse.jetty.http.BadMessageException; |
| import org.eclipse.jetty.http.HttpTester; |
| import org.eclipse.jetty.http.MimeTypes; |
| import org.eclipse.jetty.server.handler.AbstractHandler; |
| import org.eclipse.jetty.server.handler.ContextHandler; |
| import org.eclipse.jetty.server.handler.ErrorHandler; |
| import org.eclipse.jetty.toolchain.test.FS; |
| import org.eclipse.jetty.toolchain.test.MavenTestingUtils; |
| import org.eclipse.jetty.util.IO; |
| import org.eclipse.jetty.util.MultiPartInputStreamParser; |
| import org.eclipse.jetty.util.log.Log; |
| import org.eclipse.jetty.util.log.Logger; |
| import org.eclipse.jetty.util.log.StacklessLogging; |
| import org.hamcrest.Matchers; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| |
| public class RequestTest |
| { |
| private static final Logger LOG = Log.getLogger(RequestTest.class); |
| private Server _server; |
| private LocalConnector _connector; |
| private RequestHandler _handler; |
| |
| @Before |
| public void init() throws Exception |
| { |
| _server = new Server(); |
| HttpConnectionFactory http = new HttpConnectionFactory(); |
| http.setInputBufferSize(1024); |
| http.getHttpConfiguration().setRequestHeaderSize(512); |
| http.getHttpConfiguration().setResponseHeaderSize(512); |
| http.getHttpConfiguration().setOutputBufferSize(2048); |
| http.getHttpConfiguration().addCustomizer(new ForwardedRequestCustomizer()); |
| _connector = new LocalConnector(_server,http); |
| _server.addConnector(_connector); |
| _handler = new RequestHandler(); |
| _server.setHandler(_handler); |
| |
| ErrorHandler errors = new ErrorHandler(); |
| errors.setShowStacks(true); |
| _server.addBean(errors); |
| _server.start(); |
| } |
| |
| @After |
| public void destroy() throws Exception |
| { |
| _server.stop(); |
| _server.join(); |
| } |
| |
| @Test |
| public void testParamExtraction() throws Exception |
| { |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) |
| { |
| try |
| { |
| Map<String, String[]> map = null; |
| // do the parse |
| map = request.getParameterMap(); |
| return false; |
| } |
| catch(BadMessageException e) |
| { |
| // Should be able to retrieve the raw query |
| String rawQuery = request.getQueryString(); |
| return rawQuery.equals("param=aaa%ZZbbb&other=value"); |
| } |
| } |
| }; |
| |
| //Send a request with query string with illegal hex code to cause |
| //an exception parsing the params |
| String request="GET /?param=aaa%ZZbbb&other=value HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: text/html;charset=utf8\n"+ |
| "Connection: close\n"+ |
| "\n"; |
| |
| String responses=_connector.getResponses(request); |
| assertTrue(responses.startsWith("HTTP/1.1 200")); |
| } |
| |
| @Test |
| public void testEmptyHeaders() throws Exception |
| { |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) |
| { |
| assertNotNull(request.getLocale()); |
| assertTrue(request.getLocales().hasMoreElements()); // Default locale |
| assertEquals("",request.getContentType()); |
| assertNull(request.getCharacterEncoding()); |
| assertEquals(0,request.getQueryString().length()); |
| assertEquals(-1,request.getContentLength()); |
| assertNull(request.getCookies()); |
| assertEquals("",request.getHeader("Name")); |
| assertTrue(request.getHeaders("Name").hasMoreElements()); // empty |
| try |
| { |
| request.getDateHeader("Name"); |
| assertTrue(false); |
| } |
| catch(IllegalArgumentException e) |
| { |
| |
| } |
| assertEquals(-1,request.getDateHeader("Other")); |
| return true; |
| } |
| }; |
| |
| String request="GET /? HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Connection: close\n"+ |
| "Content-Type: \n"+ |
| "Accept-Language: \n"+ |
| "Cookie: \n"+ |
| "Name: \n"+ |
| "\n"; |
| |
| String responses=_connector.getResponses(request); |
| assertTrue(responses.startsWith("HTTP/1.1 200")); |
| } |
| |
| @Test |
| public void testMultiPartNoConfig() throws Exception |
| { |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) |
| { |
| try |
| { |
| Part foo = request.getPart("stuff"); |
| return false; |
| } |
| catch (IllegalStateException e) |
| { |
| //expected exception because no multipart config is set up |
| assertTrue(e.getMessage().startsWith("No multipart config")); |
| return true; |
| } |
| catch (Exception e) |
| { |
| return false; |
| } |
| } |
| }; |
| |
| String multipart = "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"field1\"\r\n"+ |
| "\r\n"+ |
| "Joe Blow\r\n"+ |
| "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"stuff\"\r\n"+ |
| "Content-Type: text/plain;charset=ISO-8859-1\r\n"+ |
| "\r\n"+ |
| "000000000000000000000000000000000000000000000000000\r\n"+ |
| "--AaB03x--\r\n"; |
| |
| String request="GET / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: multipart/form-data; boundary=\"AaB03x\"\r\n"+ |
| "Content-Length: "+multipart.getBytes().length+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| multipart; |
| |
| String responses=_connector.getResponses(request); |
| assertTrue(responses.startsWith("HTTP/1.1 200")); |
| } |
| |
| @Test |
| public void testLocale() throws Exception |
| { |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException |
| { |
| assertThat(request.getLocale().getLanguage(),is("da")); |
| Enumeration<Locale> locales = request.getLocales(); |
| Locale locale=locales.nextElement(); |
| assertThat(locale.getLanguage(),is("da")); |
| assertThat(locale.getCountry(),is("")); |
| locale=locales.nextElement(); |
| assertThat(locale.getLanguage(),is("en")); |
| assertThat(locale.getCountry(),is("AU")); |
| locale=locales.nextElement(); |
| assertThat(locale.getLanguage(),is("en")); |
| assertThat(locale.getCountry(),is("GB")); |
| locale=locales.nextElement(); |
| assertThat(locale.getLanguage(),is("en")); |
| assertThat(locale.getCountry(),is("")); |
| assertFalse(locales.hasMoreElements()); |
| return true; |
| } |
| }; |
| |
| String request="GET / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Connection: close\r\n"+ |
| "Accept-Language: da, en-gb;q=0.8, en;q=0.7\r\n"+ |
| "Accept-Language: XX;q=0, en-au;q=0.9\r\n"+ |
| "\r\n"; |
| String response = _connector.getResponses(request); |
| assertThat(response, containsString(" 200 OK")); |
| } |
| |
| |
| @Test |
| public void testMultiPart() throws Exception |
| { |
| final File testTmpDir = File.createTempFile("reqtest", null); |
| if (testTmpDir.exists()) |
| testTmpDir.delete(); |
| testTmpDir.mkdir(); |
| testTmpDir.deleteOnExit(); |
| assertTrue(testTmpDir.list().length == 0); |
| |
| ContextHandler contextHandler = new ContextHandler(); |
| contextHandler.setContextPath("/foo"); |
| contextHandler.setResourceBase("."); |
| contextHandler.setHandler(new MultiPartRequestHandler(testTmpDir)); |
| contextHandler.addEventListener(new MultiPartCleanerListener() |
| { |
| |
| @Override |
| public void requestDestroyed(ServletRequestEvent sre) |
| { |
| MultiPartInputStreamParser m = (MultiPartInputStreamParser)sre.getServletRequest().getAttribute(Request.__MULTIPART_INPUT_STREAM); |
| ContextHandler.Context c = (ContextHandler.Context)sre.getServletRequest().getAttribute(Request.__MULTIPART_CONTEXT); |
| assertNotNull (m); |
| assertNotNull (c); |
| assertTrue(c == sre.getServletContext()); |
| assertTrue(!m.getParsedParts().isEmpty()); |
| assertTrue(testTmpDir.list().length == 2); |
| super.requestDestroyed(sre); |
| String[] files = testTmpDir.list(); |
| assertTrue(files.length == 0); |
| } |
| |
| }); |
| _server.stop(); |
| _server.setHandler(contextHandler); |
| _server.start(); |
| |
| String multipart = "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"field1\"\r\n"+ |
| "\r\n"+ |
| "Joe Blow\r\n"+ |
| "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"stuff\"; filename=\"foo.upload\"\r\n"+ |
| "Content-Type: text/plain;charset=ISO-8859-1\r\n"+ |
| "\r\n"+ |
| "000000000000000000000000000000000000000000000000000\r\n"+ |
| "--AaB03x--\r\n"; |
| |
| String request="GET /foo/x.html HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: multipart/form-data; boundary=\"AaB03x\"\r\n"+ |
| "Content-Length: "+multipart.getBytes().length+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| multipart; |
| |
| String responses=_connector.getResponses(request); |
| // System.err.println(responses); |
| assertTrue(responses.startsWith("HTTP/1.1 200")); |
| } |
| |
| @Test |
| public void testBadMultiPart() throws Exception |
| { |
| //a bad multipart where one of the fields has no name |
| final File testTmpDir = File.createTempFile("badmptest", null); |
| if (testTmpDir.exists()) |
| testTmpDir.delete(); |
| testTmpDir.mkdir(); |
| testTmpDir.deleteOnExit(); |
| assertTrue(testTmpDir.list().length == 0); |
| |
| ContextHandler contextHandler = new ContextHandler(); |
| contextHandler.setContextPath("/foo"); |
| contextHandler.setResourceBase("."); |
| contextHandler.setHandler(new BadMultiPartRequestHandler(testTmpDir)); |
| contextHandler.addEventListener(new MultiPartCleanerListener() |
| { |
| |
| @Override |
| public void requestDestroyed(ServletRequestEvent sre) |
| { |
| MultiPartInputStreamParser m = (MultiPartInputStreamParser)sre.getServletRequest().getAttribute(Request.__MULTIPART_INPUT_STREAM); |
| ContextHandler.Context c = (ContextHandler.Context)sre.getServletRequest().getAttribute(Request.__MULTIPART_CONTEXT); |
| assertNotNull (m); |
| assertNotNull (c); |
| assertTrue(c == sre.getServletContext()); |
| super.requestDestroyed(sre); |
| String[] files = testTmpDir.list(); |
| assertTrue(files.length == 0); |
| } |
| |
| }); |
| _server.stop(); |
| _server.setHandler(contextHandler); |
| _server.start(); |
| |
| String multipart = "--AaB03x\r\n"+ |
| "content-disposition: form-data; name=\"xxx\"\r\n"+ |
| "\r\n"+ |
| "Joe Blow\r\n"+ |
| "--AaB03x\r\n"+ |
| "content-disposition: form-data; filename=\"foo.upload\"\r\n"+ |
| "Content-Type: text/plain;charset=ISO-8859-1\r\n"+ |
| "\r\n"+ |
| "000000000000000000000000000000000000000000000000000\r\n"+ |
| "--AaB03x--\r\n"; |
| |
| String request="GET /foo/x.html HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: multipart/form-data; boundary=\"AaB03x\"\r\n"+ |
| "Content-Length: "+multipart.getBytes().length+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| multipart; |
| |
| try(StacklessLogging stackless = new StacklessLogging(HttpChannel.class)) |
| { |
| String responses=_connector.getResponses(request); |
| //System.err.println(responses); |
| assertTrue(responses.startsWith("HTTP/1.1 500")); |
| } |
| } |
| |
| |
| @Test |
| public void testBadUtf8ParamExtraction() throws Exception |
| { |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) |
| { |
| try |
| { |
| // This throws an exception if attempted |
| request.getParameter("param"); |
| return false; |
| } |
| catch(BadMessageException e) |
| { |
| // Should still be able to get the raw query. |
| String rawQuery = request.getQueryString(); |
| return rawQuery.equals("param=aaa%E7bbb"); |
| } |
| } |
| }; |
| |
| //Send a request with query string with illegal hex code to cause |
| //an exception parsing the params |
| String request="GET /?param=aaa%E7bbb HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: text/html;charset=utf8\n"+ |
| "Connection: close\n"+ |
| "\n"; |
| |
| LOG.info("Expecting NotUtf8Exception in state 36..."); |
| String responses=_connector.getResponses(request); |
| assertThat(responses,startsWith("HTTP/1.1 200")); |
| } |
| |
| @Test |
| public void testInvalidHostHeader() throws Exception |
| { |
| // Use a contextHandler with vhosts to force call to Request.getServerName() |
| ContextHandler context = new ContextHandler(); |
| context.addVirtualHosts(new String[]{"something"}); |
| _server.stop(); |
| _server.setHandler(context); |
| _server.start(); |
| |
| |
| // Request with illegal Host header |
| String request="GET / HTTP/1.1\n"+ |
| "Host: whatever.com:xxxx\n"+ |
| "Content-Type: text/html;charset=utf8\n"+ |
| "Connection: close\n"+ |
| "\n"; |
| |
| String responses=_connector.getResponses(request); |
| assertThat(responses, Matchers.startsWith("HTTP/1.1 400")); |
| } |
| |
| @Test |
| public void testContentTypeEncoding() throws Exception |
| { |
| final ArrayList<String> results = new ArrayList<>(); |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) |
| { |
| results.add(request.getContentType()); |
| results.add(request.getCharacterEncoding()); |
| return true; |
| } |
| }; |
| |
| _connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Content-Type: text/test\n"+ |
| "\n"+ |
| |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Content-Type: text/html;charset=utf8\n"+ |
| "\n"+ |
| |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Content-Type: text/html; charset=\"utf8\"\n"+ |
| "\n"+ |
| |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Content-Type: text/html; other=foo ; blah=\"charset=wrong;\" ; charset = \" x=z; \" ; more=values \n"+ |
| "Connection: close\n"+ |
| "\n" |
| ); |
| |
| int i=0; |
| assertEquals("text/test",results.get(i++)); |
| assertEquals(null,results.get(i++)); |
| |
| assertEquals("text/html;charset=utf8",results.get(i++)); |
| assertEquals("utf-8",results.get(i++)); |
| |
| assertEquals("text/html; charset=\"utf8\"",results.get(i++)); |
| assertEquals("utf-8",results.get(i++)); |
| |
| assertTrue(results.get(i++).startsWith("text/html")); |
| assertEquals(" x=z; ",results.get(i++)); |
| } |
| |
| @Test |
| public void testHostPort() throws Exception |
| { |
| final ArrayList<String> results = new ArrayList<>(); |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) |
| { |
| results.add(request.getRequestURL().toString()); |
| results.add(request.getRemoteAddr()); |
| results.add(request.getServerName()); |
| results.add(String.valueOf(request.getServerPort())); |
| return true; |
| } |
| }; |
| |
| results.clear(); |
| String response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: myhost\n"+ |
| "Connection: close\n"+ |
| "\n"); |
| int i=0; |
| assertThat(response, containsString("200 OK")); |
| assertEquals("http://myhost/",results.get(i++)); |
| assertEquals("0.0.0.0",results.get(i++)); |
| assertEquals("myhost",results.get(i++)); |
| assertEquals("80",results.get(i++)); |
| |
| |
| results.clear(); |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: myhost:8888\n"+ |
| "Connection: close\n"+ |
| "\n"); |
| i=0; |
| assertThat(response, containsString("200 OK")); |
| assertEquals("http://myhost:8888/",results.get(i++)); |
| assertEquals("0.0.0.0",results.get(i++)); |
| assertEquals("myhost",results.get(i++)); |
| assertEquals("8888",results.get(i++)); |
| |
| |
| results.clear(); |
| response=_connector.getResponses( |
| "GET http://myhost:8888/ HTTP/1.0\n"+ |
| "\n"); |
| i=0; |
| assertThat(response, containsString("200 OK")); |
| assertEquals("http://myhost:8888/",results.get(i++)); |
| assertEquals("0.0.0.0",results.get(i++)); |
| assertEquals("myhost",results.get(i++)); |
| assertEquals("8888",results.get(i++)); |
| |
| results.clear(); |
| response=_connector.getResponses( |
| "GET http://myhost:8888/ HTTP/1.1\n"+ |
| "Host: wrong:666\n"+ |
| "Connection: close\n"+ |
| "\n"); |
| i=0; |
| assertThat(response, containsString("200 OK")); |
| assertEquals("http://myhost:8888/",results.get(i++)); |
| assertEquals("0.0.0.0",results.get(i++)); |
| assertEquals("myhost",results.get(i++)); |
| assertEquals("8888",results.get(i++)); |
| |
| |
| results.clear(); |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: 1.2.3.4\n"+ |
| "Connection: close\n"+ |
| "\n"); |
| i=0; |
| |
| assertThat(response, containsString("200 OK")); |
| assertEquals("http://1.2.3.4/",results.get(i++)); |
| assertEquals("0.0.0.0",results.get(i++)); |
| assertEquals("1.2.3.4",results.get(i++)); |
| assertEquals("80",results.get(i++)); |
| |
| |
| results.clear(); |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: 1.2.3.4:8888\n"+ |
| "Connection: close\n"+ |
| "\n"); |
| i=0; |
| assertThat(response, containsString("200 OK")); |
| assertEquals("http://1.2.3.4:8888/",results.get(i++)); |
| assertEquals("0.0.0.0",results.get(i++)); |
| assertEquals("1.2.3.4",results.get(i++)); |
| assertEquals("8888",results.get(i++)); |
| |
| |
| results.clear(); |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: [::1]\n"+ |
| "Connection: close\n"+ |
| "\n"); |
| i=0; |
| assertThat(response, containsString("200 OK")); |
| assertEquals("http://[::1]/",results.get(i++)); |
| assertEquals("0.0.0.0",results.get(i++)); |
| assertEquals("[::1]",results.get(i++)); |
| assertEquals("80",results.get(i++)); |
| |
| |
| results.clear(); |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: [::1]:8888\n"+ |
| "Connection: close\n"+ |
| "\n"); |
| i=0; |
| assertThat(response, containsString("200 OK")); |
| assertEquals("http://[::1]:8888/",results.get(i++)); |
| assertEquals("0.0.0.0",results.get(i++)); |
| assertEquals("[::1]",results.get(i++)); |
| assertEquals("8888",results.get(i++)); |
| |
| |
| results.clear(); |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: [::1]\n"+ |
| "x-forwarded-for: remote\n"+ |
| "x-forwarded-proto: https\n"+ |
| "Connection: close\n"+ |
| "\n"); |
| i=0; |
| assertThat(response, containsString("200 OK")); |
| assertEquals("https://[::1]/",results.get(i++)); |
| assertEquals("remote",results.get(i++)); |
| assertEquals("[::1]",results.get(i++)); |
| assertEquals("443",results.get(i++)); |
| |
| |
| results.clear(); |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: [::1]:8888\n"+ |
| "Connection: close\n"+ |
| "x-forwarded-for: remote\n"+ |
| "x-forwarded-proto: https\n"+ |
| "\n"); |
| i=0; |
| assertThat(response, containsString("200 OK")); |
| assertEquals("https://[::1]:8888/",results.get(i++)); |
| assertEquals("remote",results.get(i++)); |
| assertEquals("[::1]",results.get(i++)); |
| assertEquals("8888",results.get(i++)); |
| } |
| |
| @Test |
| public void testContent() throws Exception |
| { |
| final AtomicInteger length=new AtomicInteger(); |
| |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException |
| { |
| int len=request.getContentLength(); |
| ServletInputStream in = request.getInputStream(); |
| for (int i=0;i<len;i++) |
| { |
| int b=in.read(); |
| if (b<0) |
| return false; |
| } |
| if (in.read()>0) |
| return false; |
| |
| length.set(len); |
| return true; |
| } |
| }; |
| |
| |
| String content=""; |
| |
| for (int l=0;l<1024;l++) |
| { |
| String request="POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: multipart/form-data-test\r\n"+ |
| "Content-Length: "+l+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| content; |
| Log.getRootLogger().debug("test l={}",l); |
| String response = _connector.getResponses(request); |
| Log.getRootLogger().debug(response); |
| assertThat(response, containsString(" 200 OK")); |
| assertEquals(l,length.get()); |
| content+="x"; |
| } |
| } |
| |
| @Test |
| public void testEncodedForm() throws Exception |
| { |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException |
| { |
| String actual = request.getParameter("name2"); |
| return "test2".equals(actual); |
| } |
| }; |
| |
| |
| String content="name1=test&name2=test2&name3=&name4=test"; |
| String request="POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n" + |
| "Content-Length: "+content.length()+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| content; |
| String response = _connector.getResponses(request); |
| assertThat(response, containsString(" 200 OK")); |
| } |
| |
| @Test |
| public void testEncodedFormUnknownMethod() throws Exception |
| { |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException |
| { |
| return request.getParameter("name1")==null && request.getParameter("name2")==null && request.getParameter("name3")==null; |
| } |
| }; |
| |
| String content="name1=test&name2=test2&name3=&name4=test"; |
| String request="UNKNOWN / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n" + |
| "Content-Length: "+content.length()+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| content; |
| String response = _connector.getResponses(request); |
| assertThat(response, containsString(" 200 OK")); |
| } |
| |
| @Test |
| public void testEncodedFormExtraMethod() throws Exception |
| { |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException |
| { |
| String actual = request.getParameter("name2"); |
| return "test2".equals(actual); |
| } |
| }; |
| |
| _connector.getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().addFormEncodedMethod("Extra"); |
| String content="name1=test&name2=test2&name3=&name4=test"; |
| String request="EXTRA / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n" + |
| "Content-Length: "+content.length()+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| content; |
| String response = _connector.getResponses(request); |
| assertThat(response, containsString(" 200 OK")); |
| } |
| |
| @Test |
| public void test8859EncodedForm() throws Exception |
| { |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException |
| { |
| // Should be "testä" |
| // "test" followed by a LATIN SMALL LETTER A WITH DIAERESIS |
| request.setCharacterEncoding(StandardCharsets.ISO_8859_1.name()); |
| String actual = request.getParameter("name2"); |
| return "test\u00e4".equals(actual); |
| } |
| }; |
| |
| String content="name1=test&name2=test%E4&name3=&name4=test"; |
| String request="POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n" + |
| "Content-Length: "+content.length()+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| content; |
| String response = _connector.getResponses(request); |
| assertThat(response, containsString(" 200 OK")); |
| } |
| |
| @Test |
| public void testUTF8EncodedForm() throws Exception |
| { |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException |
| { |
| // http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=00e4&mode=hex |
| // Should be "testä" |
| // "test" followed by a LATIN SMALL LETTER A WITH DIAERESIS |
| String actual = request.getParameter("name2"); |
| return "test\u00e4".equals(actual); |
| } |
| }; |
| |
| String content="name1=test&name2=test%C3%A4&name3=&name4=test"; |
| String request="POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n" + |
| "Content-Length: "+content.length()+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| content; |
| String response = _connector.getResponses(request); |
| assertThat(response, containsString(" 200 OK")); |
| } |
| |
| |
| @Test |
| @Ignore("See issue #1175") |
| public void testMultiPartFormDataReadInputThenParams() throws Exception |
| { |
| final File tmpdir = MavenTestingUtils.getTargetTestingDir("multipart"); |
| FS.ensureEmpty(tmpdir); |
| |
| Handler handler = new AbstractHandler() |
| { |
| @Override |
| public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, |
| ServletException |
| { |
| if (baseRequest.getDispatcherType() != DispatcherType.REQUEST) |
| return; |
| |
| // Fake a MultiPartConfig'd servlet endpoint |
| MultipartConfigElement multipartConfig = new MultipartConfigElement(tmpdir.getAbsolutePath()); |
| request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, multipartConfig); |
| |
| // Normal processing |
| baseRequest.setHandled(true); |
| |
| // Fake the commons-fileupload behavior |
| int length = request.getContentLength(); |
| InputStream in = request.getInputStream(); |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| IO.copy(in, out, length); // commons-fileupload does not read to EOF |
| |
| LOG.info("input stream = " + in); |
| |
| // Record what happened as servlet response headers |
| response.setIntHeader("x-request-content-length", request.getContentLength()); |
| response.setIntHeader("x-request-content-read", out.size()); |
| String foo = request.getParameter("foo"); // uri query parameter |
| String bar = request.getParameter("bar"); // form-data content parameter |
| response.setHeader("x-foo", foo == null ? "null" : foo); |
| response.setHeader("x-bar", bar == null ? "null" : bar); |
| } |
| }; |
| _server.stop(); |
| _server.setHandler(handler); |
| _server.start(); |
| |
| String multipart = "--AaBbCc\r\n"+ |
| "content-disposition: form-data; name=\"bar\"\r\n"+ |
| "\r\n"+ |
| "BarContent\r\n"+ |
| "--AaBbCc\r\n"+ |
| "content-disposition: form-data; name=\"stuff\"\r\n"+ |
| "Content-Type: text/plain;charset=ISO-8859-1\r\n"+ |
| "\r\n"+ |
| "000000000000000000000000000000000000000000000000000\r\n"+ |
| "--AaBbCc--\r\n"; |
| |
| String request="POST /?foo=FooUri HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: multipart/form-data; boundary=\"AaBbCc\"\r\n"+ |
| "Content-Length: "+multipart.getBytes().length+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| multipart; |
| |
| |
| HttpTester.Response response = HttpTester.parseResponse(_connector.getResponse(request)); |
| |
| // It should always be possible to read query string |
| assertThat("response.x-foo", response.get("x-foo"), is("FooUri")); |
| // Not possible to read request content parameters? |
| assertThat("response.x-bar", response.get("x-bar"), is("null")); // TODO: should this work? |
| } |
| |
| @Test |
| public void testPartialRead() throws Exception |
| { |
| Handler handler = new AbstractHandler() |
| { |
| @Override |
| public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, |
| ServletException |
| { |
| baseRequest.setHandled(true); |
| Reader reader=request.getReader(); |
| byte[] b=("read="+reader.read()+"\n").getBytes(StandardCharsets.UTF_8); |
| response.setContentLength(b.length); |
| response.getOutputStream().write(b); |
| response.flushBuffer(); |
| } |
| |
| }; |
| _server.stop(); |
| _server.setHandler(handler); |
| _server.start(); |
| |
| String request="GET / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: text/plane\r\n"+ |
| "Content-Length: "+10+"\r\n"+ |
| "\r\n"+ |
| "0123456789\r\n"+ |
| "GET / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: text/plane\r\n"+ |
| "Content-Length: "+10+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| "ABCDEFGHIJ\r\n"; |
| |
| String responses = _connector.getResponses(request); |
| |
| int index=responses.indexOf("read="+(int)'0'); |
| assertTrue(index>0); |
| |
| index=responses.indexOf("read="+(int)'A',index+7); |
| assertTrue(index>0); |
| } |
| |
| @Test |
| public void testQueryAfterRead() |
| throws Exception |
| { |
| Handler handler = new AbstractHandler() |
| { |
| @Override |
| public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, |
| ServletException |
| { |
| baseRequest.setHandled(true); |
| Reader reader=request.getReader(); |
| String in = IO.toString(reader); |
| String param = request.getParameter("param"); |
| |
| byte[] b=("read='"+in+"' param="+param+"\n").getBytes(StandardCharsets.UTF_8); |
| response.setContentLength(b.length); |
| response.getOutputStream().write(b); |
| response.flushBuffer(); |
| } |
| }; |
| _server.stop(); |
| _server.setHandler(handler); |
| _server.start(); |
| |
| String request="POST /?param=right HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: application/x-www-form-urlencoded\r\n"+ |
| "Content-Length: "+11+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| "param=wrong\r\n"; |
| |
| String responses = _connector.getResponses(request); |
| |
| assertTrue(responses.indexOf("read='param=wrong' param=right")>0); |
| |
| } |
| |
| @Test |
| public void testSessionAfterRedirect() throws Exception |
| { |
| Handler handler = new AbstractHandler() |
| { |
| @Override |
| public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, |
| ServletException |
| { |
| baseRequest.setHandled(true); |
| response.sendRedirect("/foo"); |
| try |
| { |
| request.getSession(true); |
| fail("Session should not be created after response committed"); |
| } |
| catch (IllegalStateException e) |
| { |
| //expected |
| } |
| catch (Exception e) |
| { |
| fail("Session creation after response commit should throw IllegalStateException"); |
| } |
| } |
| }; |
| _server.stop(); |
| _server.setHandler(handler); |
| _server.start(); |
| String response=_connector.getResponses("GET / HTTP/1.1\n"+ |
| "Host: myhost\n"+ |
| "Connection: close\n"+ |
| "\n"); |
| assertThat(response, containsString(" 302 Found")); |
| assertThat(response, containsString("Location: http://myhost/foo")); |
| } |
| |
| @Test |
| public void testPartialInput() throws Exception |
| { |
| Handler handler = new AbstractHandler() |
| { |
| @Override |
| public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, |
| ServletException |
| { |
| baseRequest.setHandled(true); |
| InputStream in=request.getInputStream(); |
| byte[] b=("read="+in.read()+"\n").getBytes(StandardCharsets.UTF_8); |
| response.setContentLength(b.length); |
| response.getOutputStream().write(b); |
| response.flushBuffer(); |
| } |
| |
| }; |
| _server.stop(); |
| _server.setHandler(handler); |
| _server.start(); |
| |
| String request="GET / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: text/plane\r\n"+ |
| "Content-Length: "+10+"\r\n"+ |
| "\r\n"+ |
| "0123456789\r\n"+ |
| "GET / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: text/plane\r\n"+ |
| "Content-Length: "+10+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| "ABCDEFGHIJ\r\n"; |
| |
| String responses = _connector.getResponses(request); |
| |
| int index=responses.indexOf("read="+(int)'0'); |
| assertTrue(index>0); |
| |
| index=responses.indexOf("read="+(int)'A',index+7); |
| assertTrue(index>0); |
| } |
| |
| @Test |
| public void testConnectionClose() throws Exception |
| { |
| String response; |
| |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException |
| { |
| response.getOutputStream().println("Hello World"); |
| return true; |
| } |
| }; |
| |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "\n", |
| 200, TimeUnit.MILLISECONDS |
| ); |
| assertThat(response, containsString("200")); |
| assertThat(response, Matchers.not(containsString("Connection: close"))); |
| assertThat(response, containsString("Hello World")); |
| |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Connection: close\n"+ |
| "\n" |
| ); |
| assertThat(response, containsString("200")); |
| assertThat(response, containsString("Connection: close")); |
| assertThat(response, containsString("Hello World")); |
| |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Connection: Other, close\n"+ |
| "\n" |
| ); |
| |
| assertThat(response, containsString("200")); |
| assertThat(response, containsString("Connection: close")); |
| assertThat(response, containsString("Hello World")); |
| |
| response=_connector.getResponses( |
| "GET / HTTP/1.0\n"+ |
| "Host: whatever\n"+ |
| "\n" |
| ); |
| assertThat(response, containsString("200")); |
| assertThat(response, Matchers.not(containsString("Connection: close"))); |
| assertThat(response, containsString("Hello World")); |
| |
| response=_connector.getResponses( |
| "GET / HTTP/1.0\n"+ |
| "Host: whatever\n"+ |
| "Connection: Other, close\n"+ |
| "\n" |
| ); |
| assertThat(response, containsString("200")); |
| assertThat(response, containsString("Hello World")); |
| |
| response=_connector.getResponses( |
| "GET / HTTP/1.0\n"+ |
| "Host: whatever\n"+ |
| "Connection: Other,,keep-alive\n"+ |
| "\n", |
| 200, TimeUnit.MILLISECONDS |
| ); |
| assertThat(response, containsString("200")); |
| assertThat(response, containsString("Connection: keep-alive")); |
| assertThat(response, containsString("Hello World")); |
| |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException |
| { |
| response.setHeader("Connection","TE"); |
| response.addHeader("Connection","Other"); |
| response.getOutputStream().println("Hello World"); |
| return true; |
| } |
| }; |
| |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "\n", |
| 200, TimeUnit.MILLISECONDS |
| ); |
| assertThat(response, containsString("200")); |
| assertThat(response, containsString("Connection: TE,Other")); |
| assertThat(response, containsString("Hello World")); |
| |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Connection: close\n"+ |
| "\n" |
| ); |
| assertThat(response, containsString("200 OK")); |
| assertThat(response, containsString("Connection: close")); |
| assertThat(response, containsString("Hello World")); |
| } |
| |
| @Test |
| public void testCookies() throws Exception |
| { |
| final ArrayList<Cookie> cookies = new ArrayList<>(); |
| |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException |
| { |
| javax.servlet.http.Cookie[] ca = request.getCookies(); |
| if (ca!=null) |
| cookies.addAll(Arrays.asList(ca)); |
| response.getOutputStream().println("Hello World"); |
| return true; |
| } |
| }; |
| |
| String response; |
| |
| cookies.clear(); |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Connection: close\n"+ |
| "\n" |
| ); |
| assertTrue(response.startsWith("HTTP/1.1 200 OK")); |
| assertEquals(0,cookies.size()); |
| |
| |
| cookies.clear(); |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Cookie: name=quoted=\"\\\"value\\\"\"\n" + |
| "Connection: close\n"+ |
| "\n" |
| ); |
| assertTrue(response.startsWith("HTTP/1.1 200 OK")); |
| assertEquals(1,cookies.size()); |
| assertEquals("name", cookies.get(0).getName()); |
| assertEquals("quoted=\"value\"", cookies.get(0).getValue()); |
| |
| cookies.clear(); |
| response=_connector.getResponses( |
| "GET / HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Cookie: name=value; other=\"quoted=;value\"\n" + |
| "Connection: close\n"+ |
| "\n" |
| ); |
| assertTrue(response.startsWith("HTTP/1.1 200 OK")); |
| assertEquals(2,cookies.size()); |
| assertEquals("name", cookies.get(0).getName()); |
| assertEquals("value", cookies.get(0).getValue()); |
| assertEquals("other", cookies.get(1).getName()); |
| assertEquals("quoted=;value", cookies.get(1).getValue()); |
| |
| cookies.clear(); |
| response=_connector.getResponses( |
| "GET /other HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Other: header\n"+ |
| "Cookie: name=value; other=\"quoted=;value\"\n" + |
| "\n"+ |
| "GET /other HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Other: header\n"+ |
| "Cookie: name=value; other=\"quoted=;value\"\n" + |
| "Connection: close\n"+ |
| "\n" |
| ); |
| assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK")); |
| assertThat(response.substring(15), containsString("HTTP/1.1 200 OK")); |
| assertEquals(4,cookies.size()); |
| assertEquals("name", cookies.get(0).getName()); |
| assertEquals("value", cookies.get(0).getValue()); |
| assertEquals("other", cookies.get(1).getName()); |
| assertEquals("quoted=;value", cookies.get(1).getValue()); |
| |
| assertSame(cookies.get(0), cookies.get(2)); |
| assertSame(cookies.get(1), cookies.get(3)); |
| |
| cookies.clear(); |
| response=_connector.getResponses( |
| "GET /other HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Other: header\n"+ |
| "Cookie: name=value; other=\"quoted=;value\"\n" + |
| "\n"+ |
| "GET /other HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Other: header\n"+ |
| "Cookie: name=value; other=\"othervalue\"\n" + |
| "Connection: close\n"+ |
| "\n" |
| ); |
| assertThat(response, Matchers.startsWith("HTTP/1.1 200 OK")); |
| assertThat(response.substring(15), containsString("HTTP/1.1 200 OK")); |
| assertEquals(4,cookies.size()); |
| assertEquals("name", cookies.get(0).getName()); |
| assertEquals("value", cookies.get(0).getValue()); |
| assertEquals("other", cookies.get(1).getName()); |
| assertEquals("quoted=;value", cookies.get(1).getValue()); |
| |
| assertNotSame(cookies.get(0), cookies.get(2)); |
| assertNotSame(cookies.get(1), cookies.get(3)); |
| |
| cookies.clear(); |
| //NOTE: the javax.servlet.http.Cookie class sets the system property org.glassfish.web.rfc2109_cookie_names_enforced |
| //to TRUE by default, and rejects all cookie names containing punctuation.Therefore this test cannot use "name2". |
| response=_connector.getResponses( |
| "POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Cookie: name0=value0; name1 = value1 ; \"name2\" = \"\\\"value2\\\"\" \n" + |
| "Cookie: $Version=2; name3=value3=value3;$path=/path;$domain=acme.com;$port=8080; name4=; name5 = ; name6\n" + |
| "Cookie: name7=value7;\n" + |
| "Connection: close\r\n"+ |
| "\r\n"); |
| |
| assertEquals("name0", cookies.get(0).getName()); |
| assertEquals("value0", cookies.get(0).getValue()); |
| assertEquals("name1", cookies.get(1).getName()); |
| assertEquals("value1", cookies.get(1).getValue()); |
| assertEquals("name2", cookies.get(2).getName()); |
| assertEquals("\"value2\"", cookies.get(2).getValue()); |
| assertEquals("name3", cookies.get(3).getName()); |
| assertEquals("value3=value3", cookies.get(3).getValue()); |
| assertEquals(2, cookies.get(3).getVersion()); |
| assertEquals("/path", cookies.get(3).getPath()); |
| assertEquals("acme.com", cookies.get(3).getDomain()); |
| assertEquals("$port=8080", cookies.get(3).getComment()); |
| assertEquals("name4", cookies.get(4).getName()); |
| assertEquals("", cookies.get(4).getValue()); |
| assertEquals("name5", cookies.get(5).getName()); |
| assertEquals("", cookies.get(5).getValue()); |
| assertEquals("name6", cookies.get(6).getName()); |
| assertEquals("", cookies.get(6).getValue()); |
| assertEquals("name7", cookies.get(7).getName()); |
| assertEquals("value7", cookies.get(7).getValue()); |
| |
| cookies.clear(); |
| response=_connector.getResponses( |
| "GET /other HTTP/1.1\n"+ |
| "Host: whatever\n"+ |
| "Other: header\n"+ |
| "Cookie: __utmz=14316.133020.1.1.utr=gna.de|ucn=(real)|utd=reral|utct=/games/hen-one,gnt-50-ba-keys:key,2072262.html\n"+ |
| "Connection: close\n"+ |
| "\n" |
| ); |
| assertTrue(response.startsWith("HTTP/1.1 200 OK")); |
| assertEquals(1,cookies.size()); |
| assertEquals("__utmz", cookies.get(0).getName()); |
| assertEquals("14316.133020.1.1.utr=gna.de|ucn=(real)|utd=reral|utct=/games/hen-one,gnt-50-ba-keys:key,2072262.html", cookies.get(0).getValue()); |
| |
| } |
| |
| @Ignore("No longer relevant") |
| @Test |
| public void testCookieLeak() throws Exception |
| { |
| final String[] cookie=new String[10]; |
| |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) |
| { |
| for (int i=0;i<cookie.length; i++) |
| cookie[i]=null; |
| |
| Cookie[] cookies = request.getCookies(); |
| for (int i=0;cookies!=null && i<cookies.length; i++) |
| { |
| cookie[i]=cookies[i].getValue(); |
| } |
| return true; |
| } |
| }; |
| |
| String request="POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Cookie: other=cookie\r\n"+ |
| "\r\n" |
| + |
| "POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Cookie: name=value\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"; |
| |
| _connector.getResponses(request); |
| |
| assertEquals("value",cookie[0]); |
| assertEquals(null,cookie[1]); |
| |
| request="POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Cookie: name=value\r\n"+ |
| "\r\n" |
| + |
| "POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Cookie: \r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"; |
| |
| _connector.getResponses(request); |
| assertEquals(null,cookie[0]); |
| assertEquals(null,cookie[1]); |
| |
| request="POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Cookie: name=value\r\n"+ |
| "Cookie: other=cookie\r\n"+ |
| "\r\n" |
| + |
| "POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Cookie: name=value\r\n"+ |
| "Cookie:\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"; |
| |
| _connector.getResponses(request); |
| |
| assertEquals("value",cookie[0]); |
| assertEquals(null,cookie[1]); |
| } |
| |
| |
| @Test |
| public void testHashDOSKeys() throws Exception |
| { |
| try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class)) |
| { |
| // Expecting maxFormKeys limit and Closing HttpParser exceptions... |
| _server.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize",-1); |
| _server.setAttribute("org.eclipse.jetty.server.Request.maxFormKeys",1000); |
| |
| StringBuilder buf = new StringBuilder(4000000); |
| buf.append("a=b"); |
| |
| // The evil keys file is not distributed - as it is dangerous |
| File evil_keys = new File("/tmp/keys_mapping_to_zero_2m"); |
| if (evil_keys.exists()) |
| { |
| // Using real evil keys! |
| try (BufferedReader in = new BufferedReader(new FileReader(evil_keys))) |
| { |
| String key=null; |
| while((key=in.readLine())!=null) |
| buf.append("&").append(key).append("=").append("x"); |
| } |
| } |
| else |
| { |
| // we will just create a lot of keys and make sure the limit is applied |
| for (int i=0;i<2000;i++) |
| buf.append("&").append("K").append(i).append("=").append("x"); |
| } |
| buf.append("&c=d"); |
| |
| |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) |
| { |
| return "b".equals(request.getParameter("a")) && request.getParameter("c")==null; |
| } |
| }; |
| |
| String request="POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n"+ |
| "Content-Length: "+buf.length()+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| buf; |
| |
| long start=System.currentTimeMillis(); |
| String rawResponse = _connector.getResponses(request); |
| HttpTester.Response response = HttpTester.parseResponse(rawResponse); |
| assertThat("Response.status", response.getStatus(), is(400)); |
| assertThat("Response body content", response.getContent(),containsString(BadMessageException.class.getName())); |
| assertThat("Response body content", response.getContent(),containsString(IllegalStateException.class.getName())); |
| long now=System.currentTimeMillis(); |
| assertTrue((now-start)<5000); |
| } |
| } |
| |
| @Test |
| public void testHashDOSSize() throws Exception |
| { |
| try (StacklessLogging stackless = new StacklessLogging(HttpChannel.class)) |
| { |
| LOG.info("Expecting maxFormSize limit and too much data exceptions..."); |
| _server.setAttribute("org.eclipse.jetty.server.Request.maxFormContentSize",3396); |
| _server.setAttribute("org.eclipse.jetty.server.Request.maxFormKeys",1000); |
| |
| StringBuilder buf = new StringBuilder(4000000); |
| buf.append("a=b"); |
| // we will just create a lot of keys and make sure the limit is applied |
| for (int i=0;i<500;i++) |
| buf.append("&").append("K").append(i).append("=").append("x"); |
| buf.append("&c=d"); |
| |
| _handler._checker = new RequestTester() |
| { |
| @Override |
| public boolean check(HttpServletRequest request,HttpServletResponse response) |
| { |
| return "b".equals(request.getParameter("a")) && request.getParameter("c")==null; |
| } |
| }; |
| |
| String request="POST / HTTP/1.1\r\n"+ |
| "Host: whatever\r\n"+ |
| "Content-Type: "+MimeTypes.Type.FORM_ENCODED.asString()+"\r\n"+ |
| "Content-Length: "+buf.length()+"\r\n"+ |
| "Connection: close\r\n"+ |
| "\r\n"+ |
| buf; |
| |
| long start=System.currentTimeMillis(); |
| String rawResponse = _connector.getResponses(request); |
| HttpTester.Response response = HttpTester.parseResponse(rawResponse); |
| assertThat("Response.status", response.getStatus(), is(400)); |
| assertThat("Response body content", response.getContent(),containsString(BadMessageException.class.getName())); |
| assertThat("Response body content", response.getContent(),containsString(IllegalStateException.class.getName())); |
| long now=System.currentTimeMillis(); |
| assertTrue((now-start)<5000); |
| } |
| } |
| |
| @Test(expected = UnsupportedEncodingException.class) |
| public void testNotSupportedCharacterEncoding() throws UnsupportedEncodingException |
| { |
| Request request = new Request(null, null); |
| request.setCharacterEncoding("doesNotExist"); |
| } |
| |
| interface RequestTester |
| { |
| boolean check(HttpServletRequest request,HttpServletResponse response) throws IOException; |
| } |
| |
| private class RequestHandler extends AbstractHandler |
| { |
| private RequestTester _checker; |
| @SuppressWarnings("unused") |
| private String _content; |
| |
| @Override |
| public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException |
| { |
| ((Request)request).setHandled(true); |
| |
| if (request.getContentLength()>0 |
| && !MimeTypes.Type.FORM_ENCODED.asString().equals(request.getContentType()) |
| && !request.getContentType().startsWith("multipart/form-data")) |
| _content=IO.toString(request.getInputStream()); |
| |
| if (_checker!=null && _checker.check(request,response)) |
| response.setStatus(200); |
| else |
| response.sendError(500); |
| } |
| } |
| |
| private class MultiPartRequestHandler extends AbstractHandler |
| { |
| File tmpDir; |
| |
| public MultiPartRequestHandler(File tmpDir) |
| { |
| this.tmpDir = tmpDir; |
| } |
| |
| |
| @Override |
| public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException |
| { |
| ((Request)request).setHandled(true); |
| try |
| { |
| |
| MultipartConfigElement mpce = new MultipartConfigElement(tmpDir.getAbsolutePath(),-1, -1, 2); |
| request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce); |
| |
| String field1 = request.getParameter("field1"); |
| assertNotNull(field1); |
| |
| Part foo = request.getPart("stuff"); |
| assertNotNull(foo); |
| assertTrue(foo.getSize() > 0); |
| response.setStatus(200); |
| } |
| catch (IllegalStateException e) |
| { |
| //expected exception because no multipart config is set up |
| assertTrue(e.getMessage().startsWith("No multipart config")); |
| response.setStatus(200); |
| } |
| catch (Exception e) |
| { |
| response.sendError(500); |
| } |
| } |
| } |
| |
| private class BadMultiPartRequestHandler extends AbstractHandler |
| { |
| File tmpDir; |
| |
| public BadMultiPartRequestHandler(File tmpDir) |
| { |
| this.tmpDir = tmpDir; |
| } |
| |
| |
| @Override |
| public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException |
| { |
| ((Request)request).setHandled(true); |
| try |
| { |
| |
| MultipartConfigElement mpce = new MultipartConfigElement(tmpDir.getAbsolutePath(),-1, -1, 2); |
| request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, mpce); |
| |
| //We should get an error when we getParams if there was a problem parsing the multipart |
| request.getPart("xxx"); |
| //A 200 response is actually wrong here |
| } |
| catch (RuntimeException e) |
| { |
| response.sendError(500); |
| } |
| } |
| } |
| |
| |
| } |