Author: rjung Date: Tue Dec 16 10:58:46 2014 New Revision: 1645908 URL: http://svn.apache.org/r1645908 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. Backport of r1645488 from trunk. Modified: tomcat/tc8.0.x/trunk/ (props changed) tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/SimpleAjpClient.java tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TesterAjpMessage.java tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml Propchange: tomcat/tc8.0.x/trunk/ ------------------------------------------------------------------------------ --- svn:mergeinfo (original) +++ svn:mergeinfo Tue Dec 16 10:58:46 2014 @@ -1 +1 @@ -/tomcat/trunk:1636524,1637156,1637176,1637188,1637331,1637684,1637695,1638720-1638725,1639653,1640010,1640083-1640084,1640088,1640275,1640322,1640347,1640361,1640365,1640403,1640410,1640652,1640655-1640658,1640688,1640700-1640883,1640903,1640976,1640978,1641000,1641026,1641038-1641039,1641051-1641052,1641058,1641064,1641300,1641369,1641374,1641380,1641486,1641634,1641656-1641692,1641704,1641707-1641718,1641720-1641722,1641735,1641981,1642233,1642280,1642554,1642564,1642595,1642606,1642668,1642679,1642697,1642699,1642766,1643002,1643045,1643054-1643055,1643066,1643121,1643128,1643206,1643209-1643210,1643216,1643249,1643270,1643283,1643309-1643310,1643323,1643365-1643366,1643370-1643371,1643465,1643474,1643536,1643570,1643634,1643649,1643651,1643654,1643675,1643731,1643733-1643734,1643761,1643766,1643814,1643937,1643963,1644017,1644169,1644201-1644203,1644321,1644323,1644516,1644523,1644529,1644535,1644730,1644768,1644784-1644785,1644790,1644793,1644815,1644884,1644886,1644890,1644892 ,1644910,1644924,1644929-1644930,1644935,1644989,1645011,1645247,1645355,1645357-1645358,1645455,1645465,1645469,1645471,1645473,1645475,1645486-1645487,1645626,1645641,1645685 +/tomcat/trunk:1636524,1637156,1637176,1637188,1637331,1637684,1637695,1638720-1638725,1639653,1640010,1640083-1640084,1640088,1640275,1640322,1640347,1640361,1640365,1640403,1640410,1640652,1640655-1640658,1640688,1640700-1640883,1640903,1640976,1640978,1641000,1641026,1641038-1641039,1641051-1641052,1641058,1641064,1641300,1641369,1641374,1641380,1641486,1641634,1641656-1641692,1641704,1641707-1641718,1641720-1641722,1641735,1641981,1642233,1642280,1642554,1642564,1642595,1642606,1642668,1642679,1642697,1642699,1642766,1643002,1643045,1643054-1643055,1643066,1643121,1643128,1643206,1643209-1643210,1643216,1643249,1643270,1643283,1643309-1643310,1643323,1643365-1643366,1643370-1643371,1643465,1643474,1643536,1643570,1643634,1643649,1643651,1643654,1643675,1643731,1643733-1643734,1643761,1643766,1643814,1643937,1643963,1644017,1644169,1644201-1644203,1644321,1644323,1644516,1644523,1644529,1644535,1644730,1644768,1644784-1644785,1644790,1644793,1644815,1644884,1644886,1644890,1644892 ,1644910,1644924,1644929-1644930,1644935,1644989,1645011,1645247,1645355,1645357-1645358,1645455,1645465,1645469,1645471,1645473,1645475,1645486-1645488,1645626,1645641,1645685 Modified: tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/SimpleAjpClient.java URL: http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/SimpleAjpClient.java?rev=1645908&r1=1645907&r2=1645908&view=diff ============================================================================== --- tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/SimpleAjpClient.java (original) +++ tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/SimpleAjpClient.java Tue Dec 16 10:58:46 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/tc8.0.x/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java URL: http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java?rev=1645908&r1=1645907&r2=1645908&view=diff ============================================================================== --- tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java (original) +++ tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java Tue Dec 16 10:58:46 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; @@ -62,6 +64,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(); @@ -82,7 +465,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(); @@ -90,7 +474,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); @@ -107,18 +491,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); @@ -128,8 +513,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"); @@ -143,7 +529,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(); @@ -184,14 +570,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 @@ -203,25 +589,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(); @@ -241,7 +627,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(); @@ -249,7 +636,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); @@ -279,7 +666,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]); @@ -296,8 +683,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(); @@ -311,11 +698,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]); @@ -328,8 +714,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/tc8.0.x/trunk/test/org/apache/coyote/ajp/TesterAjpMessage.java URL: http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TesterAjpMessage.java?rev=1645908&r1=1645907&r2=1645908&view=diff ============================================================================== --- tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TesterAjpMessage.java (original) +++ tomcat/tc8.0.x/trunk/test/org/apache/coyote/ajp/TesterAjpMessage.java Tue Dec 16 10:58:46 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); + } } } Modified: tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml?rev=1645908&r1=1645907&r2=1645908&view=diff ============================================================================== --- tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc8.0.x/trunk/webapps/docs/changelog.xml Tue Dec 16 10:58:46 2014 @@ -187,6 +187,9 @@ Add RequestDescriptor class to unit tests. (rjung) Adjust TestRewriteValve to use RequestDescriptor. </add> + <update> + Add more AJP unit tests. (rjung) + </update> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org