Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 package org.apache.xmlrpc.webserver;
00020
00021 import java.io.BufferedInputStream;
00022 import java.io.BufferedOutputStream;
00023 import java.io.ByteArrayOutputStream;
00024 import java.io.IOException;
00025 import java.io.InputStream;
00026 import java.io.OutputStream;
00027 import java.io.UnsupportedEncodingException;
00028 import java.net.Socket;
00029 import java.net.SocketException;
00030 import java.util.Iterator;
00031 import java.util.Map;
00032 import java.util.StringTokenizer;
00033
00034 import org.apache.xmlrpc.common.ServerStreamConnection;
00035 import org.apache.xmlrpc.common.XmlRpcHttpRequestConfig;
00036 import org.apache.xmlrpc.common.XmlRpcNotAuthorizedException;
00037 import org.apache.xmlrpc.server.XmlRpcHttpServerConfig;
00038 import org.apache.xmlrpc.server.XmlRpcStreamServer;
00039 import org.apache.xmlrpc.util.HttpUtil;
00040 import org.apache.xmlrpc.util.LimitedInputStream;
00041 import org.apache.xmlrpc.util.ThreadPool;
00042
00043
00044
00049 public class Connection implements ThreadPool.InterruptableTask, ServerStreamConnection {
00050 private static final String US_ASCII = "US-ASCII";
00051 private static final byte[] ctype = toHTTPBytes("Content-Type: text/xml\r\n");
00052 private static final byte[] clength = toHTTPBytes("Content-Length: ");
00053 private static final byte[] newline = toHTTPBytes("\r\n");
00054 private static final byte[] doubleNewline = toHTTPBytes("\r\n\r\n");
00055 private static final byte[] conkeep = toHTTPBytes("Connection: Keep-Alive\r\n");
00056 private static final byte[] conclose = toHTTPBytes("Connection: close\r\n");
00057 private static final byte[] ok = toHTTPBytes(" 200 OK\r\n");
00058 private static final byte[] serverName = toHTTPBytes("Server: Apache XML-RPC 1.0\r\n");
00059 private static final byte[] wwwAuthenticate = toHTTPBytes("WWW-Authenticate: Basic realm=XML-RPC\r\n");
00060
00061 private static abstract class RequestException extends IOException {
00062 private static final long serialVersionUID = 2113732921468653309L;
00063 private final RequestData requestData;
00064
00065 RequestException(RequestData pData, String pMessage) {
00066 super(pMessage);
00067 requestData = pData;
00068 }
00069 RequestData getRequestData() { return requestData; }
00070 }
00071
00072 private static class BadEncodingException extends RequestException {
00073 private static final long serialVersionUID = -2674424938251521248L;
00074 BadEncodingException(RequestData pData, String pTransferEncoding) {
00075 super(pData, pTransferEncoding);
00076 }
00077 }
00078
00079 private static class BadRequestException extends RequestException {
00080 private static final long serialVersionUID = 3257848779234554934L;
00081 BadRequestException(RequestData pData, String pTransferEncoding) {
00082 super(pData, pTransferEncoding);
00083 }
00084 }
00085
00089 private static final byte[] toHTTPBytes(String text) {
00090 try {
00091 return text.getBytes(US_ASCII);
00092 } catch (UnsupportedEncodingException e) {
00093 throw new Error(e.getMessage() +
00094 ": HTTP requires US-ASCII encoding");
00095 }
00096 }
00097
00098 private final WebServer webServer;
00099 private final Socket socket;
00100 private final InputStream input;
00101 private final OutputStream output;
00102 private final XmlRpcStreamServer server;
00103 private byte[] buffer;
00104 private Map headers;
00105 private RequestData requestData;
00106 private boolean shuttingDown;
00107 private boolean firstByte;
00108
00116 public Connection(WebServer pWebServer, XmlRpcStreamServer pServer, Socket pSocket)
00117 throws IOException {
00118 webServer = pWebServer;
00119 server = pServer;
00120 socket = pSocket;
00121 input = new BufferedInputStream(socket.getInputStream()){
00126 public void close() throws IOException {
00127 }
00128 };
00129 output = new BufferedOutputStream(socket.getOutputStream());
00130 }
00131
00137 private RequestData getRequestConfig() throws IOException {
00138 requestData = new RequestData(this);
00139 if (headers != null) {
00140 headers.clear();
00141 }
00142 firstByte = true;
00143 XmlRpcHttpServerConfig serverConfig = (XmlRpcHttpServerConfig) server.getConfig();
00144 requestData.setBasicEncoding(serverConfig.getBasicEncoding());
00145 requestData.setContentLengthOptional(serverConfig.isContentLengthOptional());
00146 requestData.setEnabledForExtensions(serverConfig.isEnabledForExtensions());
00147 requestData.setEnabledForExceptions(serverConfig.isEnabledForExceptions());
00148
00149
00150 String line = readLine();
00151 if (line == null && firstByte) {
00152 return null;
00153 }
00154
00155 if (line != null && line.length() == 0) {
00156 line = readLine();
00157 if (line == null || line.length() == 0) {
00158 return null;
00159 }
00160 }
00161
00162
00163 StringTokenizer tokens = new StringTokenizer(line);
00164 String method = tokens.nextToken();
00165 if (!"POST".equalsIgnoreCase(method)) {
00166 throw new BadRequestException(requestData, method);
00167 }
00168 requestData.setMethod(method);
00169 tokens.nextToken();
00170 String httpVersion = tokens.nextToken();
00171 requestData.setHttpVersion(httpVersion);
00172 requestData.setKeepAlive(serverConfig.isKeepAliveEnabled()
00173 && WebServer.HTTP_11.equals(httpVersion));
00174 do {
00175 line = readLine();
00176 if (line != null) {
00177 String lineLower = line.toLowerCase();
00178 if (lineLower.startsWith("content-length:")) {
00179 String cLength = line.substring("content-length:".length());
00180 requestData.setContentLength(Integer.parseInt(cLength.trim()));
00181 } else if (lineLower.startsWith("connection:")) {
00182 requestData.setKeepAlive(serverConfig.isKeepAliveEnabled()
00183 && lineLower.indexOf("keep-alive") > -1);
00184 } else if (lineLower.startsWith("authorization:")) {
00185 String credentials = line.substring("authorization:".length());
00186 HttpUtil.parseAuthorization(requestData, credentials);
00187 } else if (lineLower.startsWith("transfer-encoding:")) {
00188 String transferEncoding = line.substring("transfer-encoding:".length());
00189 String nonIdentityEncoding = HttpUtil.getNonIdentityTransferEncoding(transferEncoding);
00190 if (nonIdentityEncoding != null) {
00191 throw new BadEncodingException(requestData, nonIdentityEncoding);
00192 }
00193 }
00194 }
00195 }
00196 while (line != null && line.length() != 0);
00197
00198 return requestData;
00199 }
00200
00201 public void run() {
00202 try {
00203 for (int i = 0; ; i++) {
00204 RequestData data = getRequestConfig();
00205 if (data == null) {
00206 break;
00207 }
00208 server.execute(data, this);
00209 output.flush();
00210 if (!data.isKeepAlive() || !data.isSuccess()) {
00211 break;
00212 }
00213 }
00214 } catch (RequestException e) {
00215 webServer.log(e.getClass().getName() + ": " + e.getMessage());
00216 try {
00217 writeErrorHeader(e.requestData, e, -1);
00218 output.flush();
00219 } catch (IOException e1) {
00220
00221 }
00222 } catch (Throwable t) {
00223 if (!shuttingDown) {
00224 webServer.log(t);
00225 }
00226 } finally {
00227 try { output.close(); } catch (Throwable ignore) {}
00228 try { input.close(); } catch (Throwable ignore) {}
00229 try { socket.close(); } catch (Throwable ignore) {}
00230 }
00231 }
00232
00233 private String readLine() throws IOException {
00234 if (buffer == null) {
00235 buffer = new byte[2048];
00236 }
00237 int next;
00238 int count = 0;
00239 for (;;) {
00240 try {
00241 next = input.read();
00242 firstByte = false;
00243 } catch (SocketException e) {
00244 if (firstByte) {
00245 return null;
00246 } else {
00247 throw e;
00248 }
00249 }
00250 if (next < 0 || next == '\n') {
00251 break;
00252 }
00253 if (next != '\r') {
00254 buffer[count++] = (byte) next;
00255 }
00256 if (count >= buffer.length) {
00257 throw new IOException("HTTP Header too long");
00258 }
00259 }
00260 return new String(buffer, 0, count, US_ASCII);
00261 }
00262
00269 public void writeResponse(RequestData pData, OutputStream pBuffer)
00270 throws IOException {
00271 ByteArrayOutputStream response = (ByteArrayOutputStream) pBuffer;
00272 writeResponseHeader(pData, response.size());
00273 response.writeTo(output);
00274 }
00275
00281 public void writeResponseHeader(RequestData pData, int pContentLength)
00282 throws IOException {
00283 output.write(toHTTPBytes(pData.getHttpVersion()));
00284 output.write(ok);
00285 output.write(serverName);
00286 output.write(pData.isKeepAlive() ? conkeep : conclose);
00287 output.write(ctype);
00288 if (headers != null) {
00289 for (Iterator iter = headers.entrySet().iterator(); iter.hasNext(); ) {
00290 Map.Entry entry = (Map.Entry) iter.next();
00291 String header = (String) entry.getKey();
00292 String value = (String) entry.getValue();
00293 output.write(toHTTPBytes(header + ": " + value + "\r\n"));
00294 }
00295 }
00296 if (pContentLength != -1) {
00297 output.write(clength);
00298 output.write(toHTTPBytes(Integer.toString(pContentLength)));
00299 output.write(doubleNewline);
00300 } else {
00301 output.write(newline);
00302 }
00303 pData.setSuccess(true);
00304 }
00305
00312 public void writeError(RequestData pData, Throwable pError, ByteArrayOutputStream pStream)
00313 throws IOException {
00314 writeErrorHeader(pData, pError, pStream.size());
00315 pStream.writeTo(output);
00316 output.flush();
00317 }
00318
00325 public void writeErrorHeader(RequestData pData, Throwable pError, int pContentLength)
00326 throws IOException {
00327 if (pError instanceof BadRequestException) {
00328 final byte[] content = toHTTPBytes("Method " + pData.getMethod()
00329 + " not implemented (try POST)\r\n");
00330 output.write(toHTTPBytes(pData.getHttpVersion()));
00331 output.write(toHTTPBytes(" 400 Bad Request"));
00332 output.write(newline);
00333 output.write(serverName);
00334 writeContentLengthHeader(content.length);
00335 output.write(newline);
00336 output.write(content);
00337 } else if (pError instanceof BadEncodingException) {
00338 final byte[] content = toHTTPBytes("The Transfer-Encoding " + pError.getMessage()
00339 + " is not implemented.\r\n");
00340 output.write(toHTTPBytes(pData.getHttpVersion()));
00341 output.write(toHTTPBytes(" 501 Not Implemented"));
00342 output.write(newline);
00343 output.write(serverName);
00344 writeContentLengthHeader(content.length);
00345 output.write(newline);
00346 output.write(content);
00347 } else if (pError instanceof XmlRpcNotAuthorizedException) {
00348 final byte[] content = toHTTPBytes("Method " + pData.getMethod()
00349 + " requires a " + "valid user name and password.\r\n");
00350 output.write(toHTTPBytes(pData.getHttpVersion()));
00351 output.write(toHTTPBytes(" 401 Unauthorized"));
00352 output.write(newline);
00353 output.write(serverName);
00354 writeContentLengthHeader(content.length);
00355 output.write(wwwAuthenticate);
00356 output.write(newline);
00357 output.write(content);
00358 } else {
00359 output.write(toHTTPBytes(pData.getHttpVersion()));
00360 output.write(ok);
00361 output.write(serverName);
00362 output.write(conclose);
00363 output.write(ctype);
00364 writeContentLengthHeader(pContentLength);
00365 output.write(newline);
00366 }
00367 }
00368
00369 private void writeContentLengthHeader(int pContentLength) throws IOException {
00370 if (pContentLength == -1) {
00371 return;
00372 }
00373 output.write(clength);
00374 output.write(toHTTPBytes(Integer.toString(pContentLength)));
00375 output.write(newline);
00376 }
00377
00380 public void setResponseHeader(String pHeader, String pValue) {
00381 headers.put(pHeader, pValue);
00382 }
00383
00384
00385 public OutputStream newOutputStream() throws IOException {
00386 boolean useContentLength;
00387 useContentLength = !requestData.isEnabledForExtensions()
00388 || !((XmlRpcHttpRequestConfig) requestData).isContentLengthOptional();
00389 if (useContentLength) {
00390 return new ByteArrayOutputStream();
00391 } else {
00392 return output;
00393 }
00394 }
00395
00396 public InputStream newInputStream() throws IOException {
00397 int contentLength = requestData.getContentLength();
00398 if (contentLength == -1) {
00399 return input;
00400 } else {
00401 return new LimitedInputStream(input, contentLength);
00402 }
00403 }
00404
00405 public void close() throws IOException {
00406 }
00407
00408 public void shutdown() throws Throwable {
00409 shuttingDown = true;
00410 socket.close();
00411 }
00412 }