This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,426 @@
/*
* 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();
read(is, buf, 0, Constants.H_SIZE);
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, Constants.H_SIZE, 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;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,194 @@
/*
* 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.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* Extends {@link AjpMessage} to provide additional methods for reading from the
* message.
* TODO: See if it makes sense for any/all of these methods to be transferred to
* AjpMessage
*/
public class TesterAjpMessage extends AjpMessage {
private final List<Header> headers = new ArrayList<>();
private final List<Attribute> attributes = new ArrayList<>();
public TesterAjpMessage(int packetSize) {
super(packetSize);
}
public byte readByte() {
return buf[pos++];
}
public int readInt() {
int val = (buf[pos++] & 0xFF ) << 8;
val += buf[pos++] & 0xFF;
return val;
}
public String readString() {
int len = readInt();
return readString(len);
}
public String readString(int len) {
StringBuilder buffer = new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c = (char) buf[pos++];
buffer.append(c);
}
// Read end of string marker
readByte();
return buffer.toString();
}
public String readHeaderName() {
byte b = readByte();
if ((b & 0xFF) == 0xA0) {
// Coded header
return Constants.getResponseHeaderForCode(readByte());
} else {
int len = (b & 0xFF) << 8;
len += getByte() & 0xFF;
return readString(len);
}
}
public void addHeader(int code, String value) {
headers.add(new Header(code, value));
}
public void addHeader(String name, String value) {
headers.add(new Header(name, value));
}
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
appendInt(headers.size());
for (Header header : headers) {
header.append(this);
}
for (Attribute attribute : attributes) {
attribute.append(this);
}
// Terminator
appendByte(0xFF);
len = pos;
int dLen = len - 4;
buf[0] = (byte) 0x12;
buf[1] = (byte) 0x34;
buf[2] = (byte) ((dLen>>>8) & 0xFF);
buf[3] = (byte) (dLen & 0xFF);
}
@Override
public void reset() {
super.reset();
headers.clear();
}
public void appendString(String string) {
byte[] bytes = string.getBytes(StandardCharsets.ISO_8859_1);
appendBytes(bytes, 0, bytes.length);
}
private static class Header {
private final int code;
private final String name;
private final String value;
public Header(int code, String value) {
this.code = code;
this.name = null;
this.value = value;
}
public Header(String name, String value) {
this.code = 0;
this.name = name;
this.value = value;
}
public void append(TesterAjpMessage message) {
if (code == 0) {
message.appendString(name);
} else {
message.appendInt(code);
}
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);
}
}
}