/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.axis2.transport.http.impl.httpclient3;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.axiom.mime.Header;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.http.AxisRequestEntity;
import org.apache.axis2.transport.http.HTTPAuthenticator;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.axis2.transport.http.HTTPTransportConstants;
import org.apache.axis2.transport.http.Request;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HeaderElement;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.HttpVersion;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthPolicy;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
final class RequestImpl implements Request {
private static final String[] COOKIE_HEADER_NAMES = { HTTPConstants.HEADER_SET_COOKIE, HTTPConstants.HEADER_SET_COOKIE2 };
private static final Log log = LogFactory.getLog(RequestImpl.class);
private final HttpClient httpClient;
private final MessageContext msgContext;
private final URL url;
private final HttpMethodBase method;
private final HostConfiguration config;
RequestImpl(HttpClient httpClient, MessageContext msgContext, final String methodName, URL url,
AxisRequestEntity requestEntity) throws AxisFault {
this.httpClient = httpClient;
this.msgContext = msgContext;
this.url = url;
if (requestEntity == null) {
method = new HttpMethodBase() {
@Override
public String getName() {
return methodName;
}
};
// This mimicks GetMethod
if (methodName.equals(HTTPConstants.HTTP_METHOD_GET)) {
method.setFollowRedirects(true);
}
} else {
EntityEnclosingMethod entityEnclosingMethod = new EntityEnclosingMethod() {
@Override
public String getName() {
return methodName;
}
};
entityEnclosingMethod.setRequestEntity(new AxisRequestEntityImpl(requestEntity));
entityEnclosingMethod.setContentChunked(requestEntity.isChunked());
method = entityEnclosingMethod;
}
method.setPath(url.getPath());
method.setQueryString(url.getQuery());
// TODO: this is fishy; it means that we may end up modifying a HostConfiguration from a cached HTTP client
HostConfiguration config = httpClient.getHostConfiguration();
if (config == null) {
config = new HostConfiguration();
}
this.config = config;
}
@Override
public void enableHTTP10() {
httpClient.getParams().setVersion(HttpVersion.HTTP_1_0);
}
@Override
public void setHeader(String name, String value) {
method.setRequestHeader(name, value);
}
@Override
public void addHeader(String name, String value) {
method.addRequestHeader(name, value);
}
private static Header[] convertHeaders(org.apache.commons.httpclient.Header[] headers) {
Header[] result = new Header[headers.length];
for (int i=0; i<headers.length; i++) {
result[i] = new Header(headers[i].getName(), headers[i].getValue());
}
return result;
}
@Override
public Header[] getRequestHeaders() {
return convertHeaders(method.getRequestHeaders());
}
@Override
public void setConnectionTimeout(int timeout) {
method.getParams().setParameter("http.connection.timeout", timeout);
}
@Override
public void setSocketTimeout(int timeout) {
method.getParams().setSoTimeout(timeout);
}
@Override
public int getStatusCode() {
return method.getStatusCode();
}
@Override
public String getStatusText() {
return method.getStatusText();
}
@Override
public String getResponseHeader(String name) {
org.apache.commons.httpclient.Header header = method.getResponseHeader(name);
return header == null ? null : header.getValue();
}
@Override
public Header[] getResponseHeaders() {
return convertHeaders(method.getResponseHeaders());
}
@Override
public Map<String,String> getCookies() {
Map<String,String> cookies = null;
for (String name : COOKIE_HEADER_NAMES) {
for (org.apache.commons.httpclient.Header header : method.getResponseHeaders(name)) {
for (HeaderElement element : header.getElements()) {
if (cookies == null) {
cookies = new HashMap<String,String>();
}
cookies.put(element.getName(), element.getValue());
}
}
}
return cookies;
}
@Override
public InputStream getResponseContent() throws IOException {
return method.getResponseBodyAsStream();
}
@Override
public void execute() throws IOException {
populateHostConfiguration();
// add compression headers if needed
if (msgContext.isPropertyTrue(HTTPConstants.MC_ACCEPT_GZIP)) {
method.addRequestHeader(HTTPConstants.HEADER_ACCEPT_ENCODING,
HTTPConstants.COMPRESSION_GZIP);
}
if (msgContext.getProperty(HTTPConstants.HTTP_METHOD_PARAMS) != null) {
HttpMethodParams params = (HttpMethodParams) msgContext
.getProperty(HTTPConstants.HTTP_METHOD_PARAMS);
method.setParams(params);
}
String cookiePolicy = (String) msgContext.getProperty(HTTPConstants.COOKIE_POLICY);
if (cookiePolicy != null) {
method.getParams().setCookiePolicy(cookiePolicy);
}
HttpState httpState = (HttpState) msgContext.getProperty(HTTPConstants.CACHED_HTTP_STATE);
httpClient.executeMethod(config, method, httpState);
}
@Override
public void releaseConnection() {
method.releaseConnection();
}
/**
* getting host configuration to support standard http/s, proxy and NTLM
* support
*
* @return a HostConfiguration set up with proxy information
* @throws AxisFault
* if problems occur
*/
private void populateHostConfiguration() throws AxisFault {
int port = url.getPort();
String protocol = url.getProtocol();
if (port == -1) {
if (HTTPTransportConstants.PROTOCOL_HTTP.equals(protocol)) {
port = 80;
} else if (HTTPTransportConstants.PROTOCOL_HTTPS.equals(protocol)) {
port = 443;
}
}
// one might need to set his own socket factory. Let's allow that case
// as well.
Protocol protocolHandler = (Protocol) msgContext.getOptions().getProperty(
HTTPConstants.CUSTOM_PROTOCOL_HANDLER);
// setting the real host configuration
// I assume the 90% case, or even 99% case will be no protocol handler
// case.
if (protocolHandler == null) {
config.setHost(url.getHost(), port, url.getProtocol());
} else {
config.setHost(url.getHost(), port, protocolHandler);
}
// proxy configuration
if (HTTPProxyConfigurator.isProxyEnabled(msgContext, url)) {
if (log.isDebugEnabled()) {
log.debug("Configuring HTTP proxy.");
}
HTTPProxyConfigurator.configure(msgContext, httpClient, config);
}
}
/*
* This will handle server Authentication, It could be either NTLM, Digest
* or Basic Authentication. Apart from that user can change the priory or
* add a custom authentication scheme.
*/
@Override
public void enableAuthentication(HTTPAuthenticator authenticator) {
method.setDoAuthentication(true);
String username = authenticator.getUsername();
String password = authenticator.getPassword();
String host = authenticator.getHost();
String domain = authenticator.getDomain();
int port = authenticator.getPort();
String realm = authenticator.getRealm();
Credentials creds;
HttpState tmpHttpState = null;
HttpState httpState = (HttpState) msgContext
.getProperty(HTTPConstants.CACHED_HTTP_STATE);
if (httpState != null) {
tmpHttpState = httpState;
} else {
tmpHttpState = httpClient.getState();
}
httpClient.getParams().setAuthenticationPreemptive(
authenticator.getPreemptiveAuthentication());
if (host != null) {
if (domain != null) {
/* Credentials for NTLM Authentication */
creds = new NTCredentials(username, password, host, domain);
} else {
/* Credentials for Digest and Basic Authentication */
creds = new UsernamePasswordCredentials(username, password);
}
tmpHttpState.setCredentials(new AuthScope(host, port, realm), creds);
} else {
if (domain != null) {
/*
* Credentials for NTLM Authentication when host is
* ANY_HOST
*/
creds = new NTCredentials(username, password, AuthScope.ANY_HOST, domain);
tmpHttpState.setCredentials(new AuthScope(AuthScope.ANY_HOST, port, realm),
creds);
} else {
/* Credentials only for Digest and Basic Authentication */
creds = new UsernamePasswordCredentials(username, password);
tmpHttpState.setCredentials(new AuthScope(AuthScope.ANY), creds);
}
}
/* Customizing the priority Order */
List schemes = authenticator.getAuthSchemes();
if (schemes != null && schemes.size() > 0) {
List authPrefs = new ArrayList(3);
for (int i = 0; i < schemes.size(); i++) {
if (schemes.get(i) instanceof AuthPolicy) {
authPrefs.add(schemes.get(i));
continue;
}
String scheme = (String) schemes.get(i);
authPrefs.add(authenticator.getAuthPolicyPref(scheme));
}
httpClient.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);
}
}
}