Author: rjung Date: Sun Dec 14 18:32:42 2014 New Revision: 1645488 URL: http://svn.apache.org/r1645488 Log: Add more thorough tests for AJP.
Unfortunately request attributes as sent by mod_jk JkEnvVars can not be tested, because request.getAttributeNames() does not return the names of Coyote request attributes. Only getAttribute(String) checks Coyote request attributes. Modified: tomcat/trunk/test/org/apache/coyote/ajp/SimpleAjpClient.java tomcat/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java tomcat/trunk/test/org/apache/coyote/ajp/TesterAjpMessage.java Modified: tomcat/trunk/test/org/apache/coyote/ajp/SimpleAjpClient.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/ajp/SimpleAjpClient.java?rev=1645488&r1=1645487&r2=1645488&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/coyote/ajp/SimpleAjpClient.java (original) +++ tomcat/trunk/test/org/apache/coyote/ajp/SimpleAjpClient.java Sun Dec 14 18:32:42 2014 @@ -44,6 +44,15 @@ public class SimpleAjpClient { private String host = "localhost"; private int port = -1; + /* GET == 2 */ + private int method = 2; + private String protocol = "http"; + private String uri = "/"; + private String remoteAddr = "192.168.0.1"; + private String remoteHost = "client.example.com"; + private String serverName = "www.example.com"; + private int serverPort = 80; + private boolean ssl = false; private Socket socket = null; public void setPort(int port) { @@ -54,6 +63,212 @@ public class SimpleAjpClient { return port; } + public void setMethod(String method) { + method = method.toUpperCase(); + switch (method) { + case "OPTIONS": + this.method = 1; + break; + case "GET": + this.method = 2; + break; + case "HEAD": + this.method = 3; + break; + case "POST": + this.method = 4; + break; + case "PUT": + this.method = 5; + break; + case "DELETE": + this.method = 6; + break; + case "TRACE": + this.method = 7; + break; + case "PROPFIND": + this.method = 8; + break; + case "PROPPATCH": + this.method = 9; + break; + case "MKCOL": + this.method = 10; + break; + case "COPY": + this.method = 11; + break; + case "MOVE": + this.method = 12; + break; + case "LOCK": + this.method = 13; + break; + case "UNLOCK": + this.method = 14; + break; + case "ACL": + this.method = 15; + break; + case "REPORT": + this.method = 16; + break; + case "VERSION-CONTROL": + this.method = 17; + break; + case "CHECKIN": + this.method = 18; + break; + case "CHECKOUT": + this.method = 19; + break; + case "UNCHECKOUT": + this.method = 20; + break; + case "SEARCH": + this.method = 21; + break; + case "MKWORKSPACE": + this.method = 22; + break; + case "UPDATE": + this.method = 23; + break; + case "LABEL": + this.method = 24; + break; + case "MERGE": + this.method = 25; + break; + case "BASELINE-CONTROL": + this.method = 26; + break; + case "MKACTIVITY": + this.method = 27; + break; + default: + this.method = 99; + } + } + + public String getMethod() { + switch (method) { + case 1: + return "OPTIONS"; + case 2: + return "GET"; + case 3: + return "HEAD"; + case 4: + return "POST"; + case 5: + return "PUT"; + case 6: + return "DELETE"; + case 7: + return "TRACE"; + case 8: + return "PROPFIND"; + case 9: + return "PROPPATCH"; + case 10: + return "MKCOL"; + case 11: + return "COPY"; + case 12: + return "MOVE"; + case 13: + return "LOCK"; + case 14: + return "UNLOCK"; + case 15: + return "ACL"; + case 16: + return "REPORT"; + case 17: + return "VERSION-CONTROL"; + case 18: + return "CHECKIN"; + case 19: + return "CHECKOUT"; + case 20: + return "UNCHECKOUT"; + case 21: + return "SEARCH"; + case 22: + return "MKWORKSPACE"; + case 23: + return "UPDATE"; + case 24: + return "LABEL"; + case 25: + return "MERGE"; + case 26: + return "BASELINE-CONTROL"; + case 27: + return "MKACTIVITY"; + default: + return "UNKNOWN"; + } + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public String getProtocol() { + return protocol; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getUri() { + return uri; + } + + public void setRemoteAddr(String remoteAddr) { + this.remoteAddr = remoteAddr; + } + + public String getRemoteAddr() { + return remoteAddr; + } + + public void setRemoteHost(String remoteHost) { + this.remoteHost = remoteHost; + } + + public String getRemoteHost() { + return remoteHost; + } + + public void setServerName(String serverName) { + this.serverName = serverName; + } + + public String getServerName() { + return serverName; + } + + public void setServerPort(int serverPort) { + this.serverPort = serverPort; + } + + public int getServerPort() { + return serverPort; + } + + public void setSsl(boolean ssl) { + this.ssl = ssl; + } + + public boolean isSsl() { + return ssl; + } + public void connect() throws IOException { socket = SocketFactory.getDefault().createSocket(host, port); } @@ -66,11 +281,7 @@ public class SimpleAjpClient { /* * Create a message to request the given URL. */ - public TesterAjpMessage createForwardMessage(String url) { - return createForwardMessage(url, 2); - } - - public TesterAjpMessage createForwardMessage(String url, int method) { + public TesterAjpMessage createForwardMessage() { TesterAjpMessage message = new TesterAjpMessage(AJP_PACKET_SIZE); message.reset(); @@ -86,30 +297,29 @@ public class SimpleAjpClient { message.appendByte(method); // Protocol - message.appendString("http"); + message.appendString(protocol); // Request URI - message.appendString(url); + message.appendString(uri); - // Remote address - message.appendString("10.0.0.1"); + // Client address + message.appendString(remoteAddr); - // Remote host - message.appendString("client.dev.local"); + // Client host + message.appendString(remoteHost); // Server name - message.appendString(host); + message.appendString(serverName); - // Port - message.appendInt(port); + // Server port + message.appendInt(serverPort); // Is ssl - message.appendByte(0x00); + message.appendByte(ssl ? 0x01 : 0x00); return message; } - public TesterAjpMessage createBodyMessage(byte[] data) { TesterAjpMessage message = new TesterAjpMessage(AJP_PACKET_SIZE); Modified: tomcat/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java?rev=1645488&r1=1645487&r2=1645488&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java (original) +++ tomcat/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java Sun Dec 14 18:32:42 2014 @@ -20,9 +20,11 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -60,6 +62,387 @@ public class TestAbstractAjpProcessor ex return protocol; } + private void doSnoopTest(RequestDescriptor desc) throws Exception { + + HashMap<String, String> requestInfo = desc.getRequestInfo(); + HashMap<String, String> contextInitParameters = desc.getContextInitParameters(); + HashMap<String, String> contextAttributes = desc.getContextAttributes(); + HashMap<String, String> headers = desc.getHeaders(); + HashMap<String, String> attributes = desc.getAttributes(); + HashMap<String, String> params = desc.getParams(); + + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = tomcat.addContext("", null); + + Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMapping("/", "snoop"); + + SimpleAjpClient ajpClient = new SimpleAjpClient(); + + if (requestInfo.get("REQUEST-QUERY-STRING") != null && + params.size() > 0) { + throw(new IllegalArgumentException("Request setting " + + "'REQUEST-QUERY-STRING' and explicit params not allowed " + + "together")); + } + + String value; + HashMap<String, String> savedRequestInfo = new HashMap<String, String>(); + for (String name: requestInfo.keySet()) { + value = requestInfo.get(name); + switch (name) { + case "REQUEST-METHOD": + ajpClient.setMethod(value); + break; + case "REQUEST-PROTOCOL": + ajpClient.setProtocol(value); + break; + case "REQUEST-URI": + ajpClient.setUri(value); + break; + case "REQUEST-REMOTE-HOST": + /* request.getRemoteHost() will default to + * request.getRemoteAddr() unless enableLookups is set. */ + tomcat.getConnector().setEnableLookups(true); + ajpClient.setRemoteHost(value); + break; + case "REQUEST-REMOTE-ADDR": + ajpClient.setRemoteAddr(value); + break; + case "REQUEST-SERVER-NAME": + ajpClient.setServerName(value); + break; + case "REQUEST-SERVER-PORT": + ajpClient.setServerPort(Integer.valueOf(value)); + break; + case "REQUEST-IS-SECURE": + ajpClient.setSsl(Boolean.parseBoolean(value)); + break; + case "REQUEST-LOCAL-ADDR": + savedRequestInfo.put(name, value); + break; + case "REQUEST-REMOTE-PORT": + savedRequestInfo.put(name, value); + break; + case "REQUEST-REMOTE-USER": + case "REQUEST-ROUTE": + case "REQUEST-SECRET": + case "REQUEST-AUTH-TYPE": + case "REQUEST-QUERY-STRING": + savedRequestInfo.put(name, value); + break; + case "REQUEST-CONTENT-LENGTH": + headers.put("CONTENT-LENGTH", value); + break; + case "REQUEST-CONTENT-TYPE": + headers.put("CONTENT-TYPE", value); + break; + /* Not yet implemented or not (easily) possible to implement */ + case "REQUEST-LOCAL-NAME": //request.getLocalName() + case "REQUEST-LOCAL-PORT": //request.getLocalPort() + case "REQUEST-SCHEME": //request.getScheme() + case "REQUEST-URL": //request.getRequestURL() + case "REQUEST-CONTEXT-PATH": //request.getContextPath() + case "REQUEST-SERVLET-PATH": //request.getServletPath() + case "REQUEST-PATH-INFO": //request.getPathInfo() + case "REQUEST-PATH-TRANSLATED": //request.getPathTranslated() + case "REQUEST-USER-PRINCIPAL": //request.getUserPrincipal() + case "REQUEST-CHARACTER-ENCODING": //request.getCharacterEncoding() + case "REQUEST-LOCALE": //request.getLocale() + case "SESSION-REQUESTED-ID": //request.getRequestedSessionId() + case "SESSION-REQUESTED-ID-COOKIE": //request.isRequestedSessionIdFromCookie() + case "SESSION-REQUESTED-ID-URL": //request.isRequestedSessionIdFromUrl() + case "SESSION-REQUESTED-ID-VALID": //request.isRequestedSessionIdValid() + default: + throw(new IllegalArgumentException("Request setting '" + name + "' not supported")); + } + } + + ServletContext sc = ctx.getServletContext(); + for (String name: contextInitParameters.keySet()) { + sc.setInitParameter(name, contextInitParameters.get(name)); + } + for (String name: contextAttributes.keySet()) { + sc.setAttribute(name, contextAttributes.get(name)); + } + + /* Basic request properties must be set before this call */ + TesterAjpMessage forwardMessage = ajpClient.createForwardMessage(); + + for (String name: savedRequestInfo.keySet()) { + value = savedRequestInfo.get(name); + switch (name) { + case "REQUEST-LOCAL-ADDR": + forwardMessage.addAttribute("AJP_LOCAL_ADDR", value); + break; + case "REQUEST-REMOTE-PORT": + forwardMessage.addAttribute("AJP_REMOTE_PORT", value); + break; + case "REQUEST-REMOTE-USER": + /* request.getRemoteUser() will not trust the AJP + * info if tomcatAuthentication is set. */ + tomcat.getConnector().setProperty("tomcatAuthentication", "false"); + forwardMessage.addAttribute(0x03, value); + break; + case "REQUEST-AUTH-TYPE": + /* request.getAuthType() will not trust the AJP + * info if tomcatAuthentication is set. */ + tomcat.getConnector().setProperty("tomcatAuthentication", "false"); + forwardMessage.addAttribute(0x04, value); + break; + case "REQUEST-QUERY-STRING": + forwardMessage.addAttribute(0x05, value); + break; + case "REQUEST-ROUTE": + forwardMessage.addAttribute(0x06, value); + break; + case "REQUEST-SECRET": + forwardMessage.addAttribute(0x0C, value); + break; + default: + throw(new IllegalArgumentException("Request setting '" + name + "' not supported")); + } + } + + if (params.size() > 0) { + StringBuilder query = new StringBuilder(); + boolean sep = false; + for (String name: params.keySet()) { + if (sep) { + query.append("&"); + } else { + sep = true; + } + query.append(name); + query.append("="); + query.append(params.get(name)); + } + forwardMessage.addAttribute(0x05, query.toString()); + } + + for (String name: headers.keySet()) { + value = headers.get(name); + name = name.toUpperCase(); + switch (name) { + case "ACCEPT": + forwardMessage.addHeader(0xA001, value); + break; + case "ACCEPT-CHARSET": + forwardMessage.addHeader(0xA002, value); + break; + case "ACCEPT-ENCODING": + forwardMessage.addHeader(0xA003, value); + break; + case "ACCEPT-LANGUAGE": + forwardMessage.addHeader(0xA004, value); + break; + case "AUTHORIZATION": + forwardMessage.addHeader(0xA005, value); + break; + case "CONNECTION": + forwardMessage.addHeader(0xA006, value); + break; + case "CONTENT-TYPE": + forwardMessage.addHeader(0xA007, value); + break; + case "CONTENT-LENGTH": + forwardMessage.addHeader(0xA008, value); + break; + case "COOKIE": + forwardMessage.addHeader(0xA009, value); + break; + case "COOKIE2": + forwardMessage.addHeader(0xA00A, value); + break; + case "HOST": + forwardMessage.addHeader(0xA00B, value); + break; + case "PRAGMA": + forwardMessage.addHeader(0xA00C, value); + break; + case "REFERER": + forwardMessage.addHeader(0xA00D, value); + break; + case "USER-AGENT": + forwardMessage.addHeader(0xA00E, value); + break; + default: + forwardMessage.addHeader(name, value); + break; + } + } + for (String name: attributes.keySet()) { + value = attributes.get(name); + forwardMessage.addAttribute(name, value); + } + // Complete the message + forwardMessage.end(); + + tomcat.start(); + ajpClient.setPort(getPort()); + ajpClient.connect(); + + // Expect 3 packets: headers, body, end + TesterAjpMessage responseHeaders = ajpClient.sendMessage(forwardMessage); + validateResponseHeaders(responseHeaders, 200, "OK"); + + String body = extractResponseBody(ajpClient.readMessage()); + RequestDescriptor result = SnoopResult.parse(body); + + /* AJP attributes result in Coyote Request attributes, which are + * not listed by request.getAttributeNames(), so SnoopServlet + * does not see them. Delete attributes before result comparison. */ + desc.getAttributes().clear(); + + result.compare(desc); + + validateResponseEnd(ajpClient.readMessage(), true); + } + + @Test + public void testServerName() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-SERVER-NAME", "MYSERVER"); + desc.putRequestInfo("REQUEST-URI", "/testServerName"); + doSnoopTest(desc); + } + + @Test + public void testServerPort() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-SERVER-PORT", "8888"); + desc.putRequestInfo("REQUEST-URI", "/testServerPort"); + doSnoopTest(desc); + } + + @Test + public void testLocalAddr() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-LOCAL-ADDR", "10.3.2.1"); + desc.putRequestInfo("REQUEST-URI", "/testLocalAddr"); + doSnoopTest(desc); + } + + @Test + public void testRemoteHost() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-REMOTE-HOST", "MYCLIENT"); + desc.putRequestInfo("REQUEST-URI", "/testRemoteHost"); + doSnoopTest(desc); + } + + @Test + public void testRemoteAddr() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-REMOTE-ADDR", "10.1.2.3"); + desc.putRequestInfo("REQUEST-URI", "/testRemoteAddr"); + doSnoopTest(desc); + } + + @Test + public void testRemotePort() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-REMOTE-PORT", "34567"); + desc.putRequestInfo("REQUEST-URI", "/testRemotePort"); + doSnoopTest(desc); + } + + @Test + public void testMethod() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-METHOD", "LOCK"); + desc.putRequestInfo("REQUEST-URI", "/testMethod"); + doSnoopTest(desc); + } + + @Test + public void testUri() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-URI", "/a/b/c"); + doSnoopTest(desc); + } + + @Test + public void testProtocol() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-PROTOCOL", "HTTP/1.x"); + desc.putRequestInfo("REQUEST-URI", "/testProtocol"); + doSnoopTest(desc); + } + + @Test + public void testSecure() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-IS-SECURE", "true"); + desc.putRequestInfo("REQUEST-URI", "/testSecure"); + doSnoopTest(desc); + } + + @Test + public void testQueryString() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-QUERY-STRING", "p1=1&p2=12&p3=123"); + desc.putRequestInfo("REQUEST-URI", "/testQueryString"); + doSnoopTest(desc); + } + + @Test + public void testRemoteUser() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-REMOTE-USER", "MYUSER"); + desc.putRequestInfo("REQUEST-URI", "/testRemoteUser"); + doSnoopTest(desc); + } + + @Test + public void testAuthType() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-AUTH-TYPE", "MyAuth"); + desc.putRequestInfo("REQUEST-URI", "/testAuthType"); + doSnoopTest(desc); + } + + @Test + public void testOneHeader() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putHeader("MYHEADER", "MYHEADER-VALUE"); + desc.putRequestInfo("REQUEST-URI", "/testOneHeader"); + doSnoopTest(desc); + } + + @Test + public void testOneAttribute() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putAttribute("MYATTRIBUTE", "MYATTRIBUTE-VALUE"); + desc.putRequestInfo("REQUEST-URI", "/testOneAttribute"); + doSnoopTest(desc); + } + + @Test + public void testMulti() throws Exception { + RequestDescriptor desc = new RequestDescriptor(); + desc.putRequestInfo("REQUEST-SERVER-NAME", "MYSERVER"); + desc.putRequestInfo("REQUEST-SERVER-PORT", "8888"); + desc.putRequestInfo("REQUEST-LOCAL-ADDR", "10.3.2.1"); + desc.putRequestInfo("REQUEST-REMOTE-HOST", "MYCLIENT"); + desc.putRequestInfo("REQUEST-REMOTE-ADDR", "10.1.2.3"); + desc.putRequestInfo("REQUEST-REMOTE-PORT", "34567"); + desc.putRequestInfo("REQUEST-METHOD", "LOCK"); + desc.putRequestInfo("REQUEST-URI", "/a/b/c"); + desc.putRequestInfo("REQUEST-PROTOCOL", "HTTP/1.x"); + desc.putRequestInfo("REQUEST-IS-SECURE", "true"); + desc.putRequestInfo("REQUEST-QUERY-STRING", "p1=1&p2=12&p3=123"); + desc.putRequestInfo("REQUEST-REMOTE-USER", "MYUSER"); + desc.putRequestInfo("REQUEST-AUTH-TYPE", "MyAuth"); + desc.putHeader("MYHEADER1", "MYHEADER1-VALUE"); + desc.putHeader("MYHEADER2", "MYHEADER2-VALUE"); + desc.putAttribute("MYATTRIBUTE1", "MYATTRIBUTE-VALUE1"); + desc.putAttribute("MYATTRIBUTE2", "MYATTRIBUTE-VALUE2"); + doSnoopTest(desc); + } + @Test public void testKeepAlive() throws Exception { Tomcat tomcat = getTomcatInstance(); @@ -80,7 +463,8 @@ public class TestAbstractAjpProcessor ex validateCpong(ajpClient.cping()); - TesterAjpMessage forwardMessage = ajpClient.createForwardMessage("/"); + TesterAjpMessage forwardMessage = ajpClient.createForwardMessage(); + forwardMessage.addHeader("X-DUMMY-HEADER", "IGNORE"); // Complete the message - no extra headers required. forwardMessage.end(); @@ -88,7 +472,7 @@ public class TestAbstractAjpProcessor ex for (int i = 0; i < 2; i++) { TesterAjpMessage responseHeaders = ajpClient.sendMessage(forwardMessage); // Expect 3 packets: headers, body, end - validateResponseHeaders(responseHeaders, 200); + validateResponseHeaders(responseHeaders, 200, "OK"); TesterAjpMessage responseBody = ajpClient.readMessage(); validateResponseBody(responseBody, HelloWorldServlet.RESPONSE_TEXT); validateResponseEnd(ajpClient.readMessage(), true); @@ -105,18 +489,19 @@ public class TestAbstractAjpProcessor ex @Test public void testPost() throws Exception { - doTestPost(false, HttpServletResponse.SC_OK); + doTestPost(false, HttpServletResponse.SC_OK, "OK"); } @Test public void testPostMultipleContentLength() throws Exception { // Multiple content lengths - doTestPost(true, HttpServletResponse.SC_BAD_REQUEST); + doTestPost(true, HttpServletResponse.SC_BAD_REQUEST, "Bad Request"); } - public void doTestPost(boolean multipleCL, int expectedStatus) throws Exception { + public void doTestPost(boolean multipleCL, int expectedStatus, + String expectedMessage) throws Exception { getTomcatInstanceTestWebapp(false, true); @@ -126,8 +511,9 @@ public class TestAbstractAjpProcessor ex validateCpong(ajpClient.cping()); - TesterAjpMessage forwardMessage = - ajpClient.createForwardMessage("/test/echo-params.jsp", 4); + ajpClient.setUri("/test/echo-params.jsp"); + ajpClient.setMethod("POST"); + TesterAjpMessage forwardMessage = ajpClient.createForwardMessage(); forwardMessage.addHeader(0xA008, "9"); if (multipleCL) { forwardMessage.addHeader(0xA008, "99"); @@ -141,7 +527,7 @@ public class TestAbstractAjpProcessor ex TesterAjpMessage responseHeaders = ajpClient.sendMessage(forwardMessage, bodyMessage); - validateResponseHeaders(responseHeaders, expectedStatus); + validateResponseHeaders(responseHeaders, expectedStatus, expectedMessage); if (expectedStatus == HttpServletResponse.SC_OK) { // Expect 3 messages: headers, body, end for a valid request TesterAjpMessage responseBody = ajpClient.readMessage(); @@ -182,14 +568,14 @@ public class TestAbstractAjpProcessor ex validateCpong(ajpClient.cping()); - TesterAjpMessage forwardMessage = ajpClient.createForwardMessage("/"); + TesterAjpMessage forwardMessage = ajpClient.createForwardMessage(); forwardMessage.end(); TesterAjpMessage responseHeaders = ajpClient.sendMessage(forwardMessage, null); // Expect 2 messages: headers, end - validateResponseHeaders(responseHeaders, 304); + validateResponseHeaders(responseHeaders, 304, "Not Modified"); validateResponseEnd(ajpClient.readMessage(), true); // Double check the connection is still open @@ -201,25 +587,25 @@ public class TestAbstractAjpProcessor ex @Test public void testZeroLengthRequestBodyGetA() throws Exception { - doTestZeroLengthRequestBody(2, true); + doTestZeroLengthRequestBody("GET", true); } @Test public void testZeroLengthRequestBodyGetB() throws Exception { - doTestZeroLengthRequestBody(2, false); + doTestZeroLengthRequestBody("GET", false); } @Test public void testZeroLengthRequestBodyPostA() throws Exception { - doTestZeroLengthRequestBody(4, true); + doTestZeroLengthRequestBody("POST", true); } @Test public void testZeroLengthRequestBodyPostB() throws Exception { - doTestZeroLengthRequestBody(4, false); + doTestZeroLengthRequestBody("POST", false); } - private void doTestZeroLengthRequestBody(int method, boolean callAvailable) + private void doTestZeroLengthRequestBody(String method, boolean callAvailable) throws Exception { Tomcat tomcat = getTomcatInstance(); @@ -239,7 +625,8 @@ public class TestAbstractAjpProcessor ex validateCpong(ajpClient.cping()); - TesterAjpMessage forwardMessage = ajpClient.createForwardMessage("/", method); + ajpClient.setMethod(method); + TesterAjpMessage forwardMessage = ajpClient.createForwardMessage(); forwardMessage.addHeader(0xA008, "0"); forwardMessage.end(); @@ -247,7 +634,7 @@ public class TestAbstractAjpProcessor ex ajpClient.sendMessage(forwardMessage, null); // Expect 3 messages: headers, body, end - validateResponseHeaders(responseHeaders, 200); + validateResponseHeaders(responseHeaders, 200, "OK"); validateResponseBody(ajpClient.readMessage(), "Request Body length in bytes: 0"); validateResponseEnd(ajpClient.readMessage(), true); @@ -277,7 +664,7 @@ public class TestAbstractAjpProcessor ex * ignored. */ private void validateResponseHeaders(TesterAjpMessage message, - int expectedStatus) throws Exception { + int expectedStatus, String expectedMessage) throws Exception { // First two bytes should always be AB Assert.assertEquals((byte) 'A', message.buf[0]); Assert.assertEquals((byte) 'B', message.buf[1]); @@ -294,8 +681,8 @@ public class TestAbstractAjpProcessor ex // Check status Assert.assertEquals(expectedStatus, message.readInt()); - // Read the status message - message.readString(); + // Check the reason phrase + Assert.assertEquals(expectedMessage, message.readString()); // Get the number of headers int headerCount = message.readInt(); @@ -309,11 +696,10 @@ public class TestAbstractAjpProcessor ex } /** - * Validates that the response message is valid and contains the expected - * content. + * Extract the content from a response message. */ - private void validateResponseBody(TesterAjpMessage message, - String expectedBody) throws Exception { + private String extractResponseBody(TesterAjpMessage message) + throws Exception { Assert.assertEquals((byte) 'A', message.buf[0]); Assert.assertEquals((byte) 'B', message.buf[1]); @@ -326,8 +712,17 @@ public class TestAbstractAjpProcessor ex int len = message.readInt(); Assert.assertTrue(len > 0); - String body = message.readString(len); + return message.readString(len); + } + + /** + * Validates that the response message is valid and contains the expected + * content. + */ + private void validateResponseBody(TesterAjpMessage message, + String expectedBody) throws Exception { + String body = extractResponseBody(message); Assert.assertTrue(body.contains(expectedBody)); } Modified: tomcat/trunk/test/org/apache/coyote/ajp/TesterAjpMessage.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/ajp/TesterAjpMessage.java?rev=1645488&r1=1645487&r2=1645488&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/coyote/ajp/TesterAjpMessage.java (original) +++ tomcat/trunk/test/org/apache/coyote/ajp/TesterAjpMessage.java Sun Dec 14 18:32:42 2014 @@ -29,6 +29,7 @@ import java.util.List; public class TesterAjpMessage extends AjpMessage { private final List<Header> headers = new ArrayList<>(); + private final List<Attribute> attributes = new ArrayList<>(); public TesterAjpMessage(int packetSize) { @@ -86,6 +87,16 @@ public class TesterAjpMessage extends Aj } + public void addAttribute(int code, String value) { + attributes.add(new Attribute(code, value)); + } + + + public void addAttribute(String name, String value) { + attributes.add(new Attribute(name, value)); + } + + @Override public void end() { // Add the header count @@ -95,6 +106,10 @@ public class TesterAjpMessage extends Aj header.append(this); } + for (Attribute attribute : attributes) { + attribute.append(this); + } + // Terminator appendByte(0xFF); @@ -121,7 +136,6 @@ public class TesterAjpMessage extends Aj } - private static class Header { private final int code; private final String name; @@ -147,5 +161,34 @@ public class TesterAjpMessage extends Aj } message.appendString(value); } + } + + + private static class Attribute { + private final int code; + private final String name; + private final String value; + + public Attribute(int code, String value) { + this.code = code; + this.name = null; + this.value = value; + } + + public Attribute(String name, String value) { + this.code = 0; + this.name = name; + this.value = value; + } + + public void append(TesterAjpMessage message) { + if (code == 0) { + message.appendByte(0x0A); + message.appendString(name); + } else { + message.appendByte(code); + } + message.appendString(value); + } } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org