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 }