| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.coyote.ajp; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.Socket; |
| import java.util.Locale; |
| |
| import javax.net.SocketFactory; |
| |
| /** |
| * AJP client that is not (yet) a full AJP client implementation as it just |
| * provides the functionality required for the unit tests. The client uses |
| * blocking IO throughout. |
| */ |
| public class SimpleAjpClient { |
| |
| private static final int DEFAULT_AJP_PACKET_SIZE = 8192; |
| private static final byte[] AJP_CPING; |
| |
| static { |
| TesterAjpMessage ajpCping = new TesterAjpMessage(16); |
| ajpCping.reset(); |
| ajpCping.appendByte(Constants.JK_AJP13_CPING_REQUEST); |
| ajpCping.end(); |
| AJP_CPING = new byte[ajpCping.getLen()]; |
| System.arraycopy(ajpCping.getBuffer(), 0, AJP_CPING, 0, |
| ajpCping.getLen()); |
| } |
| |
| private final int packetSize; |
| 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 SimpleAjpClient() { |
| this(DEFAULT_AJP_PACKET_SIZE); |
| } |
| |
| public SimpleAjpClient(int packetSize) { |
| this.packetSize = packetSize; |
| } |
| |
| public void setPort(int port) { |
| this.port = port; |
| } |
| |
| public int getPort() { |
| return port; |
| } |
| |
| public void setMethod(String method) { |
| method = method.toUpperCase(Locale.ENGLISH); |
| 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); |
| } |
| |
| public void disconnect() throws IOException { |
| socket.close(); |
| socket = null; |
| } |
| |
| /* |
| * Create a message to request the given URL. |
| */ |
| public TesterAjpMessage createForwardMessage() { |
| |
| TesterAjpMessage message = new TesterAjpMessage(packetSize); |
| message.reset(); |
| |
| // Set the header bytes |
| message.getBuffer()[0] = 0x12; |
| message.getBuffer()[1] = 0x34; |
| |
| // Code 2 for forward request |
| message.appendByte(Constants.JK_AJP13_FORWARD_REQUEST); |
| |
| // HTTP method, GET = 2 |
| message.appendByte(method); |
| |
| // Protocol |
| message.appendString(protocol); |
| |
| // Request URI |
| message.appendString(uri); |
| |
| // Client address |
| message.appendString(remoteAddr); |
| |
| // Client host |
| message.appendString(remoteHost); |
| |
| // Server name |
| message.appendString(serverName); |
| |
| // Server port |
| message.appendInt(serverPort); |
| |
| // Is ssl |
| message.appendByte(ssl ? 0x01 : 0x00); |
| |
| return message; |
| } |
| |
| public TesterAjpMessage createBodyMessage(byte[] data) { |
| |
| TesterAjpMessage message = new TesterAjpMessage(packetSize); |
| message.reset(); |
| |
| // Set the header bytes |
| message.getBuffer()[0] = 0x12; |
| message.getBuffer()[1] = 0x34; |
| |
| message.appendBytes(data, 0, data.length); |
| message.end(); |
| |
| return message; |
| } |
| |
| |
| /* |
| * Sends an TesterAjpMessage to the server and returns the response message. |
| */ |
| public TesterAjpMessage sendMessage(TesterAjpMessage headers) |
| throws IOException { |
| return sendMessage(headers, null); |
| } |
| |
| public TesterAjpMessage sendMessage(TesterAjpMessage headers, |
| TesterAjpMessage body) throws IOException { |
| // Send the headers |
| socket.getOutputStream().write( |
| headers.getBuffer(), 0, headers.getLen()); |
| if (body != null) { |
| // Send the body of present |
| socket.getOutputStream().write( |
| body.getBuffer(), 0, body.getLen()); |
| } |
| // Read the response |
| return readMessage(); |
| } |
| |
| /* |
| * Tests the connection to the server and returns the CPONG response. |
| */ |
| public TesterAjpMessage cping() throws IOException { |
| // Send the ping message |
| socket.getOutputStream().write(AJP_CPING); |
| // Read the response |
| return readMessage(); |
| } |
| |
| /* |
| * Reads a message from the server. |
| */ |
| public TesterAjpMessage readMessage() throws IOException { |
| |
| InputStream is = socket.getInputStream(); |
| |
| TesterAjpMessage message = new TesterAjpMessage(packetSize); |
| |
| byte[] buf = message.getBuffer(); |
| int headerLength = message.getHeaderLength(); |
| |
| read(is, buf, 0, headerLength); |
| |
| int messageLength = message.processHeader(false); |
| if (messageLength < 0) { |
| throw new IOException("Invalid AJP message length"); |
| } else if (messageLength == 0) { |
| return message; |
| } else { |
| if (messageLength > buf.length) { |
| throw new IllegalArgumentException("Message too long [" + |
| Integer.valueOf(messageLength) + |
| "] for buffer length [" + |
| Integer.valueOf(buf.length) + "]"); |
| } |
| read(is, buf, headerLength, messageLength); |
| return message; |
| } |
| } |
| |
| protected boolean read(InputStream is, byte[] buf, int pos, int n) |
| throws IOException { |
| |
| int read = 0; |
| int res = 0; |
| while (read < n) { |
| res = is.read(buf, read + pos, n - read); |
| if (res > 0) { |
| read += res; |
| } else { |
| throw new IOException("Read failed"); |
| } |
| } |
| return true; |
| } |
| } |