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 Anver Sotnikov <AS...@intralinks.com> on 2001/05/15 17:28:28 UTC

Huge attachments

I thought this change can be done and it will improve performance and 
memory utilization in SOAP.

I went through org.apache.soap.util.net.HTTPUtil code. and realized that 
it reads whole response in memory just to read headers from InputStream. 
May be it will be much better for performance to read headers directly 
from InputStream and pass the rest of the Stream to TransportMessage?
If you need concrete implementation I can send my code which replaces 
current.

  public static TransportMessage post(URL url, TransportMessage request,
                                      int timeout,
                                      String httpProxyHost, int 
httpProxyPort)
      throws IllegalArgumentException, IOException, SOAPException {

// This part sends request to HTTP server ( skip reading to next bold 
comment)

      /* Open the connection */
      OutputStream outStream = null;
      InputStream inStream = null;
      BufferedReader in = null;
      int port;
      Socket s;
      try {
          port = getPort(url);

          s = buildSocket(url, port, httpProxyHost, httpProxyPort);

          if (timeout > 0)  // Should be redundant but not every JVM likes 
this
              s.setSoTimeout(timeout);

          outStream = s.getOutputStream ();
          inStream = s.getInputStream ();
      }
      catch (Exception e) {
          e.printStackTrace();  // DEBUG - remove me
          throw new IllegalArgumentException("Error opening socket: " +
                                             e.getMessage ());
      }

      /* Construct the HTTP header. */
      StringBuffer headerbuf = new StringBuffer();
      headerbuf.append(Constants.HEADER_POST).append(' ')
          .append(httpProxyHost == null ? url.getFile() : url.toString())
          .append(" HTTP/").append(HTTP_VERSION).append("\r\n")
          .append(Constants.HEADER_HOST).append(": 
").append(url.getHost())
          .append(':').append(port).append("\r\n")
          .append(Constants.HEADER_CONTENT_TYPE).append(": ")
          .append(request.getContentType()).append("\r\n")
          .append(Constants.HEADER_CONTENT_LENGTH).append(": ")
          .append(request.getContentLength()).append("\r\n");

      for (Enumeration e = request.getHeaderNames(); e.hasMoreElements(); 
) {
          Object key = e.nextElement();
          headerbuf.append(key).append(": ")
              .append(request.getHeader((String)key)).append("\r\n");
      }
      headerbuf.append("\r\n");

      /* Send the request. */
      BufferedOutputStream bOutStream = new 
BufferedOutputStream(outStream);
      bOutStream.write(
 headerbuf.toString().getBytes(Constants.HEADERVAL_DEFAULT_CHARSET));
      request.writeTo(bOutStream);
      //bOutStream.write('\r'); bOutStream.write('\n');
      //bOutStream.write('\r'); bOutStream.write('\n');
      bOutStream.flush();
      outStream.flush();

// This part reads response from Socket

      BufferedInputStream bInStream = new BufferedInputStream(inStream);
      /* Read the response status line. */
      int statusCode = 0;
      String statusString = null;
      StringBuffer linebuf = new StringBuffer();
      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();
          while (st.hasMoreTokens()) {
              sb.append (st.nextToken());
              if (st.hasMoreTokens()) {
                  sb.append(" ");
              }
          }
          statusString = sb.toString();
      }
      catch (Exception e) {
          throw new IllegalArgumentException(
              "Error parsing HTTP status line \"" + line + "\": " + e);
      }

//      Here comes a Santa Claus :o)))
//      Next part can be easily replaced by direct reading from 
BufferedInputStream.

      /* 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();
      Hashtable respHeaders = new Hashtable();
      int respContentLength = -1;
      String respContentType = null;
      StringBuffer namebuf = new StringBuffer();
      StringBuffer valuebuf = new StringBuffer();
      boolean parsingName = true;
      int offset;
      for (offset = 0; offset < bytes.length; offset++) {
          if (bytes[offset] == '\n') {
              if (namebuf.length() == 0)
                  break;
              String name = namebuf.toString();

              // Remove trailing ; to prevent ContextType from throwing 
exception
              if (valuebuf.charAt(valuebuf.length()-1) == ';') {
                  valuebuf.deleteCharAt(valuebuf.length()-1);
              }

              String value = valuebuf.toString();
              if (name.equalsIgnoreCase(Constants.HEADER_CONTENT_LENGTH))
                  respContentLength = Integer.parseInt(value);
              else if 
(name.equalsIgnoreCase(Constants.HEADER_CONTENT_TYPE))
                  respContentType = value;
              else
                  respHeaders.put(name, value);
              namebuf = new StringBuffer();
              valuebuf = new StringBuffer();
              parsingName = true;
          }
          else if (bytes[offset] != '\r') {
              if (parsingName) {
                  if (bytes[offset] == ':') {
                      parsingName = false;
                      if ((offset != bytes.length-1) &&
                          bytes[offset+1] == ' ')
                        offset++;
                  }
                  else
                      namebuf.append((char)bytes[offset]);
              }
              else
                  valuebuf.append((char)bytes[offset]);
          }
      }
//      Headers can be processed without reading everything in memory
//      Check out InternetHeaders in javax.mail.internet.
//      After reading headers we can just pass same input stream to 
Transport Message
//      This we do one reading of Mesage in memory less :o) 

      InputStream is = ds.getInputStream();
      is.skip(offset + 1);
      if (respContentLength < 0)
          respContentLength = ds.getSize() - offset - 1;

      /* Construct the response object. */
      SOAPContext ctx;
      TransportMessage response;
      try {
          // Create response SOAPContext.
          ctx = new SOAPContext();
          // Read content.
          response = new TransportMessage(is, respContentLength,
                                          respContentType, ctx, 
respHeaders);
          // Extract envelope and SOAPContext
          response.read();
      } catch (MessagingException me) {
          throw new IllegalArgumentException("Error parsing response: " + 
me);
      }

      /* All done here! */
      bOutStream.close();
      outStream.close();
      bInStream.close();
      inStream.close();
      s.close();
      return response;
  }