Author: jochen Date: Thu Apr 20 13:52:51 2006 New Revision: 395693 URL: http://svn.apache.org/viewcvs?rev=395693&view=rev Log: XMLRPC-79: The "Content-Encoding" header wasn't set for a gzipped response.
Added: webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/server/XmlRpcHttpServer.java Modified: webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/server/XmlRpcStreamServer.java webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/Connection.java webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/ConnectionServer.java webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/XmlRpcServletServer.java Added: webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/server/XmlRpcHttpServer.java URL: http://svn.apache.org/viewcvs/webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/server/XmlRpcHttpServer.java?rev=395693&view=auto ============================================================================== --- webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/server/XmlRpcHttpServer.java (added) +++ webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/server/XmlRpcHttpServer.java Thu Apr 20 13:52:51 2006 @@ -0,0 +1,23 @@ +package org.apache.xmlrpc.server; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.xmlrpc.common.ServerStreamConnection; +import org.apache.xmlrpc.common.XmlRpcStreamRequestConfig; + + + +/** Abstract extension of [EMAIL PROTECTED] XmlRpcStreamServer} for deriving + * HTTP servers. + */ +public abstract class XmlRpcHttpServer extends XmlRpcStreamServer { + protected abstract void setResponseHeader(ServerStreamConnection pConnection, String pHeader, String pValue); + + protected OutputStream getOutputStream(ServerStreamConnection pConnection, XmlRpcStreamRequestConfig pConfig, OutputStream pStream) throws IOException { + if (pConfig.isEnabledForExtensions() && pConfig.isGzipRequesting()) { + setResponseHeader(pConnection, "Content-Encoding", "gzip"); + } + return super.getOutputStream(pConnection, pConfig, pStream); + } +} Modified: webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/server/XmlRpcStreamServer.java URL: http://svn.apache.org/viewcvs/webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/server/XmlRpcStreamServer.java?rev=395693&r1=395692&r2=395693&view=diff ============================================================================== --- webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/server/XmlRpcStreamServer.java (original) +++ webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/server/XmlRpcStreamServer.java Thu Apr 20 13:52:51 2006 @@ -147,8 +147,8 @@ /** Called to prepare the output stream. Typically used for enabling * compression, or similar filters. */ - protected OutputStream getOutputStream(XmlRpcStreamRequestConfig pConfig, - OutputStream pStream) throws IOException { + protected OutputStream getOutputStream(ServerStreamConnection pConnection, + XmlRpcStreamRequestConfig pConfig, OutputStream pStream) throws IOException { if (pConfig.isEnabledForExtensions() && pConfig.isGzipRequesting()) { return new GZIPOutputStream(pStream); } else { @@ -215,7 +215,7 @@ baos = null; ostream = newOutputStream(pConfig, pConnection); } - ostream = getOutputStream(pConfig, ostream); + ostream = getOutputStream(pConnection, pConfig, ostream); try { if (error == null) { writeResponse(pConfig, ostream, result); Modified: webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/Connection.java URL: http://svn.apache.org/viewcvs/webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/Connection.java?rev=395693&r1=395692&r2=395693&view=diff ============================================================================== --- webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/Connection.java (original) +++ webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/Connection.java Thu Apr 20 13:52:51 2006 @@ -23,6 +23,8 @@ import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.Socket; +import java.util.Iterator; +import java.util.Map; import java.util.StringTokenizer; import org.apache.xmlrpc.common.ServerStreamConnection; @@ -42,7 +44,7 @@ * multiple requests via a single, physical connection. */ public class Connection implements ThreadPool.Task, ServerStreamConnection { - private static final String US_ASCII = "US-ASCII"; + private static final String US_ASCII = "US-ASCII"; private static final byte[] ctype = toHTTPBytes("Content-Type: text/xml\r\n"); private static final byte[] clength = toHTTPBytes("Content-Length: "); private static final byte[] newline = toHTTPBytes("\r\n"); @@ -53,31 +55,32 @@ private static final byte[] serverName = toHTTPBytes("Server: Apache XML-RPC 1.0\r\n"); private static final byte[] wwwAuthenticate = toHTTPBytes("WWW-Authenticate: Basic realm=XML-RPC\r\n"); - private static class BadRequestException extends IOException { - private static final long serialVersionUID = 3257848779234554934L; - BadRequestException(String pMethod) { - super(pMethod); - } - } + private static class BadRequestException extends IOException { + private static final long serialVersionUID = 3257848779234554934L; + BadRequestException(String pMethod) { + super(pMethod); + } + } - /** Returns the US-ASCII encoded byte representation of text for + /** Returns the US-ASCII encoded byte representation of text for * HTTP use (as per section 2.2 of RFC 2068). */ private static final byte[] toHTTPBytes(String text) { try { return text.getBytes(US_ASCII); } catch (UnsupportedEncodingException e) { - throw new Error(e.getMessage() + - ": HTTP requires US-ASCII encoding"); + throw new Error(e.getMessage() + + ": HTTP requires US-ASCII encoding"); } } - private final WebServer webServer; - private final Socket socket; + private final WebServer webServer; + private final Socket socket; private final InputStream input; private final OutputStream output; - private final XmlRpcStreamServer server; - private byte[] buffer; + private final XmlRpcStreamServer server; + private byte[] buffer; + private Map headers; /** Creates a new webserver connection on the given socket. * @param pWebServer The webserver maintaining this connection. @@ -87,95 +90,98 @@ * @throws IOException */ public Connection(WebServer pWebServer, XmlRpcStreamServer pServer, Socket pSocket) - throws IOException { - webServer = pWebServer; - server = pServer; - socket = pSocket; - // set read timeout to 30 seconds + throws IOException { + webServer = pWebServer; + server = pServer; + socket = pSocket; + // set read timeout to 30 seconds socket.setSoTimeout (30000); input = new BufferedInputStream(socket.getInputStream()){ - /** It may happen, that the XML parser invokes close(). - * Closing the input stream must not occur, because - * that would close the whole socket. So we suppress it. - */ - public void close() throws IOException { - } + /** It may happen, that the XML parser invokes close(). + * Closing the input stream must not occur, because + * that would close the whole socket. So we suppress it. + */ + public void close() throws IOException { + } }; output = new BufferedOutputStream(socket.getOutputStream()); - } + } + + /** Returns the connections request configuration by + * merging the HTTP request headers and the servers configuration. + * @return The connections request configuration. + * @throws IOException Reading the request headers failed. + */ + private RequestData getRequestConfig() throws IOException { + RequestData result = new RequestData(this); + if (headers != null) { + headers.clear(); + } + XmlRpcHttpServerConfig serverConfig = (XmlRpcHttpServerConfig) server.getConfig(); + result.setBasicEncoding(serverConfig.getBasicEncoding()); + result.setContentLengthOptional(serverConfig.isContentLengthOptional()); + result.setEnabledForExtensions(serverConfig.isEnabledForExtensions()); + + // reset user authentication + String line = readLine(); + // Netscape sends an extra \n\r after bodypart, swallow it + if (line != null && line.length() == 0) { + line = readLine(); + if (line == null || line.length() == 0) { + return null; + } + } + + // tokenize first line of HTTP request + StringTokenizer tokens = new StringTokenizer(line); + String method = tokens.nextToken(); + if (!"POST".equalsIgnoreCase(method)) { + throw new BadRequestException(method); + } + result.setMethod(method); + tokens.nextToken(); // Skip URI + String httpVersion = tokens.nextToken(); + result.setHttpVersion(httpVersion); + result.setKeepAlive(serverConfig.isKeepAliveEnabled() + && WebServer.HTTP_11.equals(httpVersion)); + do { + line = readLine(); + if (line != null) { + String lineLower = line.toLowerCase(); + if (lineLower.startsWith("content-length:")) { + String cLength = line.substring("content-length:".length()); + result.setContentLength(Integer.parseInt(cLength.trim())); + } else if (lineLower.startsWith("connection:")) { + result.setKeepAlive(serverConfig.isKeepAliveEnabled() + && lineLower.indexOf("keep-alive") > -1); + } else if (lineLower.startsWith("authorization:")) { + String credentials = line.substring("authorization:".length()); + HttpUtil.parseAuthorization(result, credentials); + } + } + } + while (line != null && line.length() != 0); - /** Returns the connections request configuration by - * merging the HTTP request headers and the servers configuration. - * @return The connections request configuration. - * @throws IOException Reading the request headers failed. - */ - private RequestData getRequestConfig() throws IOException { - RequestData result = new RequestData(this); - XmlRpcHttpServerConfig serverConfig = (XmlRpcHttpServerConfig) server.getConfig(); - result.setBasicEncoding(serverConfig.getBasicEncoding()); - result.setContentLengthOptional(serverConfig.isContentLengthOptional()); - result.setEnabledForExtensions(serverConfig.isEnabledForExtensions()); - - // reset user authentication - String line = readLine(); - // Netscape sends an extra \n\r after bodypart, swallow it - if (line != null && line.length() == 0) { - line = readLine(); - if (line == null || line.length() == 0) { - return null; - } - } - - // tokenize first line of HTTP request - StringTokenizer tokens = new StringTokenizer(line); - String method = tokens.nextToken(); - if (!"POST".equalsIgnoreCase(method)) { - throw new BadRequestException(method); - } - result.setMethod(method); - tokens.nextToken(); // Skip URI - String httpVersion = tokens.nextToken(); - result.setHttpVersion(httpVersion); - result.setKeepAlive(serverConfig.isKeepAliveEnabled() - && WebServer.HTTP_11.equals(httpVersion)); - do { - line = readLine(); - if (line != null) { - String lineLower = line.toLowerCase(); - if (lineLower.startsWith("content-length:")) { - String cLength = line.substring("content-length:".length()); - result.setContentLength(Integer.parseInt(cLength.trim())); - } else if (lineLower.startsWith("connection:")) { - result.setKeepAlive(serverConfig.isKeepAliveEnabled() - && lineLower.indexOf("keep-alive") > -1); - } else if (lineLower.startsWith("authorization:")) { - String credentials = line.substring("authorization:".length()); - HttpUtil.parseAuthorization(result, credentials); - } - } - } - while (line != null && line.length() != 0); - - return result; - } + return result; + } public void run() { try { - for (int i = 0; ; i++) { - RequestData data = getRequestConfig(); - if (data == null) { - break; - } - server.execute(data, this); - output.flush(); - if (!data.isKeepAlive() || !data.isSuccess()) { - break; - } - } + for (int i = 0; ; i++) { + RequestData data = getRequestConfig(); + if (data == null) { + break; + } + server.execute(data, this); + output.flush(); + if (!data.isKeepAlive() || !data.isSuccess()) { + break; + } + } } catch (Throwable t) { - webServer.log(t); + webServer.log(t); } finally { - try { socket.close(); } catch (Throwable ignore) {} + try { socket.close(); } catch (Throwable ignore) {} } } @@ -201,121 +207,135 @@ } /** Returns the contents input stream. - * @param pData The request data - * @return The contents input stream. - */ - public InputStream getInputStream(RequestData pData) { - int contentLength = pData.getContentLength(); - if (contentLength == -1) { - return input; - } else { - return new LimitedInputStream(input, contentLength); - } - } - - /** Returns the output stream for writing the response. - * @param pConfig The request configuration. - * @return The response output stream. - */ - public OutputStream getOutputStream(XmlRpcStreamRequestConfig pConfig) { - boolean useContentLength; - if (pConfig instanceof XmlRpcHttpRequestConfig) { - useContentLength = !pConfig.isEnabledForExtensions() - || !((XmlRpcHttpRequestConfig) pConfig).isContentLengthOptional(); - } else { - useContentLength = true; - } - if (useContentLength) { - return new ByteArrayOutputStream(); - } else { - return output; - } - } - - /** Writes the response header and the response to the - * output stream. - * @param pData The request data. - * @param pBuffer The [EMAIL PROTECTED] ByteArrayOutputStream} holding the response. - * @throws IOException Writing the response failed. - */ - public void writeResponse(RequestData pData, OutputStream pBuffer) - throws IOException { - ByteArrayOutputStream response = (ByteArrayOutputStream) pBuffer; - writeResponseHeader(pData, response.size()); - response.writeTo(output); - } - - /** Writes the response header to the output stream. * - * @param pData The request data - * @param pContentLength The content length, if known, or -1. - * @throws IOException Writing the response failed. - */ - public void writeResponseHeader(RequestData pData, int pContentLength) - throws IOException { + * @param pData The request data + * @return The contents input stream. + */ + public InputStream getInputStream(RequestData pData) { + int contentLength = pData.getContentLength(); + if (contentLength == -1) { + return input; + } else { + return new LimitedInputStream(input, contentLength); + } + } + + /** Returns the output stream for writing the response. + * @param pConfig The request configuration. + * @return The response output stream. + */ + public OutputStream getOutputStream(XmlRpcStreamRequestConfig pConfig) { + boolean useContentLength; + if (pConfig instanceof XmlRpcHttpRequestConfig) { + useContentLength = !pConfig.isEnabledForExtensions() + || !((XmlRpcHttpRequestConfig) pConfig).isContentLengthOptional(); + } else { + useContentLength = true; + } + if (useContentLength) { + return new ByteArrayOutputStream(); + } else { + return output; + } + } + + /** Writes the response header and the response to the + * output stream. + * @param pData The request data. + * @param pBuffer The [EMAIL PROTECTED] ByteArrayOutputStream} holding the response. + * @throws IOException Writing the response failed. + */ + public void writeResponse(RequestData pData, OutputStream pBuffer) + throws IOException { + ByteArrayOutputStream response = (ByteArrayOutputStream) pBuffer; + writeResponseHeader(pData, response.size()); + response.writeTo(output); + } + + /** Writes the response header to the output stream. * + * @param pData The request data + * @param pContentLength The content length, if known, or -1. + * @throws IOException Writing the response failed. + */ + public void writeResponseHeader(RequestData pData, int pContentLength) + throws IOException { output.write(toHTTPBytes(pData.getHttpVersion())); output.write(ok); output.write(serverName); output.write(pData.isKeepAlive() ? conkeep : conclose); output.write(ctype); - if (pContentLength != -1) { - output.write(clength); - output.write(toHTTPBytes(Integer.toString(pContentLength))); - output.write(doubleNewline); - } else { - output.write(newline); - } - pData.setSuccess(true); - } - - /** Writes an error response to the output stream. - * @param pData The request data. - * @param pError The error being reported. - * @param pStream The [EMAIL PROTECTED] ByteArrayOutputStream} with the error response. - * @throws IOException Writing the response failed. - */ - public void writeError(RequestData pData, Throwable pError, OutputStream pStream) - throws IOException { - ByteArrayOutputStream errorResponse = (ByteArrayOutputStream) pStream; - writeErrorHeader(pData, pError, errorResponse.size()); - errorResponse.writeTo(output); - } - - /** Writes an error responses headers to the output stream. - * @param pData The request data. - * @param pError The error being reported. - * @param pContentLength The response length, if known, or -1. - * @throws IOException Writing the response failed. - */ - public void writeErrorHeader(RequestData pData, Throwable pError, int pContentLength) - throws IOException { - if (pError instanceof BadRequestException) { - output.write(toHTTPBytes(pData.getHttpVersion())); - output.write(toHTTPBytes(" 400 Bad Request")); - output.write(newline); - output.write(serverName); - output.write(doubleNewline); - output.write(toHTTPBytes("Method " + pData.getMethod() + - " not implemented (try POST)")); - } else if (pError instanceof XmlRpcNotAuthorizedException) { - output.write(toHTTPBytes(pData.getHttpVersion())); - output.write(toHTTPBytes(" 401 Unauthorized")); - output.write(newline); - output.write(serverName); - output.write(wwwAuthenticate); - output.write(doubleNewline); - output.write(toHTTPBytes("Method " + pData.getMethod() + " requires a " + - "valid user name and password")); - } else { - output.write(toHTTPBytes(pData.getHttpVersion())); - output.write(ok); - output.write(serverName); - output.write(conclose); - output.write(ctype); - if (pContentLength != -1) { - output.write(clength); - output.write(toHTTPBytes(Integer.toString(pContentLength))); - } - output.write(doubleNewline); - } - } + if (headers != null) { + for (Iterator iter = headers.entrySet().iterator(); iter.hasNext(); ) { + Map.Entry entry = (Map.Entry) iter.next(); + String header = (String) entry.getKey(); + String value = (String) entry.getValue(); + output.write(toHTTPBytes(header + ": " + value + "\r\n")); + } + } + if (pContentLength != -1) { + output.write(clength); + output.write(toHTTPBytes(Integer.toString(pContentLength))); + output.write(doubleNewline); + } else { + output.write(newline); + } + pData.setSuccess(true); + } + + /** Writes an error response to the output stream. + * @param pData The request data. + * @param pError The error being reported. + * @param pStream The [EMAIL PROTECTED] ByteArrayOutputStream} with the error response. + * @throws IOException Writing the response failed. + */ + public void writeError(RequestData pData, Throwable pError, OutputStream pStream) + throws IOException { + ByteArrayOutputStream errorResponse = (ByteArrayOutputStream) pStream; + writeErrorHeader(pData, pError, errorResponse.size()); + errorResponse.writeTo(output); + } + + /** Writes an error responses headers to the output stream. + * @param pData The request data. + * @param pError The error being reported. + * @param pContentLength The response length, if known, or -1. + * @throws IOException Writing the response failed. + */ + public void writeErrorHeader(RequestData pData, Throwable pError, int pContentLength) + throws IOException { + if (pError instanceof BadRequestException) { + output.write(toHTTPBytes(pData.getHttpVersion())); + output.write(toHTTPBytes(" 400 Bad Request")); + output.write(newline); + output.write(serverName); + output.write(doubleNewline); + output.write(toHTTPBytes("Method " + pData.getMethod() + + " not implemented (try POST)")); + } else if (pError instanceof XmlRpcNotAuthorizedException) { + output.write(toHTTPBytes(pData.getHttpVersion())); + output.write(toHTTPBytes(" 401 Unauthorized")); + output.write(newline); + output.write(serverName); + output.write(wwwAuthenticate); + output.write(doubleNewline); + output.write(toHTTPBytes("Method " + pData.getMethod() + " requires a " + + "valid user name and password")); + } else { + output.write(toHTTPBytes(pData.getHttpVersion())); + output.write(ok); + output.write(serverName); + output.write(conclose); + output.write(ctype); + if (pContentLength != -1) { + output.write(clength); + output.write(toHTTPBytes(Integer.toString(pContentLength))); + } + output.write(doubleNewline); + } + } + + /** Sets a response header value. + */ + public void setResponseHeader(String pHeader, String pValue) { + headers.put(pHeader, pValue); + } } Modified: webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/ConnectionServer.java URL: http://svn.apache.org/viewcvs/webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/ConnectionServer.java?rev=395693&r1=395692&r2=395693&view=diff ============================================================================== --- webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/ConnectionServer.java (original) +++ webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/ConnectionServer.java Thu Apr 20 13:52:51 2006 @@ -22,10 +22,10 @@ import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.common.ServerStreamConnection; import org.apache.xmlrpc.common.XmlRpcStreamRequestConfig; -import org.apache.xmlrpc.server.XmlRpcStreamServer; +import org.apache.xmlrpc.server.XmlRpcHttpServer; -class ConnectionServer extends XmlRpcStreamServer { +class ConnectionServer extends XmlRpcHttpServer { protected void writeError(XmlRpcStreamRequestConfig pConfig, OutputStream pStream, Throwable pError) throws XmlRpcException { RequestData data = (RequestData) pConfig; @@ -68,5 +68,9 @@ } protected void closeConnection(ServerStreamConnection pConnection) throws IOException { + } + + protected void setResponseHeader(ServerStreamConnection pConnection, String pHeader, String pValue) { + ((Connection) pConnection).setResponseHeader(pHeader, pValue); } } Modified: webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/XmlRpcServletServer.java URL: http://svn.apache.org/viewcvs/webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/XmlRpcServletServer.java?rev=395693&r1=395692&r2=395693&view=diff ============================================================================== --- webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/XmlRpcServletServer.java (original) +++ webservices/xmlrpc/trunk/server/src/main/java/org/apache/xmlrpc/webserver/XmlRpcServletServer.java Thu Apr 20 13:52:51 2006 @@ -25,18 +25,17 @@ import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.common.ServerStreamConnection; -import org.apache.xmlrpc.common.XmlRpcHttpRequestConfig; import org.apache.xmlrpc.common.XmlRpcHttpRequestConfigImpl; import org.apache.xmlrpc.common.XmlRpcStreamRequestConfig; +import org.apache.xmlrpc.server.XmlRpcHttpServer; import org.apache.xmlrpc.server.XmlRpcHttpServerConfig; -import org.apache.xmlrpc.server.XmlRpcStreamServer; import org.apache.xmlrpc.util.HttpUtil; /** An extension of [EMAIL PROTECTED] org.apache.xmlrpc.server.XmlRpcServer}, * which is suitable for processing servlet requests. */ -public class XmlRpcServletServer extends XmlRpcStreamServer { +public class XmlRpcServletServer extends XmlRpcHttpServer { protected static class ServletStreamConnection implements ServerStreamConnection { private final HttpServletRequest request; private final HttpServletResponse response; @@ -127,5 +126,9 @@ protected void closeConnection(ServerStreamConnection pConnection) throws IOException { ((ServletStreamConnection) pConnection).getResponse().getOutputStream().close(); + } + + protected void setResponseHeader(ServerStreamConnection pConnection, String pHeader, String pValue) { + ((ServletStreamConnection) pConnection).getResponse().setHeader(pHeader, pValue); } }