From 532c23b5c7f56e9197ac84f16fc616ae13607399 Mon Sep 17 00:00:00 2001 From: theli Date: Sat, 28 Oct 2006 12:31:48 +0000 Subject: [PATCH] *) soap handler - better errorhandling - adding support for outgoing transfer- and content-encoding - avoid holding outgoing messages into memory before sending them git-svn-id: https://svn.berlios.de/svnroot/repos/yacy/trunk@2872 6c8d7289-2bf4-0310-a012-ef5d649a1542 --- .../anomic/http/httpChunkedOutputStream.java | 2 +- source/de/anomic/http/httpHeader.java | 12 + source/de/anomic/http/httpd.java | 6 +- source/de/anomic/soap/SoapException.java | 68 +++- source/de/anomic/soap/httpdSoapHandler.java | 315 +++++++++--------- .../de/anomic/soap/services/ShareService.java | 8 + .../soap/services/AbstractServiceTest.java | 19 +- .../de/anomic/soap/services/ServiceTests.java | 18 + .../soap/services/ShareServiceTest.java | 4 +- 9 files changed, 270 insertions(+), 182 deletions(-) create mode 100644 test/de/anomic/soap/services/ServiceTests.java diff --git a/source/de/anomic/http/httpChunkedOutputStream.java b/source/de/anomic/http/httpChunkedOutputStream.java index 7ecc76429..be603d2d5 100644 --- a/source/de/anomic/http/httpChunkedOutputStream.java +++ b/source/de/anomic/http/httpChunkedOutputStream.java @@ -47,7 +47,7 @@ import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; -final class httpChunkedOutputStream extends FilterOutputStream +public final class httpChunkedOutputStream extends FilterOutputStream { private boolean finished = false; private static final byte[] crlf = {(byte)13,(byte)10}; diff --git a/source/de/anomic/http/httpHeader.java b/source/de/anomic/http/httpHeader.java index 50123c07d..fc43648c9 100644 --- a/source/de/anomic/http/httpHeader.java +++ b/source/de/anomic/http/httpHeader.java @@ -626,6 +626,7 @@ public final class httpHeader extends TreeMap implements Map { args = args.substring(7); sep = args.indexOf("/"); if (sep < 0) { + /* // this is a malformed url, something like // http://index.html // we are lazy and guess that it means @@ -633,6 +634,9 @@ public final class httpHeader extends TreeMap implements Map { // which is a localhost access to the file servlet prop.setProperty(httpHeader.CONNECTION_PROP_HOST, virtualHost); prop.setProperty(httpHeader.CONNECTION_PROP_PATH, "/" + args); + */ + prop.setProperty(httpHeader.CONNECTION_PROP_HOST, args); + prop.setProperty(httpHeader.CONNECTION_PROP_PATH, "/"); } else { // THIS IS THE "GOOD" CASE // a perfect formulated url @@ -659,6 +663,14 @@ public final class httpHeader extends TreeMap implements Map { return prop; } + public static boolean supportChunkedEncoding(Properties conProp) { + // getting the http version of the soap client + String httpVer = conProp.getProperty(httpHeader.CONNECTION_PROP_HTTP_VER); + + // only clients with http version 1.1 supports chunk + return !(httpVer.equals(httpHeader.HTTP_VERSION_0_9) || httpVer.equals(httpHeader.HTTP_VERSION_1_0)); + } + /** * Reading http headers from a reader class and building up a httpHeader object * @param reader the {@link BufferedReader} that is used to read the http header lines diff --git a/source/de/anomic/http/httpd.java b/source/de/anomic/http/httpd.java index 487d537b5..a1ec96ca1 100644 --- a/source/de/anomic/http/httpd.java +++ b/source/de/anomic/http/httpd.java @@ -256,7 +256,7 @@ public final class httpd implements serverHandler { // afterwards. In HTTP/1.1 (and above, in the future?) connections are // persistent by default, but closed with the "Connection: close" // property. - boolean persistent = !(httpVersion.equals("HTTP/0.9") || httpVersion.equals("HTTP/1.0")); + boolean persistent = !(httpVersion.equals(httpHeader.HTTP_VERSION_0_9) || httpVersion.equals(httpHeader.HTTP_VERSION_1_0)); if (((String)header.get(httpHeader.CONNECTION, "keep-alive")).toLowerCase().indexOf("close") != -1 || ((String)header.get(httpHeader.PROXY_CONNECTION, "keep-alive")).toLowerCase().indexOf("close") != -1) { persistent = false; @@ -1279,7 +1279,7 @@ public final class httpd implements serverHandler { if (respond == null) throw new NullPointerException("The outputstream must not be null."); if (conProp == null) throw new NullPointerException("The connection property structure must not be null."); - if (httpVersion == null) httpVersion = conProp.getProperty(httpHeader.CONNECTION_PROP_HTTP_VER,"HTTP/1.1"); + if (httpVersion == null) httpVersion = conProp.getProperty(httpHeader.CONNECTION_PROP_HTTP_VER,httpHeader.HTTP_VERSION_1_1); if (header == null) header = new httpHeader(); try { @@ -1294,7 +1294,7 @@ public final class httpd implements serverHandler { StringBuffer headerStringBuffer = new StringBuffer(560); // "HTTP/0.9" does not have a status line or header in the response - if (! httpVersion.toUpperCase().equals("HTTP/0.9")) { + if (! httpVersion.toUpperCase().equals(httpHeader.HTTP_VERSION_0_9)) { // write status line headerStringBuffer.append(httpVersion).append(" ") .append(Integer.toString(httpStatusCode)).append(" ") diff --git a/source/de/anomic/soap/SoapException.java b/source/de/anomic/soap/SoapException.java index c7e5d84fa..770f53020 100644 --- a/source/de/anomic/soap/SoapException.java +++ b/source/de/anomic/soap/SoapException.java @@ -45,7 +45,14 @@ package de.anomic.soap; +import javax.xml.namespace.QName; + +import org.apache.axis.AxisFault; +import org.apache.axis.Constants; import org.apache.axis.Message; +import org.apache.axis.MessageContext; +import org.apache.axis.message.SOAPEnvelope; +import org.apache.axis.message.SOAPFault; import de.anomic.http.httpHeader; @@ -54,23 +61,41 @@ public class SoapException extends Exception { private static final long serialVersionUID = 1L; private int statusCode = 500; private String statusText = (String) httpHeader.http1_1.get(Integer.toString(this.statusCode)); - private Object errorMsg = this.statusText; + private AxisFault fault = new AxisFault(this.statusText); public SoapException(int httpStatusCode, String httpStatusText, String errorMsg) { - super(httpStatusCode + " " + httpStatusText); - this.statusCode = httpStatusCode; this.statusText = httpStatusText; - this.errorMsg = errorMsg; + this.fault = new AxisFault(errorMsg); } - public SoapException(int httpStatusCode, String httpStatusText, Message errorMsg) { + public SoapException(int httpStatusCode, String httpStatusText, Exception e) { super(httpStatusCode + " " + httpStatusText); - + this.statusCode = httpStatusCode; - this.statusText = httpStatusText; - this.errorMsg = errorMsg; - } + this.statusText = httpStatusText; + + // convert the exception into an axisfault + this.fault = AxisFault.makeFault(e); + } + + public SoapException(AxisFault soapFault) { + + QName faultCode = soapFault.getFaultCode(); + if (Constants.FAULT_SOAP12_SENDER.equals(faultCode)) { + this.statusCode = 400; + this.statusText = "Bad request"; + } else if ("Server.Unauthorized".equals(faultCode.getLocalPart())) { + this.statusCode = 401; + this.statusText = "Unauthorized"; + } else { + this.statusCode = 500; + this.statusText = "Internal server error"; + } + + // convert the exception into an axisfault + this.fault = soapFault; + } public int getStatusCode() { return this.statusCode; @@ -80,7 +105,28 @@ public class SoapException extends Exception { return this.statusText; } - public Object getErrorMsg() { - return this.errorMsg; + public Object getFault() { + return this.fault; + } + + public Message getFaultMessage(MessageContext msgContext) { + Message responseMsg = msgContext.getResponseMessage(); + if (responseMsg == null) { + responseMsg = new Message(this.fault); + responseMsg.setMessageContext(msgContext); + } else { + try { + SOAPEnvelope env = responseMsg.getSOAPEnvelope(); + env.clearBody(); + env.addBodyElement(new SOAPFault(this.fault)); + } catch (AxisFault e) { + // Should never reach here! + } + } + return responseMsg; + } + + public String getMessage() { + return this.statusCode + " " + this.statusText; } } diff --git a/source/de/anomic/soap/httpdSoapHandler.java b/source/de/anomic/soap/httpdSoapHandler.java index eb2e63d3a..b1e84cd08 100644 --- a/source/de/anomic/soap/httpdSoapHandler.java +++ b/source/de/anomic/soap/httpdSoapHandler.java @@ -47,42 +47,39 @@ package de.anomic.soap; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PushbackInputStream; -import java.io.UnsupportedEncodingException; import java.util.Enumeration; import java.util.HashMap; import java.util.Properties; import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import javax.xml.namespace.QName; import javax.xml.parsers.ParserConfigurationException; import javax.xml.soap.SOAPException; import org.apache.axis.AxisFault; -import org.apache.axis.Constants; import org.apache.axis.EngineConfiguration; import org.apache.axis.Message; import org.apache.axis.MessageContext; import org.apache.axis.WSDDEngineConfiguration; import org.apache.axis.deployment.wsdd.WSDDDeployment; import org.apache.axis.deployment.wsdd.WSDDDocument; -import org.apache.axis.message.SOAPEnvelope; -import org.apache.axis.message.SOAPFault; import org.apache.axis.server.AxisServer; import org.apache.axis.utils.XMLUtils; import org.w3c.dom.Document; import org.xml.sax.SAXException; import de.anomic.http.httpChunkedInputStream; +import de.anomic.http.httpChunkedOutputStream; import de.anomic.http.httpContentLengthInputStream; import de.anomic.http.httpHeader; -import de.anomic.http.httpc; +import de.anomic.http.httpd; import de.anomic.http.httpdAbstractHandler; import de.anomic.http.httpdHandler; import de.anomic.plasma.plasmaParser; @@ -298,6 +295,7 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http * Handle TRANSFER ENCODING * =========================================================================== */ if (transferEncoding != null && !transferEncoding.equalsIgnoreCase("identity")) { + // read using transfer encoding if (transferEncoding.equalsIgnoreCase("chunked")) { input = new httpChunkedInputStream(body); } else { @@ -306,8 +304,10 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http throw new SoapException(501,"Not Implemented",errorMsg); } } else if (contentLength > 0) { + // read contentLength bytes input = new httpContentLengthInputStream(body,contentLength); } else { + // read until EOF input = body; } @@ -325,7 +325,7 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http } } } catch (IOException e) { - throw new SoapException(400,"Bad Request",e.getMessage()); + throw new SoapException(400,"Bad Request",e); } return input; @@ -340,8 +340,8 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http * * @see de.anomic.http.httpdHandler#doHead(java.util.Properties, de.anomic.http.httpHeader, java.io.OutputStream) */ - public void doHead(Properties conProp, httpHeader header, OutputStream clientOut) throws IOException { - sendMessage(clientOut, 501, "Not Implemented", "Connection method is not supported by this handler",null); + public void doHead(Properties conProp, httpHeader requestHeader, OutputStream clientOut) throws IOException { + sendMessage(conProp, requestHeader, clientOut, 501, "Not Implemented", "Connection method is not supported by this handler",null); conProp.setProperty(httpHeader.CONNECTION_PROP_PERSISTENT,"close"); } @@ -357,7 +357,7 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http * @see de.anomic.http.httpdHandler#doConnect(java.util.Properties, de.anomic.http.httpHeader, java.io.InputStream, java.io.OutputStream) */ public void doConnect(Properties conProp, httpHeader requestHeader, InputStream clientIn, OutputStream clientOut) throws IOException { - sendMessage(clientOut, 501, "Not Implemented", "Connection method is not supported by this handler",null); + sendMessage(conProp, requestHeader, clientOut, 501, "Not Implemented", "Connection method is not supported by this handler",null); conProp.setProperty(httpHeader.CONNECTION_PROP_PERSISTENT,"close"); } @@ -374,18 +374,14 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http * @see de.anomic.http.httpdHandler#doGet(java.util.Properties, de.anomic.http.httpHeader, java.io.OutputStream) */ public void doGet(Properties conProp, httpHeader requestHeader, OutputStream response) throws IOException { + MessageContext msgContext = null; String path = conProp.getProperty(httpHeader.CONNECTION_PROP_PATH); try { - MessageContext msgContext = this.generateMessageContext(path, requestHeader, conProp); + // generating message context + msgContext = this.generateMessageContext(path, requestHeader, conProp); - Document doc = null; - try { - engine.generateWSDL(msgContext); - doc = (Document) msgContext.getProperty("WSDL"); - } catch (AxisFault ex) { - Message errorMsg = faultToMessage(null, msgContext, ex); - throw new SoapException(500,"Unable to generate WSDL",errorMsg); - } + // generating wsdl file + Document doc = generateWSDL(msgContext); if (doc != null) { // Converting the the wsdl document into a byte-array @@ -393,30 +389,22 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http byte[] result = responseDoc.getBytes("UTF-8"); // send back the result - sendMessage(response, 200, "OK", "text/xml; charset=utf-8", result); + sendMessage(conProp, requestHeader, response, 200, "OK", "text/xml; charset=utf-8", result); - if (!(requestHeader.get("Connection", "close").equals("keep-alive"))) { + if (!(requestHeader.get(httpHeader.CONNECTION, "close").equals("keep-alive"))) { // wait a little time until everything closes so that clients can read from the streams/sockets try {Thread.currentThread().join(200);} catch (InterruptedException e) {/* ignore this */} } } else { // if we where unable to generate the wsdl file .... String errorMsg = "Internal Server Error: Unable to generate the WSDL file."; - sendMessage(response, 500, "Internal Error", "text/plain",errorMsg.getBytes("UTF-8")); + sendMessage(conProp, requestHeader, response, 500, "Internal Error", "text/plain",errorMsg.getBytes("UTF-8")); } return; - } catch (SoapException e) { - try { - sendSoapException(response, e); - } catch (Exception ex) { - this.theLogger.logSevere("Unexpected Exception while sending error message",e); - } finally { - conProp.setProperty(httpHeader.CONNECTION_PROP_PERSISTENT,"close"); - } } catch (Exception e) { - conProp.setProperty(httpHeader.CONNECTION_PROP_PERSISTENT,"close"); - this.theLogger.logSevere("Unexpected Exception with query: " + path,e); + // handle error + handleException(conProp,requestHeader,msgContext,response,e); } } @@ -428,11 +416,11 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http * @param response {@link OutputStream} to the client * @param body the request body containing the SOAP message * - * @throws IOException - * * @see de.anomic.http.httpdHandler#doPost(java.util.Properties, de.anomic.http.httpHeader, java.io.OutputStream, java.io.PushbackInputStream) */ - public void doPost(Properties conProp, httpHeader requestHeader, OutputStream response, PushbackInputStream body) throws IOException { + public void doPost(Properties conProp, httpHeader requestHeader, OutputStream response, PushbackInputStream body) { + + MessageContext msgContext = null; String path = conProp.getProperty(httpHeader.CONNECTION_PROP_PATH); try { /* ======================================================================== @@ -442,7 +430,7 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http InputStream bodyStream = getBodyInputStream(requestHeader, body); // generating the SOAP message context that will be passed over to the invoked service - MessageContext msgContext = this.generateMessageContext(path, requestHeader, conProp); + msgContext = this.generateMessageContext(path, requestHeader, conProp); // Generating a SOAP Request Message Object String mime = plasmaParser.getRealMimeType(requestHeader.mime()); // this is important !!!! @@ -461,84 +449,69 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http Message responseMsg = this.invokeService(msgContext); if (responseMsg != null) { - sendMessage(response, 200, "OK", responseMsg); + sendMessage(conProp, requestHeader, response, 200, "OK", responseMsg); } else { - sendMessage(response, 202, "Accepted", "text/plain", null); + sendMessage(conProp, requestHeader, response, 202, "Accepted", "text/plain", null); } return; - } catch (SoapException e) { - try { - sendSoapException(response, e); - } catch (Exception ex) { - this.theLogger.logSevere("Unexpected Exception while sending error message",e); - } finally { - conProp.setProperty(httpHeader.CONNECTION_PROP_PERSISTENT,"close"); - } } catch (Exception e) { - conProp.setProperty(httpHeader.CONNECTION_PROP_PERSISTENT,"close"); - this.theLogger.logSevere("Unexpected Exception",e); + // handle error + handleException(conProp, requestHeader, msgContext, response,e); } } + private void handleException(Properties conProp, httpHeader requestHeader, MessageContext messageContext, OutputStream response, Exception e) { + try { + Message soapErrorMsg = null; + + if (!conProp.containsKey(httpHeader.CONNECTION_PROP_PROXY_RESPOND_HEADER)) { + SoapException soapEx = null; + if (!(e instanceof SoapException)) { + soapEx = new SoapException(500,"internal server error",e); + } else { + soapEx = (SoapException) e; + } + // generating a soap error message + soapErrorMsg = soapEx.getFaultMessage(messageContext); + + // send error message back to the client + sendMessage(conProp,requestHeader,response,soapEx.getStatusCode(),soapEx.getStatusText(),soapErrorMsg); + } else { + this.theLogger.logSevere("Unexpected Exception while sending data to client",e); + } + } catch (Exception ex) { + this.theLogger.logSevere("Unexpected Exception while sending error message",e); + } finally { + conProp.setProperty(httpHeader.CONNECTION_PROP_PERSISTENT,"close"); + } + } + + private Document generateWSDL(MessageContext msgContext) throws SoapException { + try { + engine.generateWSDL(msgContext); + Document doc = (Document) msgContext.getProperty("WSDL"); + return doc; + } catch (Exception ex) { + if (ex instanceof AxisFault) throw new SoapException((AxisFault)ex); + throw new SoapException(500,"Unable to generate WSDL",ex); + } + } + protected Message invokeService(MessageContext msgContext) throws SoapException { - - int invocationStatusCode = 200; - String invocationStatusText = "OK"; try { // invoke the service engine.invoke(msgContext); // Retrieve the response from Axis return msgContext.getResponseMessage(); - } catch (Exception ex) { - Message errorMsg; - AxisFault soapFault; - if (ex instanceof AxisFault) { - soapFault = (AxisFault) ex; - - QName faultCode = soapFault.getFaultCode(); - if (Constants.FAULT_SOAP12_SENDER.equals(faultCode)) { - invocationStatusCode = 400; - invocationStatusText = "Bad request"; - } else if ("Server.Unauthorized".equals(faultCode.getLocalPart())) { - invocationStatusCode = 401; - invocationStatusText = "Unauthorized"; - } else { - invocationStatusCode = 500; - invocationStatusText = "Internal server error"; - } - } else { - invocationStatusCode = 500; - invocationStatusText = "Internal server error"; - soapFault = AxisFault.makeFault(ex); - } - - // There may be headers we want to preserve in the - // response message - so if it's there, just add the - // FaultElement to it. Otherwise, make a new one. - errorMsg = msgContext.getResponseMessage(); - errorMsg = faultToMessage(errorMsg, msgContext, soapFault); - throw new SoapException(invocationStatusCode,invocationStatusText,errorMsg); + } catch (Exception ex) { + if (ex instanceof AxisFault) throw new SoapException((AxisFault)ex); + throw new SoapException(500,"Unable to invoke service",ex); } } - protected Message faultToMessage(Message errorMsg, MessageContext msgContext, AxisFault soapFault) { - Message theErrorMsg = errorMsg; - if (theErrorMsg == null) { - theErrorMsg = new Message(soapFault); - theErrorMsg.setMessageContext(msgContext); - } else { - try { - SOAPEnvelope env = theErrorMsg.getSOAPEnvelope(); - env.clearBody(); - env.addBodyElement(new SOAPFault(soapFault)); - } catch (AxisFault fault) { - // Should never reach here! - } - } - return theErrorMsg; - } + /** * This function deplays all java classes that should be available via SOAP call. @@ -636,8 +609,9 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http msgContext.setTargetService(serviceName); return msgContext; - } catch (AxisFault e) { - throw new SoapException(500,"Unable to set the target service",e.getMessage()); + } catch (Exception e) { + if (e instanceof AxisFault) throw new SoapException((AxisFault)e); + throw new SoapException(500,"Unable to generate message context",e); } } @@ -666,79 +640,102 @@ public final class httpdSoapHandler extends httpdAbstractHandler implements http return result; } - protected void sendSoapException(OutputStream out, SoapException e) throws UnsupportedEncodingException, IOException, SOAPException { - Object errorMsg = e.getErrorMsg(); - String contentType = null; - - // getting the error message body and content length - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - if (errorMsg instanceof String) { - bout.write(((String)errorMsg).getBytes("UTF-8")); - contentType = "text/plain; charset=UTF-8"; - } else { - Message soapErrorMsg = (Message)errorMsg; - soapErrorMsg.writeTo(bout); - contentType = soapErrorMsg.getContentType(soapErrorMsg.getMessageContext().getSOAPConstants()); - } - - // send out the message - sendMessage(out, e.getStatusCode(), e.getStatusText(), contentType, bout.toByteArray()); - } +// protected void sendException(Properties conProp, OutputStream out, Exception e) throws UnsupportedEncodingException, IOException, SOAPException { +// +// Message soapErrorMsg = null; +// if (e instanceof SoapException) { +// Object errorMsg = ((SoapException)e).getErrorMsg(); +// String contentType = null; +// +// // getting the error message body and content length +// ByteArrayOutputStream bout = new ByteArrayOutputStream(); +// if (errorMsg instanceof String) { +// bout.write(((String)errorMsg).getBytes("UTF-8")); +// contentType = "text/plain; charset=UTF-8"; +// } else { +// Message soapErrorMsg = (Message)errorMsg; +// soapErrorMsg.writeTo(bout); +// contentType = soapErrorMsg.getContentType(soapErrorMsg.getMessageContext().getSOAPConstants()); +// } +// } +// +// // send out the message +// sendMessage(conProp, out, e.getStatusCode(), e.getStatusText(), contentType, bout.toByteArray()); +// } - protected void sendMessage(OutputStream out, int statusCode, String statusText, String contentType, byte[] MessageBody) throws IOException { + protected void sendMessage(Properties conProp, httpHeader requestHeader, OutputStream out, int statusCode, String statusText, String contentType, byte[] MessageBody) throws IOException { // write out the response header - respondHeader(out, statusCode, statusText, (MessageBody==null)?null:contentType, (MessageBody==null)?-1:MessageBody.length); + respondHeader(conProp, out, statusCode, statusText, (MessageBody==null)?null:contentType, (MessageBody==null)?-1:MessageBody.length, null, null); // write the message body if (MessageBody != null) out.write(MessageBody); out.flush(); } - - protected void sendMessage(OutputStream out, int statusCode, String statusText, Message soapMessage) throws IOException, SoapException { - // getting the content body - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - - try { - soapMessage.writeTo(bout); - } catch (SOAPException e) { - throw new SoapException(500,"Unable to externalize SOAP message",e.getMessage()); - } + protected void sendMessage(Properties conProp, httpHeader requestHeader, OutputStream out, int statusCode, String statusText, Message soapMessage) throws IOException, SOAPException { + httpChunkedOutputStream chunkedOut = null; + GZIPOutputStream gzipOut = null; + OutputStream bodyOut = out; + // getting the content type - String contentType = null; - try { - contentType = soapMessage.getContentType(soapMessage.getMessageContext().getSOAPConstants()); - } catch (AxisFault e) { - throw new SoapException(500,"Unable to get content-type for SOAP message",e.getMessage()); + String contentType = soapMessage.getContentType(soapMessage.getMessageContext().getSOAPConstants()); + + // getting the content length + String transferEncoding = null; + String contentEncoding = null; + long contentLength = -1; + + if (httpHeader.supportChunkedEncoding(conProp)) { + transferEncoding = "chunked"; + } else { + contentLength = soapMessage.getContentLength(); + } + if (requestHeader.acceptGzip()) { + contentEncoding = "gzip"; } + + // sending the soap header + respondHeader(conProp, out, statusCode, statusText, contentType, contentLength, contentEncoding, transferEncoding); + + if (transferEncoding != null) bodyOut = chunkedOut = new httpChunkedOutputStream(bodyOut); + if (contentEncoding != null) bodyOut = gzipOut = new GZIPOutputStream(bodyOut); - // sending the message - sendMessage(out, statusCode, statusText, contentType, bout.toByteArray()); + // sending the body + soapMessage.writeTo(bodyOut); + bodyOut.flush(); + + if (gzipOut != null) { + gzipOut.flush(); + gzipOut.finish(); + } + if (chunkedOut != null) { + chunkedOut.finish(); + } } - /** - * This method was copied from {@link httpdFileHandler}. Maybe it would be a good idea - * to move this function up into {@link httpdAbstractHandler} - * - * @param out the {@link OutputStream} to the client - * @param retcode the http code - * @param conttype the content type and encoding - * @param contlength the content length - * @throws IOException - */ - protected void respondHeader(OutputStream out, int retcode, String returnStatus, String conttype, long contlength) throws IOException { - try { - out.write(("HTTP/1.1 " + retcode + " " + returnStatus + "\r\n").getBytes()); - out.write(("Server: AnomicHTTPD (www.anomic.de)\r\n").getBytes()); - out.write(("Date: " + httpc.dateString(httpc.nowDate()) + "\r\n").getBytes()); - if (conttype != null) out.write((httpHeader.CONTENT_TYPE + ": " + conttype + "\r\n").getBytes()); - if (contlength != -1) out.write((httpHeader.CONTENT_LENGTH + ": " + contlength +"\r\n").getBytes()); - out.write(("\r\n").getBytes()); - out.flush(); - } catch (Exception e) { - // any interruption may be caused be network error or because the user has closed - // the windows during transmission. We simply pass it as IOException - throw new IOException(e.getMessage()); - } + + + protected void respondHeader( + Properties conProp, + OutputStream respond, + int httpStatusCode, + String httpStatusText, + String conttype, + long contlength, + String contentEncoding, + String transferEncoding + ) throws IOException { + httpHeader outgoingHeader = new httpHeader(); + outgoingHeader.put(httpHeader.SERVER,"AnomicHTTPD (www.anomic.de)"); + if (conttype != null) outgoingHeader.put(httpHeader.CONTENT_TYPE,conttype); + if (contlength != -1) outgoingHeader.put(httpHeader.CONTENT_LENGTH, Long.toString(contlength)); + if (contentEncoding != null) outgoingHeader.put(httpHeader.CONTENT_ENCODING, contentEncoding); + if (transferEncoding != null) outgoingHeader.put(httpHeader.TRANSFER_ENCODING, transferEncoding); + + // getting the http version of the soap client + String httpVer = conProp.getProperty(httpHeader.CONNECTION_PROP_HTTP_VER); + + // sending http headers + httpd.sendRespondHeader(conProp,respond,httpVer,httpStatusCode,httpStatusText,outgoingHeader); } } diff --git a/source/de/anomic/soap/services/ShareService.java b/source/de/anomic/soap/services/ShareService.java index f4fe8c000..6bda810c3 100644 --- a/source/de/anomic/soap/services/ShareService.java +++ b/source/de/anomic/soap/services/ShareService.java @@ -158,7 +158,15 @@ public class ShareService extends AbstractService { private File getWorkingFile(File workingDir, String workingFileName) throws AxisFault, IOException { if (workingDir == null) throw new NullPointerException("Working dir is null"); + // getting file-share directory File share = getShareDir(); + + // check filename for illegal characters + if (workingFileName != null) { + if ((workingFileName.indexOf("/") != -1) || (workingFileName.indexOf("/") != -1)) + throw new AxisFault("Filename contains illegal characters."); + } + File workingFile = (workingFileName==null)?workingDir:new File(workingDir, workingFileName); if (workingFile.getAbsolutePath().length() > serverSystem.maxPathLength) diff --git a/test/de/anomic/soap/services/AbstractServiceTest.java b/test/de/anomic/soap/services/AbstractServiceTest.java index 50f2230d3..a2d9d7ce2 100644 --- a/test/de/anomic/soap/services/AbstractServiceTest.java +++ b/test/de/anomic/soap/services/AbstractServiceTest.java @@ -4,16 +4,17 @@ import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.rmi.Remote; +import java.util.Hashtable; import java.util.Properties; import javax.xml.rpc.ServiceException; -import org.apache.axis.client.Stub; - -import yacy.soap.status.StatusService; -import yacy.soap.status.StatusServiceServiceLocator; import junit.framework.TestCase; +import org.apache.axis.MessageContext; +import org.apache.axis.client.Stub; +import org.apache.axis.transport.http.HTTPConstants; + public abstract class AbstractServiceTest extends TestCase { protected static final String SOAP_HEADER_NAMESPACE = "http://http.anomic.de/header"; protected static final String SOAP_HEADER_AUTHORIZATION = "Authorization"; @@ -23,7 +24,7 @@ public abstract class AbstractServiceTest extends TestCase { protected static Remote service; protected void setUp() throws Exception { - if (peerPort == null) this.loadConfigProperties(); + this.loadConfigProperties(); super.setUp(); } @@ -57,6 +58,14 @@ public abstract class AbstractServiceTest extends TestCase { // setting the authentication header ((Stub)service).setHeader(SOAP_HEADER_NAMESPACE,SOAP_HEADER_AUTHORIZATION,authString); + + // configure axis to use HTTP 1.1 + ((Stub)service)._setProperty(MessageContext.HTTP_TRANSPORT_VERSION,HTTPConstants.HEADER_PROTOCOL_V11); + + // configure axis to use chunked transfer encoding + Hashtable userHeaderTable = new Hashtable(); + userHeaderTable.put(HTTPConstants.HEADER_TRANSFER_ENCODING, HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED); + ((Stub)service)._setProperty(HTTPConstants.REQUEST_HEADERS,userHeaderTable); } catch (Exception e) { e.printStackTrace(); } finally { diff --git a/test/de/anomic/soap/services/ServiceTests.java b/test/de/anomic/soap/services/ServiceTests.java new file mode 100644 index 000000000..b53b69349 --- /dev/null +++ b/test/de/anomic/soap/services/ServiceTests.java @@ -0,0 +1,18 @@ +package de.anomic.soap.services; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class ServiceTests { + + public static Test suite() { + TestSuite suite = new TestSuite("Test for de.anomic.soap.services"); + //$JUnit-BEGIN$ + suite.addTestSuite(ShareServiceTest.class); + suite.addTestSuite(StatusServiceTest.class); + suite.addTestSuite(BlacklistServiceTest.class); + //$JUnit-END$ + return suite; + } + +} diff --git a/test/de/anomic/soap/services/ShareServiceTest.java b/test/de/anomic/soap/services/ShareServiceTest.java index 0ae8972ec..0f7dde64f 100644 --- a/test/de/anomic/soap/services/ShareServiceTest.java +++ b/test/de/anomic/soap/services/ShareServiceTest.java @@ -1,7 +1,6 @@ package de.anomic.soap.services; import java.io.IOException; -import java.rmi.RemoteException; import java.util.Date; import javax.activation.DataHandler; @@ -15,10 +14,9 @@ import org.apache.axis.client.Stub; import org.apache.axis.utils.XMLUtils; import org.w3c.dom.Document; -import de.anomic.server.serverFileUtils; - import yacy.soap.share.ShareService; import yacy.soap.share.ShareServiceServiceLocator; +import de.anomic.server.serverFileUtils; public class ShareServiceTest extends AbstractServiceTest {