// Copyright (C) 2003-2009 by Object Mentor, Inc. All rights reserved. // Released under the terms of the CPL Common Public License version 1.0. package fitnesse.http; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import org.junit.After; import org.junit.Before; import org.junit.Test; import util.FileUtil; import fitnesse.util.Base64; public class RequestTest { PipedOutputStream output; Request request; public Thread parseThread; public Exception exception; ByteArrayOutputStream messageBuffer; private String contextRoot; @Before public void setUp() throws Exception { contextRoot = "/"; output = new PipedOutputStream(); request = new Request(new PipedInputStream(output)); request.setContextRoot(contextRoot); messageBuffer = new ByteArrayOutputStream(); } @After public void tearDown() throws Exception { output.close(); } private void appendToMessage(StringBuffer buffer) throws Exception { messageBuffer.write(buffer.toString().getBytes()); } private void appendToMessage(String value) throws Exception { messageBuffer.write(value.getBytes()); } private void appendToMessage(byte[] bytes) throws Exception { messageBuffer.write(bytes); } private void parseMessage() throws Exception { ByteArrayInputStream stream = new ByteArrayInputStream(messageBuffer.toByteArray()); request = new Request(stream); request.setContextRoot(contextRoot); try { request.parse(); } catch(Exception record) { exception = record; } } @Test public void isNotParsedUntilItsParsed() { assertFalse(request.hasBeenParsed()); } @Test public void testMultilevelRequest() throws Exception { appendToMessage("GET /SomePage.SubPage?edit HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertEquals("SomePage.SubPage", request.getResource()); } @Test public void testSimpleRequest() throws Exception { appendToMessage("GET /request-uri HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertTrue(request.hasBeenParsed()); assertEquals("/request-uri", request.getRequestUri()); } @Test public void testMalformedRequestLine() throws Exception { appendToMessage("/resource HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertNotNull("no exception was thrown", exception); assertEquals("The request string is malformed and can not be parsed", exception.getMessage()); } @Test public void testBadMethod() throws Exception { appendToMessage("DELETE /resource HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertNotNull("no exception was thrown", exception); assertEquals("The DELETE method is not currently supported", exception.getMessage()); } @Test public void testQueryStringValueWithNoQueryString() throws Exception { appendToMessage("GET /resource HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertEquals("", request.getQueryString()); } @Test public void testParsingRequestUri() throws Exception { appendToMessage("GET /resource?queryString HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertEquals("resource", request.getResource()); assertEquals("queryString", request.getQueryString()); } @Test public void testContextRoot() throws Exception { contextRoot = "/fitnesse/"; appendToMessage("GET /fitnesse HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertTrue(request.hasBeenParsed()); assertEquals("", request.getResource()); } @Test public void testSimpleRequestWithContextRoot() throws Exception { contextRoot = "/fitnesse/"; appendToMessage("GET /fitnesse/request-uri HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertTrue(request.hasBeenParsed()); assertEquals("request-uri", request.getResource()); } @Test public void testCanGetQueryStringValues() throws Exception { appendToMessage("GET /resource?key1=value1&key2=value2 HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); checkInputs(); } @Test public void testHeaders() throws Exception { appendToMessage("GET /something HTTP/1.1\r\n"); appendToMessage("Content-Length: 0\r\n"); appendToMessage("Accept: text/html\r\n"); appendToMessage("Connection: Keep-Alive\r\n"); appendToMessage("\r\n"); parseMessage(); assertEquals(true, request.hasHeader("Content-Length")); assertEquals("0", request.getHeader("Content-Length")); assertEquals(true, request.hasHeader("Accept")); assertEquals("text/html", request.getHeader("Accept")); assertEquals(true, request.hasHeader("Connection")); assertEquals("Keep-Alive", request.getHeader("Connection")); assertEquals(false, request.hasHeader("Something-Else")); assertEquals(null, request.getHeader("Something-Else")); } @Test public void testEntityBodyWithoutContentLength() throws Exception { appendToMessage("GET /something HTTP/1.1\r\n"); appendToMessage("\r\nThis is the Entity Body"); parseMessage(); assertEquals("", request.getBody()); } @Test public void testEntityBodyIsRead() throws Exception { appendToMessage("GET /something HTTP/1.1\r\n"); appendToMessage("Content-Length: 23\r\n"); appendToMessage("\r\n"); appendToMessage("This is the Entity Body"); parseMessage(); assertEquals("This is the Entity Body", request.getBody()); } @Test public void testEntityBodyParametersAreParsed() throws Exception { appendToMessage("GET /something HTTP/1.1\r\n"); appendToMessage("Content-Length: 23\r\n"); appendToMessage("\r\n"); appendToMessage("key1=value1&key2=value2"); parseMessage(); checkInputs(); } private void checkInputs() { assertEquals(true, request.hasInput("key1")); assertEquals("value1", request.getInput("key1")); assertEquals(true, request.hasInput("key2")); assertEquals("value2", request.getInput("key2")); assertEquals(false, request.hasInput("someOtherKey")); assertEquals(null, request.getInput("someOtherKey")); } @Test public void testPostMethod() throws Exception { appendToMessage("POST /something HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertNull("POST method should be allowed", exception); } @Test public void testSimpleInputStyle() throws Exception { appendToMessage("GET /abc?something HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertEquals(true, request.hasInput("something")); } @Test public void testOperaPostRequest() throws Exception { appendToMessage("POST /HelloThere HTTP/1.1\r\n"); appendToMessage("User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MSIE 5.5; Windows NT 5.1) Opera 7.02 [en]\r\n"); appendToMessage("Host: localhost:75\r\n"); appendToMessage("Accept: text/html, image/png, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1\r\n"); appendToMessage("Accept-Language: en\r\n"); appendToMessage("Accept-Charset: windows-1252, utf-8, utf-16, iso-8859-1;q=0.6, *;q=0.1\r\n"); appendToMessage("Accept-Encoding: deflate, gzip, x-gzip, identity, *;q=0\r\n"); appendToMessage("Referer: http://localhost:75/HeloThere?edit=\r\n"); appendToMessage("Connection: Keep-Alive, TE\r\n"); appendToMessage("TE: deflate, gzip, chunked, identity, trailers\r\n"); appendToMessage("Content-type: application/x-www-form-urlencoded\r\n"); appendToMessage("Content-length: 67\r\n"); appendToMessage("\r\n"); appendToMessage("edittime=1046584670887&Edit=on&Search=on&Test=on&Suite=on&content=abc"); parseMessage(); assertTrue(request.hasInput("edittime")); assertTrue(request.hasInput("Edit")); assertTrue(request.hasInput("Search")); assertTrue(request.hasInput("Test")); assertTrue(request.hasInput("Suite")); assertTrue(request.hasInput("content")); } @Test public void testBigPosts() throws Exception { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < 10; i++) { for (int j = 0; j < 1000; j++) buffer.append(i); } String prefix = "edittime=12345&content="; appendToMessage("POST /HelloThere HTTP/1.1\r\n"); appendToMessage(String.format("Content-length: %d\r\n", prefix.length()+buffer.length())); appendToMessage("\r\n"); appendToMessage(prefix); appendToMessage(buffer); parseMessage(); String expected = buffer.toString(); String actual = request.getInput("content"); assertEquals(expected.length(), actual.length()); assertEquals(expected, actual); } @Test public void testMultiPartForms() throws Exception { String content = "----bob\r\n" + "Content-Disposition: form-data; name=\"key1\"\r\n" + "\r\n" + "value1\r\n" + "----bob\r\n" + "Content-Disposition: form-data; name=\"key3\"\r\n" + "\r\n" + "some\r\nmulti-line\r\nvalue\r\n\r\n" + "----bob\r\n" + "Content-Disposition: form-data; name=\"key2\"\r\n" + "\r\n" + "value2\r\n" + "----bob\r\n" + "Content-Disposition: form-data; name=\"key4\"\r\n" + "\r\n" + "\r\n" + "----bob--\r\n"; appendToMessage("GET /request-uri HTTP/1.1\r\n"); appendToMessage("Content-Length: " + content.length() + "\r\n"); appendToMessage("Content-Type: multipart/form-data; boundary=--bob\r\n"); appendToMessage("\r\n"); appendToMessage(content); parseMessage(); if (exception != null) { throw exception; } checkInputs(); assertEquals(true, request.hasInput("key3")); assertEquals("some\r\nmulti-line\r\nvalue\r\n", request.getInput("key3")); assertEquals(true, request.hasInput("key4")); assertEquals("", request.getInput("key4")); } @Test public void testUploadingFile() throws Exception { String content = "----bob\r\n" + "Content-Disposition: form-data; name=\"file1\"; filename=\"mike dile.txt\"\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "file contents\r\n" + "----bob--\r\n"; appendToMessage("GET /request-uri HTTP/1.1\r\n"); appendToMessage("Content-Length: " + content.length() + "\r\n"); appendToMessage("Content-Type: multipart/form-data; boundary=--bob\r\n"); appendToMessage("\r\n"); appendToMessage(content); parseMessage(); testUploadedFile("file1", "mike dile.txt", "text/plain", "file contents"); } @Test public void testUploadingTwoFiles() throws Exception { String content = "-----------------------------7d32df3a80058\r\n" + "Content-Disposition: form-data; name=\"file\"; filename=\"C:\\test.txt\"\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "test\r\n" + "-----------------------------7d32df3a80058\r\n" + "Content-Disposition: form-data; name=\"file2\"; filename=\"C:\\test2.txt\"\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "test2\r\n" + "-----------------------------7d32df3a80058--\r\n"; appendToMessage("GET /request-uri HTTP/1.1\r\n"); appendToMessage("Content-Length: " + content.length() + "\r\n"); appendToMessage("Content-Type: multipart/form-data; boundary=---------------------------7d32df3a80058\r\n"); appendToMessage("\r\n"); appendToMessage(content); parseMessage(); testUploadedFile("file", "C:\\test.txt", "text/plain", "test"); testUploadedFile("file2", "C:\\test2.txt", "text/plain", "test2"); } private void testUploadedFile(String name, String filename, String contentType, String content) throws Exception { UploadedFile file = request.getUploadedFile(name); assertNotNull(file); assertEquals(filename, file.getName()); assertEquals(contentType, file.getType()); assertEquals(content, FileUtil.getFileContent(file.getFile())); } @Test public void testUploadingBinaryFile() throws Exception { appendToMessage("GET /request-uri HTTP/1.1\r\n"); appendToMessage("Content-Length: " + (83) + "\r\n"); appendToMessage("Content-Type: multipart/form-data; boundary=--bob\r\n"); appendToMessage("\r\n"); appendToMessage("----bob\r\n"); appendToMessage("Content-Disposition: name=\"n\"; filename=\"f\"\r\n"); appendToMessage("\r\n"); appendToMessage(new byte[]{(byte) 0x9D, (byte) 0x90, (byte) 0x81}); appendToMessage("file contents"); appendToMessage("\r\n"); appendToMessage("----bob--"); parseMessage(); UploadedFile file = request.getUploadedFile("n"); assertNotNull(file); byte[] contents = FileUtil.getFileBytes(file.getFile()); assertEquals((byte) 0x9D, contents[0]); assertEquals((byte) 0x90, contents[1]); assertEquals((byte) 0x81, contents[2]); assertEquals("file contents", new String(contents, 3, contents.length - 3)); } @Test public void testCanGetCredentials() throws Exception { appendToMessage("GET /abc?something HTTP/1.1\r\n"); appendToMessage("Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n"); appendToMessage("\r\n"); parseMessage(); request.getCredentials(); assertEquals("Aladdin", request.getAuthorizationUsername()); assertEquals("open sesame", request.getAuthorizationPassword()); } @Test public void testDoenstChokeOnMissingPassword() throws Exception { appendToMessage("GET /abc?something HTTP/1.1\r\n"); appendToMessage("Authorization: Basic " + Base64.encode("Aladin") + "\r\n"); appendToMessage("\r\n"); parseMessage(); request.getCredentials(); } @Test public void testGetUserpass() throws Exception { assertEquals("Aladdin:open sesame", request.getUserpass("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==")); } @Test public void testUnicodeCharacters() throws Exception { appendToMessage("GET /?key=%EB%AA%80%EB%AA%81%EB%AA%82%EB%AA%83 HTTP/1.1\r\n\r\n"); parseMessage(); assertEquals("\uba80\uba81\uba82\uba83", request.getInput("key")); } @Test public void testParsingProgress() throws Exception { appendToMessage("GET /something HTTP/1.1\r\n"); appendToMessage("Content-Length: 23\r\n"); appendToMessage("\r\n"); appendToMessage("This is "); appendToMessage("the Entity Body"); parseMessage(); assertEquals(70, request.numberOfBytesParsed()); } @Test public void testMultipleRequests() throws Exception { appendToMessage("GET /resource?key1=value1&key1=value2 HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertEquals(true, request.hasInput("key1")); assertEquals("value1,value2", request.getInput("key1")); } @Test public void testRequestWithSameValue() throws Exception { appendToMessage("GET /resource?key1=value1&key1=value1 HTTP/1.1\r\n"); appendToMessage("\r\n"); parseMessage(); assertEquals(true, request.hasInput("key1")); assertEquals("value1", request.getInput("key1")); } }