Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/axis/transport/http/HTTPSender.java


1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.apache.axis.transport.http;
17  
18  import org.apache.axis.AxisFault;
19  import org.apache.axis.Message;
20  import org.apache.axis.MessageContext;
21  import org.apache.axis.Constants;
22  import org.apache.axis.components.logger.LogFactory;
23  import org.apache.axis.components.net.BooleanHolder;
24  import org.apache.axis.components.net.SocketFactory;
25  import org.apache.axis.components.net.SocketFactoryFactory;
26  import org.apache.axis.components.net.DefaultSocketFactory;
27  import org.apache.axis.encoding.Base64;
28  import org.apache.axis.handlers.BasicHandler;
29  import org.apache.axis.soap.SOAP12Constants;
30  import org.apache.axis.soap.SOAPConstants;
31  import org.apache.axis.utils.Messages;
32  import org.apache.axis.utils.TeeOutputStream;
33  import org.apache.commons.logging.Log;
34  
35  import javax.xml.soap.MimeHeader;
36  import javax.xml.soap.MimeHeaders;
37  import javax.xml.soap.SOAPException;
38  import java.io.BufferedInputStream;
39  import java.io.BufferedOutputStream;
40  import java.io.ByteArrayOutputStream;
41  import java.io.IOException;
42  import java.io.InputStream;
43  import java.io.OutputStream;
44  import java.net.Socket;
45  import java.net.URL;
46  import java.util.Enumeration;
47  import java.util.Hashtable;
48  import java.util.Iterator;
49  import java.util.ArrayList;
50  import java.util.Collection;
51  
52  /**
53   * This is meant to be used on a SOAP Client to call a SOAP server.
54   *
55   * @author Doug Davis (dug@us.ibm.com)
56   * @author Davanum Srinivas (dims@yahoo.com)
57   */
58  public class HTTPSender extends BasicHandler {
59  
60      protected static Log log = LogFactory.getLog(HTTPSender.class.getName());
61  
62      private static final String ACCEPT_HEADERS = 
63          HTTPConstants.HEADER_ACCEPT + //Limit to the types that are meaningful to us.
64          ": " +
65          HTTPConstants.HEADER_ACCEPT_APPL_SOAP +
66          ", " +
67          HTTPConstants.HEADER_ACCEPT_APPLICATION_DIME +
68          ", " +
69          HTTPConstants.HEADER_ACCEPT_MULTIPART_RELATED +
70          ", " +
71          HTTPConstants.HEADER_ACCEPT_TEXT_ALL +
72          "\r\n" +
73          HTTPConstants.HEADER_USER_AGENT +   //Tell who we are.
74          ": " +
75          Messages.getMessage("axisUserAgent") +
76          "\r\n";
77  
78      private static final String CACHE_HEADERS = 
79          HTTPConstants.HEADER_CACHE_CONTROL +   //Stop caching proxies from caching SOAP reqeuest.
80          ": " +
81          HTTPConstants.HEADER_CACHE_CONTROL_NOCACHE +
82          "\r\n" +
83          HTTPConstants.HEADER_PRAGMA +
84          ": " +
85          HTTPConstants.HEADER_CACHE_CONTROL_NOCACHE +
86          "\r\n";
87  
88      private static final String CHUNKED_HEADER =
89          HTTPConstants.HEADER_TRANSFER_ENCODING +
90          ": " +
91          HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED +
92          "\r\n";
93  
94      private static final String HEADER_CONTENT_TYPE_LC =
95          HTTPConstants.HEADER_CONTENT_TYPE.toLowerCase();
96      
97      private static final String HEADER_LOCATION_LC = 
98          HTTPConstants.HEADER_LOCATION.toLowerCase();
99      
100     private static final String HEADER_CONTENT_LOCATION_LC = 
101         HTTPConstants.HEADER_CONTENT_LOCATION.toLowerCase();
102     
103     private static final String HEADER_CONTENT_LENGTH_LC = 
104         HTTPConstants.HEADER_CONTENT_LENGTH.toLowerCase();
105     
106     private static final String HEADER_TRANSFER_ENCODING_LC = 
107         HTTPConstants.HEADER_TRANSFER_ENCODING.toLowerCase();
108 
109     /**
110      * the url; used for error reporting
111      */
112     URL targetURL;
113     
114     /**
115      * invoke creates a socket connection, sends the request SOAP message and then
116      * reads the response SOAP message back from the SOAP server
117      *
118      * @param msgContext the messsage context
119      *
120      * @throws AxisFault
121      */
122     public void invoke(MessageContext msgContext) throws AxisFault {
123 
124         if (log.isDebugEnabled()) {
125             log.debug(Messages.getMessage("enter00", "HTTPSender::invoke"));
126         }
127 
128         SocketHolder socketHolder = new SocketHolder(null);
129 
130         try {
131             BooleanHolder useFullURL = new BooleanHolder(false);
132             StringBuffer otherHeaders = new StringBuffer();
133             targetURL = new URL(msgContext.getStrProp(MessageContext.TRANS_URL));
134             String host = targetURL.getHost();
135             int port = targetURL.getPort();
136             
137             // Send the SOAP request to the server
138             InputStream inp = writeToSocket(socketHolder, msgContext, targetURL,
139                         otherHeaders, host, port, msgContext.getTimeout(), useFullURL);
140 
141             // Read the response back from the server
142             Hashtable headers = new Hashtable();
143             inp = readHeadersFromSocket(socketHolder, msgContext, inp, headers);
144             readFromSocket(socketHolder, msgContext, inp, headers);
145         } catch (Exception e) {
146             log.debug(e);
147             try {
148               if (socketHolder.getSocket() != null ) {
149                 socketHolder.getSocket().close();
150               }
151             } catch (IOException ie) {
152               // we shouldn't get here.
153             }
154             throw AxisFault.makeFault(e);
155         }
156         if (log.isDebugEnabled()) {
157             log.debug(Messages.getMessage("exit00",
158                     "HTTPDispatchHandler::invoke"));
159         }
160     }
161 
162     /**
163      * Creates a socket connection to the SOAP server
164      *
165      * @param protocol "http" for standard, "https" for ssl.
166      * @param host host name
167      * @param port port to connect to
168      * @param otherHeaders buffer for storing additional headers that need to be sent
169      * @param useFullURL flag to indicate if the complete URL has to be sent
170      *
171      * @throws IOException
172      */
173     protected void getSocket(SocketHolder sockHolder,
174                              MessageContext msgContext,
175                              String protocol,
176                              String host, int port, int timeout, 
177                              StringBuffer otherHeaders, 
178                              BooleanHolder useFullURL)
179         throws Exception {
180         Hashtable options = getOptions();
181         if(timeout > 0) {
182             if(options == null) {
183                 options = new Hashtable();
184             }
185             options.put(DefaultSocketFactory.CONNECT_TIMEOUT,Integer.toString(timeout));
186         }
187         SocketFactory factory = SocketFactoryFactory.getFactory(protocol, options);
188         if (factory == null) {
189             throw new IOException(Messages.getMessage("noSocketFactory", protocol));
190         }
191         Socket sock = factory.create(host, port, otherHeaders, useFullURL);
192         if(timeout > 0) {
193             sock.setSoTimeout(timeout);
194         }
195         sockHolder.setSocket(sock);
196     }
197 
198     /**
199      * Send the soap request message to the server
200      *
201      * @param msgContext message context
202      * @param tmpURL url to connect to
203      * @param otherHeaders other headers if any
204      * @param host host name
205      * @param port port
206      * @param useFullURL flag to indicate if the whole url needs to be sent
207      *
208      * @throws IOException
209      */
210     private InputStream writeToSocket(SocketHolder sockHolder,
211             MessageContext msgContext, URL tmpURL,
212             StringBuffer otherHeaders, String host, int port, int timeout,
213             BooleanHolder useFullURL)
214             throws Exception {
215 
216         String userID = msgContext.getUsername();
217         String passwd = msgContext.getPassword();
218 
219         // Get SOAPAction, default to ""
220         String action = msgContext.useSOAPAction()
221                 ? msgContext.getSOAPActionURI()
222                 : "";
223 
224         if (action == null) {
225             action = "";
226         }
227 
228         // if UserID is not part of the context, but is in the URL, use
229         // the one in the URL.
230         if ((userID == null) && (tmpURL.getUserInfo() != null)) {
231             String info = tmpURL.getUserInfo();
232             int sep = info.indexOf(':');
233 
234             if ((sep >= 0) && (sep + 1 < info.length())) {
235                 userID = info.substring(0, sep);
236                 passwd = info.substring(sep + 1);
237             } else {
238                 userID = info;
239             }
240         }
241         if (userID != null) {
242             StringBuffer tmpBuf = new StringBuffer();
243 
244             tmpBuf.append(userID).append(":").append((passwd == null)
245                     ? ""
246                     : passwd);
247             otherHeaders.append(HTTPConstants.HEADER_AUTHORIZATION)
248                     .append(": Basic ")
249                     .append(Base64.encode(tmpBuf.toString().getBytes()))
250                     .append("\r\n");
251         }
252 
253         // don't forget the cookies!
254         // mmm... cookies
255         if (msgContext.getMaintainSession()) {
256             fillHeaders(msgContext, HTTPConstants.HEADER_COOKIE, otherHeaders);
257             fillHeaders(msgContext, HTTPConstants.HEADER_COOKIE2, otherHeaders);
258         }
259 
260         StringBuffer header2 = new StringBuffer();
261 
262         String webMethod = null;
263         boolean posting = true;
264 
265         Message reqMessage = msgContext.getRequestMessage();
266 
267         boolean http10 = true; //True if this is to use HTTP 1.0 / false HTTP 1.1
268         boolean httpChunkStream = false; //Use HTTP chunking or not.
269         boolean httpContinueExpected = false; //Under HTTP 1.1 if false you *MAY* need to wait for a 100 rc,
270                                               //  if true the server MUST reply with 100 continue.
271         String httpConnection = null;
272 
273         String httpver = msgContext.getStrProp(MessageContext.HTTP_TRANSPORT_VERSION);
274         if (null == httpver) {
275             httpver = HTTPConstants.HEADER_PROTOCOL_V10;
276         }
277         httpver = httpver.trim();
278         if (httpver.equals(HTTPConstants.HEADER_PROTOCOL_V11)) {
279             http10 = false;
280         }
281 
282         //process user defined headers for information.
283         Hashtable userHeaderTable = (Hashtable) msgContext.
284                 getProperty(HTTPConstants.REQUEST_HEADERS);
285 
286         if (userHeaderTable != null) {
287             if (null == otherHeaders) {
288                 otherHeaders = new StringBuffer(1024);
289             }
290 
291             for (java.util.Iterator e = userHeaderTable.entrySet().iterator();
292                  e.hasNext();) {
293 
294                 java.util.Map.Entry me = (java.util.Map.Entry) e.next();
295                 Object keyObj = me.getKey();
296                 if (null == keyObj) continue;
297                 String key = keyObj.toString().trim();
298 
299                 if (key.equalsIgnoreCase(HTTPConstants.HEADER_TRANSFER_ENCODING)) {
300                     if (!http10) {
301                         String val = me.getValue().toString();
302                         if (null != val && val.trim().equalsIgnoreCase(HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED))
303                             httpChunkStream = true;
304                     }
305                 } else if (key.equalsIgnoreCase(HTTPConstants.HEADER_CONNECTION)) {
306                     if (!http10) {
307                         String val = me.getValue().toString();
308                         if (val.trim().equalsIgnoreCase(HTTPConstants.HEADER_CONNECTION_CLOSE))
309                             httpConnection = HTTPConstants.HEADER_CONNECTION_CLOSE;
310                     }
311                     //HTTP 1.0 will always close.
312                     //HTTP 1.1 will use persistent. //no need to specify
313                 } else {
314                     if( !http10 && key.equalsIgnoreCase(HTTPConstants.HEADER_EXPECT)) {
315                         String val = me.getValue().toString();
316                         if (null != val && val.trim().equalsIgnoreCase(HTTPConstants.HEADER_EXPECT_100_Continue))
317                             httpContinueExpected = true;
318                     }
319 
320                     otherHeaders.append(key).append(": ").append(me.getValue()).append("\r\n");
321                 }
322             }
323         }
324 
325         if (!http10) {
326             //Force close for now.
327             //TODO HTTP/1.1
328             httpConnection = HTTPConstants.HEADER_CONNECTION_CLOSE;
329         }
330 
331         header2.append(" ");
332         header2.append(http10 ? HTTPConstants.HEADER_PROTOCOL_10 :
333                 HTTPConstants.HEADER_PROTOCOL_11)
334                 .append("\r\n");
335         MimeHeaders mimeHeaders = reqMessage.getMimeHeaders();
336 
337         if (posting) {
338             String contentType;
339             final String[] header = mimeHeaders.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
340             if (header != null && header.length > 0) {
341                 contentType = mimeHeaders.getHeader(HTTPConstants.HEADER_CONTENT_TYPE)[0];
342             } else {
343                 contentType = reqMessage.getContentType(msgContext.getSOAPConstants());
344             }
345             
346             //fix for AXIS-2027
347             if (contentType == null || contentType.equals("")) {
348               throw new Exception(Messages.getMessage("missingContentType"));
349             }
350             header2.append(HTTPConstants.HEADER_CONTENT_TYPE)
351                     .append(": ")
352                     .append(contentType)
353                     .append("\r\n");
354         }
355 
356         header2.append(ACCEPT_HEADERS)
357                 .append(HTTPConstants.HEADER_HOST)  //used for virtual connections
358                 .append(": ")
359                 .append(host)
360                 .append((port == -1)?(""):(":" + port))
361                 .append("\r\n")
362                 .append(CACHE_HEADERS)
363                 .append(HTTPConstants.HEADER_SOAP_ACTION)  //The SOAP action.
364                 .append(": \"")
365                 .append(action)
366                 .append("\"\r\n");
367 
368         if (posting) {
369             if (!httpChunkStream) {
370                 //Content length MUST be sent on HTTP 1.0 requests.
371                 header2.append(HTTPConstants.HEADER_CONTENT_LENGTH)
372                         .append(": ")
373                         .append(reqMessage.getContentLength())
374                         .append("\r\n");
375             } else {
376                 //Do http chunking.
377                 header2.append(CHUNKED_HEADER);
378             }
379         }
380 
381         // Transfer MIME headers of SOAPMessage to HTTP headers. 
382         if (mimeHeaders != null) {
383             for (Iterator i = mimeHeaders.getAllHeaders(); i.hasNext(); ) {
384                 MimeHeader mimeHeader = (MimeHeader) i.next();
385                 String headerName = mimeHeader.getName();
386                 if (headerName.equals(HTTPConstants.HEADER_CONTENT_TYPE)
387                         || headerName.equals(HTTPConstants.HEADER_SOAP_ACTION)) {
388                         continue;
389                 }
390                 header2.append(mimeHeader.getName())
391                 .append(": ")
392                 .append(mimeHeader.getValue())
393                 .append("\r\n");
394             }
395         }
396 
397         if (null != httpConnection) {
398             header2.append(HTTPConstants.HEADER_CONNECTION);
399             header2.append(": ");
400             header2.append(httpConnection);
401             header2.append("\r\n");
402         }
403 
404         getSocket(sockHolder, msgContext, targetURL.getProtocol(),
405                   host, port, timeout, otherHeaders, useFullURL);
406         
407         if (null != otherHeaders) {
408             //Add other headers to the end.
409             //for pre java1.4 support, we have to turn the string buffer argument into
410             //a string before appending.
411             header2.append(otherHeaders.toString());
412         }
413 
414         header2.append("\r\n"); //The empty line to start the BODY.
415 
416         StringBuffer header = new StringBuffer();
417         
418         // If we're SOAP 1.2, allow the web method to be set from the
419         // MessageContext.
420         if (msgContext.getSOAPConstants() == SOAPConstants.SOAP12_CONSTANTS) {
421             webMethod = msgContext.getStrProp(SOAP12Constants.PROP_WEBMETHOD);
422         }
423         if (webMethod == null) {
424             webMethod = HTTPConstants.HEADER_POST;
425         } else {
426             posting = webMethod.equals(HTTPConstants.HEADER_POST);
427         }
428 
429         header.append(webMethod).append(" ");
430         if (useFullURL.value) {
431             header.append(tmpURL.toExternalForm());
432         } else {
433             header.append((((tmpURL.getFile() == null)
434                     || tmpURL.getFile().equals(""))
435                     ? "/"
436                     : tmpURL.getFile()));
437         }
438         header.append(header2.toString());
439 
440         OutputStream out = sockHolder.getSocket().getOutputStream();
441 
442         if (!posting) {
443             out.write(header.toString()
444                     .getBytes(HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING));
445             out.flush();
446             return null;
447         }
448 
449         InputStream inp = null;
450 
451         if (httpChunkStream || httpContinueExpected) {
452             out.write(header.toString()
453                     .getBytes(HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING));
454         }
455 
456         if(httpContinueExpected ){ //We need to get a reply from the server as to whether
457             // it wants us send anything more.
458             out.flush();
459             Hashtable cheaders= new Hashtable ();
460             inp = readHeadersFromSocket(sockHolder, msgContext, null, cheaders);
461             int returnCode= -1;
462             Integer Irc= (Integer)msgContext.getProperty(HTTPConstants.MC_HTTP_STATUS_CODE);
463             if(null != Irc) {
464                 returnCode= Irc.intValue();
465             }
466             if(100 == returnCode){  // got 100 we may continue.
467                 //Need todo a little msgContext house keeping....
468                 msgContext.removeProperty(HTTPConstants.MC_HTTP_STATUS_CODE);
469                 msgContext.removeProperty(HTTPConstants.MC_HTTP_STATUS_MESSAGE);
470             }
471             else{ //If no 100 Continue then we must not send anything!
472                 String statusMessage= (String)
473                         msgContext.getProperty(HTTPConstants.MC_HTTP_STATUS_MESSAGE);
474 
475                 AxisFault fault = new AxisFault("HTTP", "(" + returnCode+ ")" + statusMessage, null, null);
476 
477                 fault.setFaultDetailString(Messages.getMessage("return01",
478                                                                "" + returnCode, ""));
479                 throw fault;
480             }
481         }
482         ByteArrayOutputStream baos = null;
483         if (log.isDebugEnabled()) {
484             log.debug(Messages.getMessage("xmlSent00"));
485             log.debug("---------------------------------------------------");
486             baos = new ByteArrayOutputStream();
487         }
488         if (httpChunkStream) {
489             ChunkedOutputStream chunkedOutputStream = new ChunkedOutputStream(out);
490             out = new BufferedOutputStream(chunkedOutputStream, Constants.HTTP_TXR_BUFFER_SIZE);
491             try {
492                 if(baos != null) {
493                     out = new TeeOutputStream(out, baos);
494                 }
495                 reqMessage.writeTo(out);
496             } catch (SOAPException e) {
497                 log.error(Messages.getMessage("exception00"), e);
498             }
499             out.flush();
500             chunkedOutputStream.eos();
501         } else {
502             out = new BufferedOutputStream(out, Constants.HTTP_TXR_BUFFER_SIZE);
503             try {
504                 if (!httpContinueExpected) {
505                     out.write(header.toString()
506                             .getBytes(HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING));
507                 }
508                 if(baos != null) {
509                     out = new TeeOutputStream(out, baos);
510                 }
511                 reqMessage.writeTo(out);
512             } catch (SOAPException e) {
513                 log.error(Messages.getMessage("exception00"), e);
514             }
515             // Flush ONLY once.
516             out.flush();
517         }
518         if (log.isDebugEnabled()) {
519             log.debug(header + new String(baos.toByteArray()));
520         }
521 
522         return inp;
523     }
524 
525     /**
526      * Get cookies from message context and add it to the headers 
527      * @param msgContext
528      * @param header
529      * @param otherHeaders
530      */
531     private void fillHeaders(MessageContext msgContext, String header, StringBuffer otherHeaders) {
532         Object ck1 = msgContext.getProperty(header);
533         if (ck1 != null) {
534             if (ck1 instanceof String[]) {
535                 String [] cookies = (String[]) ck1;
536                 for (int i = 0; i < cookies.length; i++) {
537                     addCookie(otherHeaders, header, cookies[i]);
538                 }
539             } else {
540                 addCookie(otherHeaders, header, (String) ck1);
541             }
542         }
543     }
544 
545     /**
546      * add cookie to headers
547      * @param otherHeaders
548      * @param header
549      * @param cookie
550      */
551     private void addCookie(StringBuffer otherHeaders, String header, String cookie) {
552         otherHeaders.append(header).append(": ")
553                 .append(cookie).append("\r\n");
554     }
555 
556     private InputStream readHeadersFromSocket(SocketHolder sockHolder,
557                                               MessageContext msgContext,
558                                               InputStream inp,
559                                               Hashtable headers)
560             throws IOException {
561         byte b = 0;
562         int len = 0;
563         int colonIndex = -1;
564         String name, value;
565         int returnCode = 0;
566         if(null == inp) {
567             inp = new BufferedInputStream(sockHolder.getSocket().getInputStream());
568         }
569 
570         if (headers == null) {
571             headers = new Hashtable();
572         }
573 
574         // Should help performance. Temporary fix only till its all stream oriented.
575         // Need to add logic for getting the version # and the return code
576         // but that's for tomorrow!
577 
578         /* Logic to read HTTP response headers */
579         boolean readTooMuch = false;
580 
581         for (ByteArrayOutputStream buf = new ByteArrayOutputStream(4097); ;) {
582             if (!readTooMuch) {
583                 b = (byte) inp.read();
584             }
585             if (b == -1) {
586                 break;
587             }
588             readTooMuch = false;
589             if ((b != '\r') && (b != '\n')) {
590                 if ((b == ':') && (colonIndex == -1)) {
591                     colonIndex = len;
592                 }
593                 len++;
594                 buf.write(b);
595             } else if (b == '\r') {
596                 continue;
597             } else {    // b== '\n'
598                 if (len == 0) {
599                     break;
600                 }
601                 b = (byte) inp.read();
602                 readTooMuch = true;
603 
604                 // A space or tab at the begining of a line means the header continues.
605                 if ((b == ' ') || (b == '\t')) {
606                     continue;
607                 }
608                 buf.close();
609                 byte[] hdata = buf.toByteArray();
610                 buf.reset();
611                 if (colonIndex != -1) {
612                     name =
613                             new String(hdata, 0, colonIndex,
614                                     HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING);
615                     value =
616                             new String(hdata, colonIndex + 1, len - 1 - colonIndex,
617                                     HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING);
618                     colonIndex = -1;
619                 } else {
620 
621                     name =
622                             new String(hdata, 0, len,
623                                     HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING);
624                     value = "";
625                 }
626                 if (log.isDebugEnabled()) {
627                     log.debug(name + value);
628                 }
629                 if (msgContext.getProperty(HTTPConstants.MC_HTTP_STATUS_CODE)
630                         == null) {
631 
632                     // Reader status code
633                     int start = name.indexOf(' ') + 1;
634                     String tmp = name.substring(start).trim();
635                     int end = tmp.indexOf(' ');
636 
637                     if (end != -1) {
638                         tmp = tmp.substring(0, end);
639                     }
640                     returnCode = Integer.parseInt(tmp);
641                     msgContext.setProperty(HTTPConstants.MC_HTTP_STATUS_CODE,
642                             new Integer(returnCode));
643                     msgContext.setProperty(HTTPConstants.MC_HTTP_STATUS_MESSAGE,
644                             name.substring(start + end + 1));
645                 } else {
646                     // if we are maintaining session state,
647                     // handle cookies (if any)
648                     if (msgContext.getMaintainSession()) {
649                         final String nameLowerCase = name.toLowerCase();
650                         if (nameLowerCase.equalsIgnoreCase(HTTPConstants.HEADER_SET_COOKIE)) {
651                             handleCookie(HTTPConstants.HEADER_COOKIE, null, value, msgContext);
652                         } else if (nameLowerCase.equalsIgnoreCase(HTTPConstants.HEADER_SET_COOKIE2)) {
653                             handleCookie(HTTPConstants.HEADER_COOKIE2, null, value, msgContext);
654                         } else {
655                             headers.put(name.toLowerCase(), value);
656                         }
657                     } else {
658                         headers.put(name.toLowerCase(), value);
659                     }
660                 }
661                 len = 0;
662             }
663         }
664 
665         return inp;
666     }
667 
668     /**
669      * Reads the SOAP response back from the server
670      *
671      * @param msgContext message context
672      *
673      * @throws IOException
674      */
675     private InputStream readFromSocket(SocketHolder socketHolder,
676                                        MessageContext msgContext,
677                                        InputStream inp,
678                                        Hashtable headers)
679             throws IOException {
680         Message outMsg = null;
681         byte b;
682 
683         Integer rc = (Integer)msgContext.getProperty(
684                                             HTTPConstants.MC_HTTP_STATUS_CODE);
685         int returnCode = 0;
686         if (rc != null) {
687             returnCode = rc.intValue();
688         } else {
689             // No return code?? Should have one by now.
690         }
691 
692         /* All HTTP headers have been read. */
693         String contentType = (String) headers.get(HEADER_CONTENT_TYPE_LC);
694                         
695         contentType = (null == contentType)
696                 ? null
697                 : contentType.trim();
698                 
699         String location = (String) headers.get(HEADER_LOCATION_LC);
700 
701         location = (null == location)
702                 ? null
703                 : location.trim();
704                 
705         if ((returnCode > 199) && (returnCode < 300)) {
706             if (returnCode == 202) {
707                 return inp;
708             }
709             // SOAP return is OK - so fall through
710         } else if (msgContext.getSOAPConstants() ==
711                 SOAPConstants.SOAP12_CONSTANTS) {
712             // For now, if we're SOAP 1.2, fall through, since the range of
713             // valid result codes is much greater
714         } else if ((contentType != null) && !contentType.startsWith("text/html")
715                 && ((returnCode > 499) && (returnCode < 600))) {
716             // SOAP Fault should be in here - so fall through
717         } else if ((location != null) &&
718                 ((returnCode == 302) || (returnCode == 307))) {
719             // Temporary Redirect (HTTP: 302/307)            
720             // close old connection
721                 inp.close();
722                 socketHolder.getSocket().close();    
723             // remove former result and set new target url
724                 msgContext.removeProperty(HTTPConstants.MC_HTTP_STATUS_CODE);
725                 msgContext.setProperty(MessageContext.TRANS_URL, location);
726             // next try
727                 invoke(msgContext);            
728                 return inp;
729         } else if (returnCode == 100) {
730             msgContext.removeProperty(HTTPConstants.MC_HTTP_STATUS_CODE);
731             msgContext.removeProperty(HTTPConstants.MC_HTTP_STATUS_MESSAGE);
732             readHeadersFromSocket(socketHolder, msgContext, inp, headers);
733             return readFromSocket(socketHolder, msgContext, inp, headers);
734         } else {
735             // Unknown return code - so wrap up the content into a
736             // SOAP Fault.
737             ByteArrayOutputStream buf = new ByteArrayOutputStream(4097);
738 
739             while (-1 != (b = (byte) inp.read())) {
740                 buf.write(b);
741             }
742             String statusMessage = msgContext.getStrProp(
743                                         HTTPConstants.MC_HTTP_STATUS_MESSAGE);
744             AxisFault fault = new AxisFault("HTTP", "(" + returnCode + ")" +
745                                                     statusMessage, null, null);
746 
747             fault.setFaultDetailString(Messages.getMessage("return01",
748                     "" + returnCode, buf.toString()));
749             fault.addFaultDetail(Constants.QNAME_FAULTDETAIL_HTTPERRORCODE,
750                     Integer.toString(returnCode));
751             throw fault;
752         }
753 
754         String contentLocation = 
755             (String) headers.get(HEADER_CONTENT_LOCATION_LC);
756 
757         contentLocation = (null == contentLocation)
758                 ? null
759                 : contentLocation.trim();
760 
761         String contentLength = 
762             (String) headers.get(HEADER_CONTENT_LENGTH_LC);
763 
764         contentLength = (null == contentLength)
765                 ? null
766                 : contentLength.trim();
767 
768         String transferEncoding =
769             (String) headers.get(HEADER_TRANSFER_ENCODING_LC);
770 
771         if (null != transferEncoding) {
772             transferEncoding = transferEncoding.trim().toLowerCase();
773             if (transferEncoding.equals(
774                    HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED)) {
775                 inp = new ChunkedInputStream(inp);
776             }
777         }
778 
779         outMsg = new Message( new SocketInputStream(inp, socketHolder.getSocket()), false,
780                               contentType, contentLocation);
781         // Transfer HTTP headers of HTTP message to MIME headers of SOAP message
782         MimeHeaders mimeHeaders = outMsg.getMimeHeaders();
783         for (Enumeration e = headers.keys(); e.hasMoreElements(); ) {
784             String key = (String) e.nextElement();
785             mimeHeaders.addHeader(key, ((String) headers.get(key)).trim());
786         }        
787         outMsg.setMessageType(Message.RESPONSE);
788         msgContext.setResponseMessage(outMsg);
789         if (log.isDebugEnabled()) {
790             if (null == contentLength) {
791                 log.debug("\n"
792                           + Messages.getMessage("no00", "Content-Length"));
793             }
794             log.debug("\n" + Messages.getMessage("xmlRecd00"));
795             log.debug("-----------------------------------------------");
796             log.debug(outMsg.getSOAPEnvelope().toString());
797         }
798 
799         return inp;
800     }
801 
802     /**
803      * little helper function for cookies. fills up the message context with
804      * a string or an array of strings (if there are more than one Set-Cookie)
805      *
806      * @param cookieName
807      * @param setCookieName
808      * @param cookie
809      * @param msgContext
810      */
811     public void handleCookie(String cookieName, String setCookieName,
812                              String cookie, MessageContext msgContext) {
813 
814         cookie = cleanupCookie(cookie);
815         int keyIndex = cookie.indexOf("=");
816         String key = (keyIndex != -1) ? cookie.substring(0, keyIndex) : cookie;
817         
818         ArrayList cookies = new ArrayList();
819         Object oldCookies = msgContext.getProperty(cookieName);
820         boolean alreadyExist = false;
821         if(oldCookies != null) {
822             if(oldCookies instanceof String[]) {
823                 String[] oldCookiesArray = (String[])oldCookies;
824                 for(int i = 0; i < oldCookiesArray.length; i++) {
825                     String anOldCookie = oldCookiesArray[i];
826                     if (key != null && anOldCookie.indexOf(key) == 0) { // same cookie key
827                         anOldCookie = cookie;             // update to new one
828                         alreadyExist = true;
829                     }
830                     cookies.add(anOldCookie);
831                 }
832             } else {
833         String oldCookie = (String)oldCookies;
834                 if (key != null && oldCookie.indexOf(key) == 0) { // same cookie key
835           oldCookie = cookie;             // update to new one
836                     alreadyExist = true;
837                 }
838                 cookies.add(oldCookie);
839             }
840         }
841         
842         if (!alreadyExist) {
843             cookies.add(cookie);
844         }
845         
846         if(cookies.size()==1) {
847             msgContext.setProperty(cookieName, cookies.get(0));
848         } else if (cookies.size() > 1) {
849             msgContext.setProperty(cookieName, cookies.toArray(new String[cookies.size()]));
850         }
851     }
852     
853     /**
854      * cleanup the cookie value.
855      *
856      * @param cookie initial cookie value
857      *
858      * @return a cleaned up cookie value.
859      */
860     private String cleanupCookie(String cookie) {
861         cookie = cookie.trim();
862         // chop after first ; a la Apache SOAP (see HTTPUtils.java there)
863         int index = cookie.indexOf(';');
864         if (index != -1) {
865             cookie = cookie.substring(0, index);
866         }
867         return cookie;
868     }
869 }