You are viewing a plain text version of this content. The canonical link for it is here.
Posted to soap-dev@xml.apache.org by sn...@apache.org on 2002/11/12 15:15:38 UTC

cvs commit: xml-soap/java/src/org/apache/soap/transport/http SOAPHTTPConnection.java

snichol     2002/11/12 06:15:38

  Modified:    java/src/org/apache/soap/util/net HTTPUtils.java
               java/src/org/apache/soap/transport TransportMessage.java
               java/src/org/apache/soap/transport/http
                        SOAPHTTPConnection.java
  Log:
  Reduce the number of times a response is copied in part or in whole.
  Improve error reporting during response parsing.
  Support services that shutdown the write half of the socket rather than
  provide a Content-Length header.
  Add getEnvelope to SOAPHTTPConnection.  (The method will also be added
  to SOAPTransport soon.)
  
  Revision  Changes    Path
  1.36      +104 -82   xml-soap/java/src/org/apache/soap/util/net/HTTPUtils.java
  
  Index: HTTPUtils.java
  ===================================================================
  RCS file: /home/cvs/xml-soap/java/src/org/apache/soap/util/net/HTTPUtils.java,v
  retrieving revision 1.35
  retrieving revision 1.36
  diff -u -r1.35 -r1.36
  --- HTTPUtils.java	18 Oct 2002 20:30:54 -0000	1.35
  +++ HTTPUtils.java	12 Nov 2002 14:15:38 -0000	1.36
  @@ -57,21 +57,30 @@
   
   package org.apache.soap.util.net;
   
  -import java.io.*;
  -import java.lang.reflect.*;
  -import java.net.*;
  -import java.util.*;
  -
  -import javax.mail.*;
  -import javax.mail.internet.*;
  -import javax.activation.*;
  +import java.io.BufferedInputStream;
  +import java.io.BufferedOutputStream;
  +import java.io.BufferedReader;
  +import java.io.InputStream;
  +import java.io.IOException;
  +import java.io.OutputStream;
  +import java.io.UnsupportedEncodingException;
  +import java.lang.reflect.InvocationTargetException;
  +import java.lang.reflect.Method;
  +import java.net.HttpURLConnection;
  +import java.net.Socket;
  +import java.net.URL;
  +import java.util.Enumeration;
  +import java.util.Hashtable;
  +import java.util.StringTokenizer;
   
  -import org.apache.soap.*;
  +import javax.mail.MessagingException;
  +
  +import org.apache.soap.Constants;
  +import org.apache.soap.SOAPException;
   import org.apache.soap.encoding.soapenc.Base64;
  -import org.apache.soap.rpc.*;
  -import org.apache.soap.transport.*;
  +import org.apache.soap.rpc.SOAPContext;
  +import org.apache.soap.transport.TransportMessage;
   import org.apache.soap.util.MutableBoolean;
  -import org.apache.soap.util.mime.*;
   
   /**
    * A bunch of utility stuff for doing HTTP things.
  @@ -91,6 +100,7 @@
     private static final String HTTP_VERSION = "1.0";
     private static final int    HTTP_DEFAULT_PORT = 80;
     private static final int    HTTPS_DEFAULT_PORT = 443;
  +  private static final String ISO_8859_1 = "8859_1";
   
     public  static final int    DEFAULT_OUTPUT_BUFFER_SIZE = 8 * 1024;
   
  @@ -100,7 +110,7 @@
     public static String encodeAuth(String userName, String password)
         throws SOAPException {
       try {
  -      return Base64.encode((userName + ":" + password).getBytes("8859_1"));
  +      return Base64.encode((userName + ":" + password).getBytes(ISO_8859_1));
       } catch (UnsupportedEncodingException e) {
         throw new SOAPException (Constants.FAULT_CODE_CLIENT, e.getMessage(), e);
       }
  @@ -482,65 +492,93 @@
         bOutStream.flush();
   
         BufferedInputStream bInStream = new BufferedInputStream(inStream);
  +      byte[] linebuf = new byte[1024];
  +      int count = 0;
  +      int b;
  +
         /* Read the response status line. */
  +      String versionString = null;
         int statusCode = 0;
         String statusString = null;
  -      StringBuffer linebuf = new StringBuffer(128);
  -      int b = 0;
  -      while (b != '\n' && b != -1) {
  -          b = bInStream.read();
  -          if (b != '\n' && b != '\r' && b != -1)
  -              linebuf.append((char)b);
  -      }
  -      String line = linebuf.toString();
  +
         try {
  -          StringTokenizer st = new StringTokenizer(line);
  -          st.nextToken(); // ignore version part
  -          statusCode = Integer.parseInt (st.nextToken());
  -          StringBuffer sb = new StringBuffer(128);
  -          while (st.hasMoreTokens()) {
  -              sb.append (st.nextToken());
  -              if (st.hasMoreTokens()) {
  -                  sb.append(" ");
  +          int versionEnd = -1;
  +          int codeStart = -1;
  +          int codeEnd = -1;
  +          int stringStart = -1;
  +    
  +          for (count = 0, b = bInStream.read(); b != '\n' && b != -1; b = bInStream.read()) {
  +              if (b != '\r') {
  +              	  if (b == ' ') {
  +              	      if (versionEnd == -1) {
  +              	          versionEnd = count;
  +              	      } else if (codeStart != -1 && codeEnd == -1) {
  +              	          codeEnd = count;
  +              	      }
  +              	  } else {
  +              	      if (versionEnd != -1 && codeStart == -1) {
  +              	          codeStart = count;
  +              	      } else if (codeEnd != -1 && stringStart == -1) {
  +              	          stringStart = count;
  +              	      }
  +              	  }
  +                  if (count >= linebuf.length) {
  +                      byte[] newbuf = new byte[linebuf.length * 2];
  +                      System.arraycopy(linebuf, 0, newbuf, 0, linebuf.length);
  +                      linebuf = newbuf;
  +                  }
  +                  linebuf[count++] = (byte) b;
                 }
             }
  -          statusString = sb.toString();
  -      }
  -      catch (Exception e) {
  +          if (b == -1)
  +              throw new Exception("Reached end of stream while reading HTTP response status");
  +          versionString = new String(linebuf, 0, versionEnd, ISO_8859_1);
  +          statusCode = Integer.parseInt(new String(linebuf, codeStart, codeEnd - codeStart, ISO_8859_1));
  +          statusString = new String(linebuf, stringStart, count - stringStart, ISO_8859_1);
  +      } catch (Exception e) {
             throw new SOAPException(Constants.FAULT_CODE_CLIENT,
  -              "Error parsing HTTP status line \"" + line + "\": " + e, e);
  +              "Error parsing HTTP status line \"" + new String(linebuf, 0, count, ISO_8859_1) + "\": " + e, e);
         }
   
  -      /* Read the entire response (following the status line)
  -       * into a byte array. */
  -      ByteArrayDataSource ds = new ByteArrayDataSource(bInStream,
  -                          Constants.HEADERVAL_DEFAULT_CHARSET);
  -
  -      /* Extract the headers, content type and content length. */
  -      byte[] bytes = ds.toByteArray();
  +      /* Read the HTTP headers. */
         Hashtable respHeaders = new Hashtable();
         int respContentLength = -1;
         String respContentType = null;
   
  -      int nameStart = 0;
  -      int nameEnd = 0;
  -      int valStart = 0;
  -      boolean parsingName = true;
  -      int offset;
  -
  -      for (offset = 0; offset < bytes.length; offset++) {
  -          if (bytes[offset] == '\n') {
  -              if (nameStart >= nameEnd)
  +      try {
  +          // Read all headers
  +          for (;;) {
  +              // Read and parse one header
  +              int nameEnd = -1;
  +              int valStart = -1;
  +              for (count = 0, b = bInStream.read(); b != '\n' && b != -1; b = bInStream.read()) {
  +                  if (b != '\r') {
  +                      if (nameEnd == -1 && b == ':') {
  +                          nameEnd = count;
  +                      } else if (nameEnd != -1 && valStart == -1 && b != ' ' & b != '\t') {
  +                          valStart = count;
  +                      }
  +                      if (count >= linebuf.length) {
  +                          byte[] newbuf = new byte[linebuf.length * 2];
  +                          System.arraycopy(linebuf, 0, newbuf, 0, linebuf.length);
  +                          linebuf = newbuf;
  +                      }
  +                      linebuf[count++] = (byte) b;
  +                  }
  +              }
  +              if (b == -1)
  +                  throw new Exception("Reached end of stream while reading HTTP response header");
  +              if (count == 0)   // Read the header/entity separator
                     break;
  -              String name = new String(bytes, nameStart, nameEnd-nameStart+1);
  -
  -              // Remove trailing ; to prevent ContextType from throwing exception
  -              int valueLen = offset - valStart -1;
  +              if (nameEnd == -1 || valStart == -1)
  +                  throw new Exception("Incorrectly formed HTTP response header");
   
  -              if (valueLen > 0 && bytes[offset-1] == ';')
  -                  valueLen--;
  +              String name = new String(linebuf, 0, nameEnd, ISO_8859_1);
  +              // Remove trailing ; to prevent ContentType from throwing exception
  +              if (linebuf[count - 1] == ';')
  +                  --count;
  +              String value = new String(linebuf, valStart, count - valStart, ISO_8859_1);
   
  -              String value = new String(bytes, valStart, valueLen);
                 if (name.equalsIgnoreCase(Constants.HEADER_CONTENT_LENGTH))
                     respContentLength = Integer.parseInt(value);
                 else if (name.equalsIgnoreCase(Constants.HEADER_CONTENT_TYPE))
  @@ -556,27 +594,11 @@
                         }
                     }
                 }
  -              parsingName = true;
  -              nameStart = offset+1;
             }
  -          else if (bytes[offset] != '\r') {
  -              if (parsingName) {
  -                  if (bytes[offset] == ':') {
  -                      parsingName = false;
  -                      nameEnd = offset - 1;
  -                      if ((offset != bytes.length-1) &&
  -                          bytes[offset+1] == ' ')
  -                        offset++;
  -                      valStart = offset+1;
  -                  }
  -              }
  -          }
  -      } // End of for
  -
  -      InputStream is = ds.getInputStream();
  -      is.skip(offset + 1);
  -      if (respContentLength < 0)
  -          respContentLength = ds.getSize() - offset - 1;
  +      } catch (Exception e) {
  +          throw new SOAPException(Constants.FAULT_CODE_CLIENT,
  +              "Error parsing HTTP header line \"" + new String(linebuf, 0, count, ISO_8859_1) + "\": " + e, e);
  +      }
   
         /* Handle redirect here */
         if (statusCode >= HttpURLConnection.HTTP_MULT_CHOICE &&
  @@ -595,11 +617,6 @@
             }
         }
   
  -      /* If required, capture a copy of the response. */
  -      if (responseCopy != null) {
  -        responseCopy.append(line).append("\r\n").append(new String(bytes));  /* May get junk due to actual encoding */
  -      }
  -
         // TODO: process differently depending on statusCode and respContentLength
         //       (TransportMessage does not even get statusCode)
         // e.g. statusCode 401 is Unauthorized
  @@ -612,13 +629,18 @@
             // Create response SOAPContext.
             ctx = new SOAPContext();
             // Read content.
  -          response = new TransportMessage(is, respContentLength,
  +          response = new TransportMessage(bInStream, respContentLength,
                                             respContentType, ctx, respHeaders);
             // Extract envelope and SOAPContext
             response.read();
         } catch (MessagingException me) {
             throw new SOAPException(Constants.FAULT_CODE_CLIENT,
                                     "Error parsing response: " + me, me);
  +      }
  +
  +      /* If required, capture a copy of the response. */
  +      if (responseCopy != null) {
  +        responseCopy.append(new String(response.getBytes()));  /* May get junk due to actual encoding */
         }
   
         /* All done here! */
  
  
  
  1.17      +51 -37    xml-soap/java/src/org/apache/soap/transport/TransportMessage.java
  
  Index: TransportMessage.java
  ===================================================================
  RCS file: /home/cvs/xml-soap/java/src/org/apache/soap/transport/TransportMessage.java,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- TransportMessage.java	6 Sep 2002 17:02:58 -0000	1.16
  +++ TransportMessage.java	12 Nov 2002 14:15:38 -0000	1.17
  @@ -128,25 +128,36 @@
           this.ctx = ctx;
           this.contentType = contentType;
   
  -        if (contentLength < 0)
  -            throw new SOAPException (Constants.FAULT_CODE_PROTOCOL,
  -                                     "Content length must be specified.");
  -
  -        bytes = new byte[contentLength];
  -        int offset = 0;
  -        int bytesRead = 0;
  -
  -        // We're done reading when we get all the content OR when the stream
  -        // returns a -1.
  -        while ((offset < contentLength) && (bytesRead >= 0)) {
  -            bytesRead = is.read(bytes, offset, contentLength - offset);
  -            offset += bytesRead;
  -        }
  -        if (offset < contentLength)
  -            throw new SOAPException (Constants.FAULT_CODE_PROTOCOL,
  -                       "Premature end of stream. Data is truncated. Read "
  -                       + offset + " bytes successfully, expected "
  -                       + contentLength);
  +        bytes = new byte[contentLength >= 0 ? contentLength : 4096];
  +        if (contentLength != 0) {
  +            int offset = 0;
  +            int bytesRead = 0;
  +    
  +            // We're done reading when we get all the content OR when the stream
  +            // returns a -1.
  +            while ((contentLength < 0 || offset < contentLength) && (bytesRead >= 0)) {
  +                bytesRead = is.read(bytes, offset, bytes.length - offset);
  +                offset += bytesRead;
  +                if (contentLength < 0 && offset >= bytes.length) {
  +                    byte[] newbuf = new byte[bytes.length * 2];
  +                    System.arraycopy(bytes, 0, newbuf, 0, bytes.length);
  +                    bytes = newbuf;
  +                }
  +            }
  +
  +            if (contentLength < 0) {
  +                if (offset < bytes.length) {
  +                    byte[] newbuf = new byte[offset];
  +                    System.arraycopy(bytes, 0, newbuf, 0, offset);
  +                    bytes = newbuf;
  +                }
  +            } else if (offset < contentLength) {
  +                throw new SOAPException (Constants.FAULT_CODE_PROTOCOL,
  +                           "Premature end of stream. Data is truncated. Read "
  +                           + offset + " bytes successfully, expected "
  +                           + contentLength);
  +            }
  +        }
       }
   
       /**
  @@ -284,7 +295,7 @@
           }
   
           // If the root part is text, extract it as a String.
  -        // Note that we could use JAF's help to do this (see getEnvelope())
  +        // Note that we could use JAF's help to do this (see save())
           // but implementing it ourselves is safer and faster.
           if (rootContentType.match("text/*")) {
               String charset = rootContentType.getParameter("charset");
  @@ -323,9 +334,11 @@
        */
       public void save()
           throws MessagingException, IOException {
  -        /* If an envelope was provided as a string, set it as the root part.
  -         * Otherwise, assume that the SOAPContext already has a root part.
  +        /*
  +         * If an envelope was provided as a string, set it as the root part.
            * If there was already a root part, preserve its content-type.
  +         * Otherwise, assume that the SOAPContext already has a root part,
  +         * and try to use it as the envelope.
            */
           String rootContentType = null;
           if (ctx.isRootPartSet()) {
  @@ -339,8 +352,18 @@
           }
           if (rootContentType == null)
               rootContentType = Constants.HEADERVAL_CONTENT_TYPE_UTF8;
  -        if (getEnvelope() != null)
  +        if (getEnvelope() != null) {
               ctx.setRootPart(envelope, rootContentType);
  +        } else {
  +            MimeBodyPart rootPart = ctx.getRootPart();
  +            if (rootPart != null) {
  +                if (rootPart.isMimeType("text/*")) {
  +                    ByteArrayDataSource ds = new ByteArrayDataSource(
  +                        rootPart.getInputStream(), rootPart.getContentType());
  +                    envelope = ds.getText();
  +                }
  +            }
  +        }
   
           // Print the whole response to a byte array.
           ByteArrayOutputStream payload =
  @@ -428,20 +451,9 @@
       }
   
       /**
  -     * Get SOAP Envelope/root part as a String.
  -     * This method will extract the root part from the SOAPContext as a String
  -     * if there is no SOAP Envelope.
  +     * Get SOAP Envelope as a String.
        */
  -    public String getEnvelope() throws MessagingException, IOException {
  -        if (envelope == null) {
  -            MimeBodyPart rootPart = ctx.getRootPart();
  -            if (rootPart != null)
  -                if (rootPart.isMimeType("text/*")) {
  -                    ByteArrayDataSource ds = new ByteArrayDataSource(
  -                        rootPart.getInputStream(), rootPart.getContentType());
  -                    envelope = ds.getText();
  -            }
  -        }
  +    public String getEnvelope() {
           return envelope;
       }
   
  @@ -449,7 +461,7 @@
        * Get SOAP Envelope/root part as a Reader. Returns null if the root part
        * is not text.
        */
  -    public Reader getEnvelopeReader() throws MessagingException, IOException {
  +    public Reader getEnvelopeReader() {
           if (getEnvelope() == null)
               return null;
           else
  @@ -530,6 +542,8 @@
   
       /**
        * Set the byte array of the response.
  +     *
  +     * @deprecated After 2.3.1
        */
       public void readFully(InputStream is) throws IOException {
           offset = 0;
  
  
  
  1.29      +19 -16    xml-soap/java/src/org/apache/soap/transport/http/SOAPHTTPConnection.java
  
  Index: SOAPHTTPConnection.java
  ===================================================================
  RCS file: /home/cvs/xml-soap/java/src/org/apache/soap/transport/http/SOAPHTTPConnection.java,v
  retrieving revision 1.28
  retrieving revision 1.29
  diff -u -r1.28 -r1.29
  --- SOAPHTTPConnection.java	16 Oct 2002 04:16:15 -0000	1.28
  +++ SOAPHTTPConnection.java	12 Nov 2002 14:15:38 -0000	1.29
  @@ -84,9 +84,7 @@
    * @author Arek Wnukowski (apache@wnuko.demon.co.uk)
    */
   public class SOAPHTTPConnection implements SOAPTransport {
  -  private BufferedReader responseReader;
  -  private Hashtable responseHeaders;
  -  private SOAPContext responseSOAPContext;
  +  private TransportMessage response;
   
     private String httpProxyHost;
     private int    httpProxyPort = 80;
  @@ -369,7 +367,6 @@
                       "Basic " + HTTPUtils.encodeAuth(proxyUserName, proxyPassword));
         }
   
  -      TransportMessage response;
         try
         {
           TransportMessage msg = new TransportMessage(payload, ctx, headers);
  @@ -385,18 +382,12 @@
           throw new IOException ("Failed to encode mime multipart: " + uee);
         }
   
  -      Reader envReader = response.getEnvelopeReader();
  -      if (envReader != null)
  -        responseReader = new BufferedReader(envReader);
  -      else
  -        responseReader = null;
  -      responseSOAPContext = response.getSOAPContext();
  -      responseHeaders = response.getHeaders();
         if (maintainSession) {
           // look for Set-Cookie2 and Set-Cookie headers and save them.
           // Only update my state iff the header is there .. otherwise
           // leave the current 
           // Note: Header is case-insensitive
  +        Hashtable responseHeaders = response.getHeaders();
           String hdr;
   
           hdr = HTTPUtils.getHeaderValue (responseHeaders, "Set-Cookie2");
  @@ -421,8 +412,6 @@
         }
       } catch (IllegalArgumentException e) {
         throw new SOAPException (Constants.FAULT_CODE_CLIENT, e.getMessage(), e);
  -    } catch (MessagingException e) {
  -      throw new SOAPException (Constants.FAULT_CODE_CLIENT, e.getMessage(), e);
       } catch (IOException e) {
         throw new SOAPException (Constants.FAULT_CODE_CLIENT, e.getMessage(), e);
       }
  @@ -436,7 +425,21 @@
      *         possible.
      */
     public BufferedReader receive () {
  -    return responseReader;
  +    if (response != null) {
  +      Reader envReader = response.getEnvelopeReader();
  +      if (envReader != null)
  +        return new BufferedReader(envReader);
  +    }
  +    return null;
  +  }
  +
  +  /**
  +   * Returns the SOAP envelope.
  +   *
  +   * @return The SOAP envelope.
  +   */
  +  public String getEnvelope() {
  +    return response != null ? response.getEnvelope() : null;
     }
   
     /**
  @@ -445,7 +448,7 @@
      * @return a hashtable containing all the headers
      */
     public Hashtable getHeaders () {
  -    return responseHeaders;
  +    return response != null ? response.getHeaders() : null;
     }
   
     /**
  @@ -454,6 +457,6 @@
      * @return response SOAPContext
      */
     public SOAPContext getResponseSOAPContext () {
  -    return responseSOAPContext;
  +    return response != null ? response.getSOAPContext() : null;
     }
   }
  
  
  

--
To unsubscribe, e-mail:   <ma...@xml.apache.org>
For additional commands, e-mail: <ma...@xml.apache.org>