001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019:
020: package org.apache.axis2.transport.http;
021:
022: import org.apache.axiom.om.OMAttribute;
023: import org.apache.axiom.om.OMElement;
024: import org.apache.axiom.om.OMOutputFormat;
025: import org.apache.axis2.AxisFault;
026: import org.apache.axis2.Constants;
027: import org.apache.axis2.context.MessageContext;
028: import org.apache.axis2.context.OperationContext;
029: import org.apache.axis2.description.TransportOutDescription;
030: import org.apache.axis2.i18n.Messages;
031: import org.apache.axis2.transport.MessageFormatter;
032: import org.apache.axis2.transport.TransportUtils;
033: import org.apache.axis2.util.JavaUtils;
034: import org.apache.axis2.wsdl.WSDLConstants;
035: import org.apache.commons.httpclient.*;
036: import org.apache.commons.httpclient.auth.AuthPolicy;
037: import org.apache.commons.httpclient.auth.AuthScope;
038: import org.apache.commons.httpclient.protocol.Protocol;
039: import org.apache.commons.logging.Log;
040: import org.apache.commons.logging.LogFactory;
041:
042: import javax.xml.namespace.QName;
043: import java.io.IOException;
044: import java.io.InputStream;
045: import java.net.URL;
046: import java.util.ArrayList;
047: import java.util.HashMap;
048: import java.util.List;
049: import java.util.Map;
050: import java.util.zip.GZIPInputStream;
051:
052: public abstract class AbstractHTTPSender {
053: protected static final String ANONYMOUS = "anonymous";
054: protected static final String PROXY_HOST_NAME = "proxy_host";
055: protected static final String PROXY_PORT = "proxy_port";
056: protected boolean chunked = false;
057: protected String httpVersion = HTTPConstants.HEADER_PROTOCOL_11;
058: private static final Log log = LogFactory
059: .getLog(AbstractHTTPSender.class);
060:
061: protected static final String PROTOCOL_HTTP = "http";
062: protected static final String PROTOCOL_HTTPS = "https";
063:
064: /**
065: * proxydiscription
066: */
067: protected TransportOutDescription proxyOutSetting = null;
068: protected OMOutputFormat format = new OMOutputFormat();
069:
070: /**
071: * isAllowedRetry will be using to check where the
072: * retry should be allowed or not.
073: */
074: protected boolean isAllowedRetry = false;
075:
076: public void setChunked(boolean chunked) {
077: this .chunked = chunked;
078: }
079:
080: public void setHttpVersion(String version) throws AxisFault {
081: if (version != null) {
082: if (HTTPConstants.HEADER_PROTOCOL_11.equals(version)) {
083: this .httpVersion = HTTPConstants.HEADER_PROTOCOL_11;
084: } else if (HTTPConstants.HEADER_PROTOCOL_10.equals(version)) {
085: this .httpVersion = HTTPConstants.HEADER_PROTOCOL_10;
086: // chunked is not possible with HTTP/1.0
087: this .chunked = false;
088: } else {
089: throw new AxisFault("Parameter "
090: + HTTPConstants.PROTOCOL_VERSION
091: + " Can have values only HTTP/1.0 or HTTP/1.1");
092: }
093: }
094: }
095:
096: /**
097: * Collect the HTTP header information and set them in the message context
098: *
099: * @param method HttpMethodBase from which to get information
100: * @param msgContext the MessageContext in which to place the information... OR NOT!
101: * @throws AxisFault if problems occur
102: */
103: protected void obtainHTTPHeaderInformation(HttpMethodBase method,
104: MessageContext msgContext) throws AxisFault {
105: // Set RESPONSE properties onto the REQUEST message context. They will need to be copied off the request context onto
106: // the response context elsewhere, for example in the OutInOperationClient.
107: Map transportHeaders = new CommonsTransportHeaders(method
108: .getResponseHeaders());
109: msgContext.setProperty(MessageContext.TRANSPORT_HEADERS,
110: transportHeaders);
111: msgContext.setProperty(HTTPConstants.MC_HTTP_STATUS_CODE,
112: new Integer(method.getStatusCode()));
113: Header header = method
114: .getResponseHeader(HTTPConstants.HEADER_CONTENT_TYPE);
115:
116: if (header != null) {
117: HeaderElement[] headers = header.getElements();
118: MessageContext inMessageContext = msgContext
119: .getOperationContext().getMessageContext(
120: WSDLConstants.MESSAGE_LABEL_IN_VALUE);
121:
122: Object contentType = header.getValue();
123: Object charSetEnc = null;
124:
125: for (int i = 0; i < headers.length; i++) {
126: NameValuePair charsetEnc = headers[i]
127: .getParameterByName(HTTPConstants.CHAR_SET_ENCODING);
128: if (charsetEnc != null) {
129: charSetEnc = charsetEnc.getValue();
130: }
131: }
132:
133: if (inMessageContext != null) {
134: inMessageContext.setProperty(
135: Constants.Configuration.CONTENT_TYPE,
136: contentType);
137: inMessageContext.setProperty(
138: Constants.Configuration.CHARACTER_SET_ENCODING,
139: charSetEnc);
140: } else {
141:
142: // Transport details will be stored in a HashMap so that anybody interested can
143: // retrieve them
144: HashMap transportInfoMap = new HashMap();
145: transportInfoMap.put(
146: Constants.Configuration.CONTENT_TYPE,
147: contentType);
148: transportInfoMap.put(
149: Constants.Configuration.CHARACTER_SET_ENCODING,
150: charSetEnc);
151:
152: //the HashMap is stored in the outgoing message.
153: msgContext.setProperty(
154: Constants.Configuration.TRANSPORT_INFO_MAP,
155: transportInfoMap);
156: }
157: }
158:
159: String sessionCookie = null;
160: // Process old style headers first
161: Header[] cookieHeaders = method
162: .getResponseHeaders(HTTPConstants.HEADER_SET_COOKIE);
163: String customCoookiId = (String) msgContext
164: .getProperty(Constants.CUSTOM_COOKIE_ID);
165: for (int i = 0; i < cookieHeaders.length; i++) {
166: HeaderElement[] elements = cookieHeaders[i].getElements();
167: for (int e = 0; e < elements.length; e++) {
168: HeaderElement element = elements[e];
169: if (Constants.SESSION_COOKIE.equalsIgnoreCase(element
170: .getName())
171: || Constants.SESSION_COOKIE_JSESSIONID
172: .equalsIgnoreCase(element.getName())) {
173: sessionCookie = processCookieHeader(element);
174: }
175: if (customCoookiId != null
176: && customCoookiId.equalsIgnoreCase(element
177: .getName())) {
178: sessionCookie = processCookieHeader(element);
179: }
180: }
181: }
182: // Overwrite old style cookies with new style ones if present
183: cookieHeaders = method
184: .getResponseHeaders(HTTPConstants.HEADER_SET_COOKIE2);
185: for (int i = 0; i < cookieHeaders.length; i++) {
186: HeaderElement[] elements = cookieHeaders[i].getElements();
187: for (int e = 0; e < elements.length; e++) {
188: HeaderElement element = elements[e];
189: if (Constants.SESSION_COOKIE.equalsIgnoreCase(element
190: .getName())
191: || Constants.SESSION_COOKIE_JSESSIONID
192: .equalsIgnoreCase(element.getName())) {
193: sessionCookie = processCookieHeader(element);
194: }
195: if (customCoookiId != null
196: && customCoookiId.equalsIgnoreCase(element
197: .getName())) {
198: sessionCookie = processCookieHeader(element);
199: }
200: }
201: }
202:
203: if (sessionCookie != null) {
204: msgContext.getServiceContext().setProperty(
205: HTTPConstants.COOKIE_STRING, sessionCookie);
206: }
207: }
208:
209: private String processCookieHeader(HeaderElement element) {
210: String cookie = element.getName() + "=" + element.getValue();
211: NameValuePair[] parameters = element.getParameters();
212: for (int j = 0; j < parameters.length; j++) {
213: NameValuePair parameter = parameters[j];
214: cookie = cookie + "; " + parameter.getName() + "="
215: + parameter.getValue();
216: }
217: return cookie;
218: }
219:
220: protected void processResponse(HttpMethodBase httpMethod,
221: MessageContext msgContext) throws IOException {
222: obtainHTTPHeaderInformation(httpMethod, msgContext);
223:
224: InputStream in = httpMethod.getResponseBodyAsStream();
225: if (in == null) {
226: throw new AxisFault(Messages.getMessage("canNotBeNull",
227: "InputStream"));
228: }
229: Header contentEncoding = httpMethod
230: .getResponseHeader(HTTPConstants.HEADER_CONTENT_ENCODING);
231: if (contentEncoding != null) {
232: if (contentEncoding.getValue().equalsIgnoreCase(
233: HTTPConstants.COMPRESSION_GZIP)) {
234: in = new GZIPInputStream(in);
235: } else {
236: throw new AxisFault("HTTP :"
237: + "unsupported content-encoding of '"
238: + contentEncoding.getValue() + "' found");
239: }
240: }
241:
242: OperationContext opContext = msgContext.getOperationContext();
243: if (opContext != null) {
244: opContext.setProperty(MessageContext.TRANSPORT_IN, in);
245: }
246: }
247:
248: public abstract void send(MessageContext msgContext, URL url,
249: String soapActionString) throws IOException;
250:
251: /**
252: * getting host configuration to support standard http/s, proxy and NTLM support
253: *
254: * @param client active HttpClient
255: * @param msgCtx active MessageContext
256: * @param targetURL the target URL
257: * @return a HostConfiguration set up with proxy information
258: * @throws AxisFault if problems occur
259: */
260: protected HostConfiguration getHostConfiguration(HttpClient client,
261: MessageContext msgCtx, URL targetURL) throws AxisFault {
262:
263: boolean isAuthenticationEnabled = isAuthenticationEnabled(msgCtx);
264: int port = targetURL.getPort();
265:
266: String protocol = targetURL.getProtocol();
267: if (port == -1) {
268: if (PROTOCOL_HTTP.equals(protocol)) {
269: port = 80;
270: } else if (PROTOCOL_HTTPS.equals(protocol)) {
271: port = 443;
272: }
273:
274: }
275:
276: // to see the host is a proxy and in the proxy list - available in axis2.xml
277: HostConfiguration config = new HostConfiguration();
278:
279: // one might need to set his own socket factory. Let's allow that case as well.
280: Protocol protocolHandler = (Protocol) msgCtx.getOptions()
281: .getProperty(HTTPConstants.CUSTOM_PROTOCOL_HANDLER);
282:
283: // setting the real host configuration
284: // I assume the 90% case, or even 99% case will be no protocol handler case.
285: if (protocolHandler == null) {
286: config.setHost(targetURL.getHost(), port, targetURL
287: .getProtocol());
288: } else {
289: config.setHost(targetURL.getHost(), port, protocolHandler);
290: }
291:
292: if (isAuthenticationEnabled) {
293: // Basic, Digest, NTLM and custom authentications.
294: this .setAuthenticationInfo(client, msgCtx, config);
295: }
296: // proxy configuration
297:
298: if (ProxyConfiguration.isProxyEnabled(msgCtx, targetURL)) {
299: log.debug("ProxyConfiguration");
300: ProxyConfiguration proxyConfiguration = new ProxyConfiguration();
301: proxyConfiguration.configure(msgCtx, client, config);
302: }
303:
304: return config;
305: }
306:
307: protected boolean isAuthenticationEnabled(MessageContext msgCtx) {
308: return (msgCtx.getProperty(HTTPConstants.AUTHENTICATE) != null);
309: }
310:
311: /*
312: This will handle server Authentication, It could be either NTLM, Digest or Basic Authentication.
313: Apart from that user can change the priory or add a custom authentication scheme.
314: */
315: protected void setAuthenticationInfo(HttpClient agent,
316: MessageContext msgCtx, HostConfiguration config)
317: throws AxisFault {
318: HttpTransportProperties.Authenticator authenticator;
319: Object obj = msgCtx.getProperty(HTTPConstants.AUTHENTICATE);
320: if (obj != null) {
321: if (obj instanceof HttpTransportProperties.Authenticator) {
322: authenticator = (HttpTransportProperties.Authenticator) obj;
323:
324: String username = authenticator.getUsername();
325: String password = authenticator.getPassword();
326: String host = authenticator.getHost();
327: String domain = authenticator.getDomain();
328:
329: int port = authenticator.getPort();
330: String realm = authenticator.getRealm();
331:
332: /* If retrying is available set it first */
333: isAllowedRetry = authenticator.isAllowedRetry();
334:
335: Credentials creds;
336:
337: agent.getParams().setAuthenticationPreemptive(
338: authenticator.getPreemptiveAuthentication());
339:
340: if (host != null) {
341: if (domain != null) {
342: /*Credentials for NTLM Authentication*/
343: creds = new NTCredentials(username, password,
344: host, domain);
345: } else {
346: /*Credentials for Digest and Basic Authentication*/
347: creds = new UsernamePasswordCredentials(
348: username, password);
349: }
350: agent.getState().setCredentials(
351: new AuthScope(host, port, realm), creds);
352: } else {
353: if (domain != null) {
354: /*Credentials for NTLM Authentication when host is ANY_HOST*/
355: creds = new NTCredentials(username, password,
356: AuthScope.ANY_HOST, domain);
357: agent.getState().setCredentials(
358: new AuthScope(AuthScope.ANY_HOST, port,
359: realm), creds);
360: } else {
361: /*Credentials only for Digest and Basic Authentication*/
362: creds = new UsernamePasswordCredentials(
363: username, password);
364: agent.getState().setCredentials(
365: new AuthScope(AuthScope.ANY), creds);
366: }
367: }
368: /* Customizing the priority Order */
369: List schemes = authenticator.getAuthSchemes();
370: if (schemes != null && schemes.size() > 0) {
371: List authPrefs = new ArrayList(3);
372: for (int i = 0; i < schemes.size(); i++) {
373: if (schemes.get(i) instanceof AuthPolicy) {
374: authPrefs.add(schemes.get(i));
375: continue;
376: }
377: String scheme = (String) schemes.get(i);
378: if (HttpTransportProperties.Authenticator.BASIC
379: .equals(scheme)) {
380: authPrefs.add(AuthPolicy.BASIC);
381: } else if (HttpTransportProperties.Authenticator.NTLM
382: .equals(scheme)) {
383: authPrefs.add(AuthPolicy.NTLM);
384: } else if (HttpTransportProperties.Authenticator.DIGEST
385: .equals(scheme)) {
386: authPrefs.add(AuthPolicy.DIGEST);
387: }
388: }
389: agent.getParams().setParameter(
390: AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);
391: }
392:
393: } else {
394: throw new AxisFault(
395: "HttpTransportProperties.Authenticator class cast exception");
396: }
397: }
398:
399: }
400:
401: /**
402: * Method used to copy all the common properties
403: *
404: * @param msgContext - The messageContext of the request message
405: * @param url - The target URL
406: * @param httpMethod - The http method used to send the request
407: * @param httpClient - The httpclient used to send the request
408: * @param soapActionString - The soap action atring of the request message
409: * @return MessageFormatter - The messageFormatter for the relavent request message
410: * @throws AxisFault - Thrown in case an exception occurs
411: */
412: protected MessageFormatter populateCommonProperties(
413: MessageContext msgContext, URL url,
414: HttpMethodBase httpMethod, HttpClient httpClient,
415: String soapActionString) throws AxisFault {
416:
417: if (isAuthenticationEnabled(msgContext)) {
418: httpMethod.setDoAuthentication(true);
419: }
420:
421: MessageFormatter messageFormatter = TransportUtils
422: .getMessageFormatter(msgContext);
423:
424: url = messageFormatter
425: .getTargetAddress(msgContext, format, url);
426:
427: httpMethod.setPath(url.getPath());
428:
429: httpMethod.setQueryString(url.getQuery());
430:
431: httpMethod.setRequestHeader(HTTPConstants.HEADER_CONTENT_TYPE,
432: messageFormatter.getContentType(msgContext, format,
433: soapActionString));
434:
435: httpMethod.setRequestHeader(HTTPConstants.HEADER_HOST, url
436: .getHost());
437:
438: if (msgContext.getOptions() != null
439: && msgContext.getOptions().isManageSession()) {
440: // setting the cookie in the out path
441: Object cookieString = msgContext
442: .getProperty(HTTPConstants.COOKIE_STRING);
443:
444: if (cookieString != null) {
445: StringBuffer buffer = new StringBuffer();
446: buffer.append(cookieString);
447: httpMethod.setRequestHeader(
448: HTTPConstants.HEADER_COOKIE, buffer.toString());
449: }
450: }
451:
452: if (httpVersion.equals(HTTPConstants.HEADER_PROTOCOL_10)) {
453: httpClient.getParams().setVersion(HttpVersion.HTTP_1_0);
454: }
455: return messageFormatter;
456: }
457:
458: /**
459: * This is used to get the dynamically set time out values from the
460: * message context. If the values are not available or invalid then
461: * the default values or the values set by the configuration will be used
462: *
463: * @param msgContext the active MessageContext
464: * @param httpClient
465: */
466: protected void initializeTimeouts(MessageContext msgContext,
467: HttpClient httpClient) {
468: // If the SO_TIMEOUT of CONNECTION_TIMEOUT is set by dynamically the
469: // override the static config
470: Integer tempSoTimeoutProperty = (Integer) msgContext
471: .getProperty(HTTPConstants.SO_TIMEOUT);
472: Integer tempConnTimeoutProperty = (Integer) msgContext
473: .getProperty(HTTPConstants.CONNECTION_TIMEOUT);
474: long timeout = msgContext.getOptions()
475: .getTimeOutInMilliSeconds();
476:
477: if (tempConnTimeoutProperty != null) {
478: int connectionTimeout = tempConnTimeoutProperty.intValue();
479: // timeout for initial connection
480: httpClient.getHttpConnectionManager().getParams()
481: .setConnectionTimeout(connectionTimeout);
482: } else {
483: // set timeout in client
484: if (timeout > 0) {
485: httpClient.getHttpConnectionManager().getParams()
486: .setConnectionTimeout((int) timeout);
487: }
488: }
489:
490: if (tempSoTimeoutProperty != null) {
491: int soTimeout = tempSoTimeoutProperty.intValue();
492: // SO_TIMEOUT -- timeout for blocking reads
493: httpClient.getHttpConnectionManager().getParams()
494: .setSoTimeout(soTimeout);
495: httpClient.getParams().setSoTimeout(soTimeout);
496: } else {
497: // set timeout in client
498: if (timeout > 0) {
499: httpClient.getHttpConnectionManager().getParams()
500: .setSoTimeout((int) timeout);
501: httpClient.getParams().setSoTimeout((int) timeout);
502: }
503: }
504: }
505:
506: public void setFormat(OMOutputFormat format) {
507: this .format = format;
508: }
509:
510: protected HttpClient getHttpClient(MessageContext msgContext) {
511: HttpClient httpClient;
512: Object reuse = msgContext.getOptions().getProperty(
513: HTTPConstants.REUSE_HTTP_CLIENT);
514: if (reuse == null) {
515: reuse = msgContext.getConfigurationContext().getProperty(
516: HTTPConstants.REUSE_HTTP_CLIENT);
517: }
518: if (reuse != null && JavaUtils.isTrueExplicitly(reuse)) {
519: httpClient = (HttpClient) msgContext.getOptions()
520: .getProperty(HTTPConstants.CACHED_HTTP_CLIENT);
521: if (httpClient == null) {
522: httpClient = (HttpClient) msgContext
523: .getConfigurationContext().getProperty(
524: HTTPConstants.CACHED_HTTP_CLIENT);
525: }
526: if (httpClient != null)
527: return httpClient;
528: MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
529: httpClient = new HttpClient(connectionManager);
530: msgContext.getConfigurationContext().setProperty(
531: HTTPConstants.CACHED_HTTP_CLIENT, httpClient);
532: } else {
533: HttpConnectionManager connManager = (HttpConnectionManager) msgContext
534: .getProperty(HTTPConstants.MUTTITHREAD_HTTP_CONNECTION_MANAGER);
535: if (connManager != null) {
536: httpClient = new HttpClient(connManager);
537: } else {
538: //Multi threaded http connection manager has set as the default
539: connManager = new MultiThreadedHttpConnectionManager();
540: httpClient = new HttpClient(connManager);
541: }
542: }
543:
544: // Get the timeout values set in the runtime
545: initializeTimeouts(msgContext, httpClient);
546: return httpClient;
547: }
548:
549: protected void executeMethod(HttpClient httpClient,
550: MessageContext msgContext, URL url, HttpMethod method)
551: throws IOException {
552: HostConfiguration config = this .getHostConfiguration(
553: httpClient, msgContext, url);
554:
555: msgContext.setProperty(HTTPConstants.HTTP_METHOD, method);
556:
557: // set the custom headers, if available
558: addCustomHeaders(method, msgContext);
559:
560: // add compression headers if needed
561: if (msgContext.isPropertyTrue(HTTPConstants.MC_ACCEPT_GZIP)) {
562: method.addRequestHeader(
563: HTTPConstants.HEADER_ACCEPT_ENCODING,
564: HTTPConstants.COMPRESSION_GZIP);
565: }
566:
567: if (msgContext.isPropertyTrue(HTTPConstants.MC_GZIP_REQUEST)) {
568: method.addRequestHeader(
569: HTTPConstants.HEADER_CONTENT_ENCODING,
570: HTTPConstants.COMPRESSION_GZIP);
571: }
572:
573: httpClient.executeMethod(config, method);
574: }
575:
576: public void addCustomHeaders(HttpMethod method,
577: MessageContext msgContext) {
578:
579: boolean isCustomUserAgentSet = false;
580: // set the custom headers, if available
581: Object httpHeadersObj = msgContext
582: .getProperty(HTTPConstants.HTTP_HEADERS);
583: if (httpHeadersObj != null
584: && httpHeadersObj instanceof ArrayList) {
585: ArrayList httpHeaders = (ArrayList) httpHeadersObj;
586: Header header;
587: for (int i = 0; i < httpHeaders.size(); i++) {
588: header = (Header) httpHeaders.get(i);
589: if (HTTPConstants.HEADER_USER_AGENT.equals(header
590: .getName())) {
591: isCustomUserAgentSet = true;
592: }
593: method.addRequestHeader(header);
594: }
595:
596: }
597:
598: if (!isCustomUserAgentSet) {
599: String userAgentString = getUserAgent(msgContext);
600: method.setRequestHeader(HTTPConstants.HEADER_USER_AGENT,
601: userAgentString);
602: }
603:
604: }
605:
606: private String getUserAgent(MessageContext messageContext) {
607: String userAgentString = "Axis2";
608: boolean locked = false;
609: if (messageContext.getParameter(HTTPConstants.USER_AGENT) != null) {
610: OMElement userAgentElement = messageContext.getParameter(
611: HTTPConstants.USER_AGENT).getParameterElement();
612: userAgentString = userAgentElement.getText().trim();
613: OMAttribute lockedAttribute = userAgentElement
614: .getAttribute(new QName("locked"));
615: if (lockedAttribute != null) {
616: if (lockedAttribute.getAttributeValue()
617: .equalsIgnoreCase("true")) {
618: locked = true;
619: }
620: }
621: }
622: // Runtime overing part
623: if (!locked) {
624: if (messageContext.getProperty(HTTPConstants.USER_AGENT) != null) {
625: userAgentString = (String) messageContext
626: .getProperty(HTTPConstants.USER_AGENT);
627: }
628: }
629:
630: return userAgentString;
631: }
632: }
|