All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.axis2.transport.http.HTTPSender Maven / Gradle / Ivy

There is a newer version: 5.0.22
Show newest version
/*
 * 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;


import org.apache.axiom.mime.ContentType;
import org.apache.axiom.mime.Header;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axis2.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.context.NamedValue;
import org.apache.axis2.context.OperationContext;
import org.apache.axis2.description.TransportOutDescription;
import org.apache.axis2.i18n.Messages;
import org.apache.axis2.transport.MessageFormatter;
import org.apache.axis2.util.MessageProcessorSelector;
import org.apache.axis2.util.Utils;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpStatus;
import org.apache.http.protocol.HTTP;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.GZIPInputStream;

import javax.xml.namespace.QName;

//TODO - It better if we can define these method in a interface move these into AbstractHTTPSender and get rid of this class.
public abstract class HTTPSender {

    private static final Log log = LogFactory.getLog(HTTPSender.class);
    
    private boolean chunked = false;
    private String httpVersion = HTTPConstants.HEADER_PROTOCOL_11;
    protected TransportOutDescription proxyOutSetting = null;
    protected OMOutputFormat format = new OMOutputFormat();
    
    public void setChunked(boolean chunked) {
        this.chunked = chunked;
    }

    public void setHttpVersion(String version) throws AxisFault {
        if (version != null) {
            if (HTTPConstants.HEADER_PROTOCOL_11.equals(version)) {
                this.httpVersion = HTTPConstants.HEADER_PROTOCOL_11;
            } else if (HTTPConstants.HEADER_PROTOCOL_10.equals(version)) {
                this.httpVersion = HTTPConstants.HEADER_PROTOCOL_10;
                // chunked is not possible with HTTP/1.0
                this.chunked = false;
            } else {
                throw new AxisFault(
                        "Parameter " + HTTPConstants.PROTOCOL_VERSION
                                + " Can have values only HTTP/1.0 or HTTP/1.1");
            }
        }
    }       

    public void setFormat(OMOutputFormat format) {
        this.format = format;
    }

    /**
     * Start a new HTTP request.
     *
     * @param msgContext
     *            The MessageContext of the request message
     * @param methodName
     *            The HTTP method name
     * @param url
     *            The target URL
     * @param requestEntity
     *            The content of the request or {@code null} if the HTTP request shouldn't have any
     *            content (e.g. for {@code GET} requests)
     * @throws AxisFault
     *            Thrown in case an exception occurs
     */
    protected abstract Request createRequest(MessageContext msgContext, String methodName, URL url,
            AxisRequestEntity requestEntity) throws AxisFault;
    
    public void send(MessageContext msgContext, URL url, String soapActionString)
            throws IOException {

        // execute the HtttpMethodBase - a connection manager can be given for
        // handle multiple

        String httpMethod =
                (String) msgContext.getProperty(Constants.Configuration.HTTP_METHOD);
        if (httpMethod == null) {
            httpMethod = Constants.Configuration.HTTP_METHOD_POST;
        }

        MessageFormatter messageFormatter = MessageProcessorSelector
                .getMessageFormatter(msgContext);
        url = messageFormatter.getTargetAddress(msgContext, format, url);
        String contentType = messageFormatter.getContentType(msgContext, format, soapActionString);

        HTTPAuthenticator authenticator;
        Object obj = msgContext.getProperty(HTTPConstants.AUTHENTICATE);
        if (obj == null) {
            authenticator = null;
        } else {
            if (obj instanceof HTTPAuthenticator) {
                authenticator = (HTTPAuthenticator) obj;
            } else {
                throw new AxisFault("HttpTransportProperties.Authenticator class cast exception");
            }
        }

        AxisRequestEntity requestEntity;
        boolean gzip;
        if (Constants.Configuration.HTTP_METHOD_GET.equalsIgnoreCase(httpMethod)
                || Constants.Configuration.HTTP_METHOD_DELETE.equalsIgnoreCase(httpMethod)) {
            requestEntity = null;
            gzip = false;
        } else if (Constants.Configuration.HTTP_METHOD_POST.equalsIgnoreCase(httpMethod)
                || Constants.Configuration.HTTP_METHOD_PUT.equalsIgnoreCase(httpMethod)) {
            gzip = msgContext.isPropertyTrue(HTTPConstants.MC_GZIP_REQUEST);
            requestEntity = new AxisRequestEntity(messageFormatter, msgContext, format,
                    contentType, chunked, gzip, authenticator != null && authenticator.isAllowedRetry());
        } else {
            throw new AxisFault("Unsupported HTTP method " + httpMethod);
        }

        Request request = createRequest(msgContext, httpMethod, url, requestEntity);

        if (msgContext.getOptions() != null && msgContext.getOptions().isManageSession()) {
            // setting the cookie in the out path
            Object cookieString = msgContext.getProperty(HTTPConstants.COOKIE_STRING);

            if (cookieString != null) {
                StringBuffer buffer = new StringBuffer();
                buffer.append(cookieString);
                request.setHeader(HTTPConstants.HEADER_COOKIE, buffer.toString());
            }
        }

        if (httpVersion.equals(HTTPConstants.HEADER_PROTOCOL_10)) {
            request.enableHTTP10();
        }
        
        request.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, contentType);

        String soapAction = messageFormatter.formatSOAPAction(msgContext, format, soapActionString);

        if (soapAction != null && !msgContext.isDoingREST()) {
            request.setHeader(HTTPConstants.HEADER_SOAP_ACTION, soapAction);
        }

        if (gzip) {
            request.setHeader(HTTPConstants.HEADER_CONTENT_ENCODING,
                    HTTPConstants.COMPRESSION_GZIP);
        }
        
        // set the custom headers, if available
        addCustomHeaders(msgContext, request);
        
        if (authenticator != null) {
            request.enableAuthentication(authenticator);
        }

        setTimeouts(msgContext, request);

        try {
            request.execute();
            boolean cleanup = true;
            try {
                int statusCode = request.getStatusCode();
                log.trace("Handling response - " + statusCode);
                boolean processResponse;
                boolean fault;
                if (statusCode == HttpStatus.SC_ACCEPTED) {
                    processResponse = false;
                    fault = false;
                } else if (statusCode >= 200 && statusCode < 300) {
                    processResponse = true;
                    fault = false;
                } else if (statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR
                           || statusCode == HttpStatus.SC_BAD_REQUEST || statusCode == HttpStatus.SC_NOT_FOUND) {
                    processResponse = true;
                    fault = true;
                } else {
                    throw new AxisFault(Messages.getMessage("transportError", String.valueOf(statusCode),
                                                            request.getStatusText()));
                }
                obtainHTTPHeaderInformation(request, msgContext);
                if (processResponse) {
                    OperationContext opContext = msgContext.getOperationContext();
                    MessageContext inMessageContext = opContext == null ? null
                            : opContext.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
                    if (opContext != null) {
                        InputStream in = request.getResponseContent();
                        if (in != null) {
                            String contentEncoding = request.getResponseHeader(HTTPConstants.HEADER_CONTENT_ENCODING);
                            if (contentEncoding != null) {
                                if (contentEncoding.equalsIgnoreCase(HTTPConstants.COMPRESSION_GZIP)) {
                                    in = new GZIPInputStream(in);
                                    // If the content-encoding is identity we can basically ignore
                                    // it.
                                } else if (!"identity".equalsIgnoreCase(contentEncoding)) {
                                    throw new AxisFault("HTTP :" + "unsupported content-encoding of '"
                                                        + contentEncoding + "' found");
                                }
                            }
                            opContext.setProperty(MessageContext.TRANSPORT_IN, in);
                            // This implements the behavior of the HTTPClient 3.x based transport in
                            // Axis2 1.7: if AUTO_RELEASE_CONNECTION is enabled, we set the input stream
                            // in the message context, but we nevertheless release the connection.
                            // It is unclear in which situation this would actually be the right thing
                            // to do.
                            if (msgContext.isPropertyTrue(HTTPConstants.AUTO_RELEASE_CONNECTION)) {
                                log.debug("AUTO_RELEASE_CONNECTION enabled; are you sure that you really want that?");
                            } else {
                                cleanup = false;
                            }
                        }
                    }
                    if (fault) {
                        if (inMessageContext != null) {
                            inMessageContext.setProcessingFault(true);
                        }
                        if (Utils.isClientThreadNonBlockingPropertySet(msgContext)) {
                            throw new AxisFault(Messages.
                                    getMessage("transportError",
                                               String.valueOf(statusCode),
                                               request.getStatusText()));
                        }
                    }
                }
            } finally {
                if (cleanup) {
                    request.releaseConnection();
                }
            }
        } catch (IOException e) {
            log.info("Unable to send to url[" + url + "]", e);
            throw AxisFault.makeFault(e);
        }
    }   

    private void addCustomHeaders(MessageContext msgContext, Request request) {
    
        boolean isCustomUserAgentSet = false;
        // set the custom headers, if available
        Object httpHeadersObj = msgContext.getProperty(HTTPConstants.HTTP_HEADERS);
        if (httpHeadersObj != null) {
            if (httpHeadersObj instanceof List) {
                List httpHeaders = (List) httpHeadersObj;
                for (int i = 0; i < httpHeaders.size(); i++) {
                    NamedValue nv = (NamedValue) httpHeaders.get(i);
                    if (nv != null) {
                        if (HTTPConstants.HEADER_USER_AGENT.equals(nv.getName())) {
                            isCustomUserAgentSet = true;
                        }
                        request.addHeader(nv.getName(), nv.getValue());
                    }
                }
    
            }
            if (httpHeadersObj instanceof Map) {
                Map httpHeaders = (Map) httpHeadersObj;
                for (Iterator iterator = httpHeaders.entrySet().iterator(); iterator.hasNext();) {
                    Map.Entry entry = (Map.Entry) iterator.next();
                    String key = (String) entry.getKey();
                    String value = (String) entry.getValue();
                    if (HTTPConstants.HEADER_USER_AGENT.equals(key)) {
                        isCustomUserAgentSet = true;
                    }
                    request.addHeader(key, value);
                }
            }
        }
    
        // we have to consider the TRANSPORT_HEADERS map as well
        Map transportHeaders = (Map) msgContext.getProperty(MessageContext.TRANSPORT_HEADERS);
        if (transportHeaders != null) {
            removeUnwantedHeaders(msgContext);
    
            Set headerEntries = transportHeaders.entrySet();
    
            for (Object headerEntry : headerEntries) {
                if (headerEntry instanceof Map.Entry) {
                    Header[] headers = request.getRequestHeaders();
    
                    boolean headerAdded = false;
                    for (Header header : headers) {
                        if (header.getName() != null
                                && header.getName().equals(((Map.Entry) headerEntry).getKey())) {
                            headerAdded = true;
                            break;
                        }
                    }
    
                    if (!headerAdded) {
                        request.addHeader(((Map.Entry) headerEntry).getKey().toString(),
                                ((Map.Entry) headerEntry).getValue().toString());
                    }
                }
            }
        }
    
        if (!isCustomUserAgentSet) {
            String userAgentString = getUserAgent(msgContext);
            request.setHeader(HTTPConstants.HEADER_USER_AGENT, userAgentString);
        }
    
    }

    /**
     * Remove unwanted headers from the transport headers map of outgoing
     * request. These are headers which should be dictated by the transport and
     * not the user. We remove these as these may get copied from the request
     * messages
     * 
     * @param msgContext
     *            the Axis2 Message context from which these headers should be
     *            removed
     */
    private void removeUnwantedHeaders(MessageContext msgContext) {
        Map headers = (Map) msgContext.getProperty(MessageContext.TRANSPORT_HEADERS);
    
        if (headers == null || headers.isEmpty()) {
            return;
        }
    
        Iterator iter = headers.keySet().iterator();
        while (iter.hasNext()) {
            String headerName = (String) iter.next();
            if (HTTP.CONN_DIRECTIVE.equalsIgnoreCase(headerName)
                    || HTTP.TRANSFER_ENCODING.equalsIgnoreCase(headerName)
                    || HTTP.DATE_HEADER.equalsIgnoreCase(headerName)
                    || HTTP.CONTENT_TYPE.equalsIgnoreCase(headerName)
                    || HTTP.CONTENT_LEN.equalsIgnoreCase(headerName)) {
                iter.remove();
            }
        }
    }

    private String getUserAgent(MessageContext messageContext) {
        String userAgentString = "Axis2";
        boolean locked = false;
        if (messageContext.getParameter(HTTPConstants.USER_AGENT) != null) {
            OMElement userAgentElement = messageContext.getParameter(HTTPConstants.USER_AGENT)
                    .getParameterElement();
            userAgentString = userAgentElement.getText().trim();
            OMAttribute lockedAttribute = userAgentElement.getAttribute(new QName("locked"));
            if (lockedAttribute != null) {
                if (lockedAttribute.getAttributeValue().equalsIgnoreCase("true")) {
                    locked = true;
                }
            }
        }
        // Runtime overing part
        if (!locked) {
            if (messageContext.getProperty(HTTPConstants.USER_AGENT) != null) {
                userAgentString = (String) messageContext.getProperty(HTTPConstants.USER_AGENT);
            }
        }
    
        return userAgentString;
    }

    /**
     * This is used to get the dynamically set time out values from the message
     * context. If the values are not available or invalid then the default
     * values or the values set by the configuration will be used
     * 
     * @param msgContext
     *            the active MessageContext
     * @param request
     *            request
     */
    private void setTimeouts(MessageContext msgContext, Request request) {
        // If the SO_TIMEOUT of CONNECTION_TIMEOUT is set by dynamically the
        // override the static config
        Integer tempSoTimeoutProperty = (Integer) msgContext.getProperty(HTTPConstants.SO_TIMEOUT);
        Integer tempConnTimeoutProperty = (Integer) msgContext
                .getProperty(HTTPConstants.CONNECTION_TIMEOUT);
        long timeout = msgContext.getOptions().getTimeOutInMilliSeconds();

        if (tempConnTimeoutProperty != null) {
            // timeout for initial connection
            request.setConnectionTimeout(tempConnTimeoutProperty);
        }

        if (tempSoTimeoutProperty != null) {
            // SO_TIMEOUT -- timeout for blocking reads
            request.setSocketTimeout(tempSoTimeoutProperty);
        } else {
            // set timeout in client
            if (timeout > 0) {
                request.setSocketTimeout((int) timeout);
            }
        }
    }

    private void obtainHTTPHeaderInformation(Request request, MessageContext msgContext) throws AxisFault {
        // Set RESPONSE properties onto the REQUEST message context. They will
        // need to be copied off the request context onto
        // the response context elsewhere, for example in the
        // OutInOperationClient.
        msgContext.setProperty(
                MessageContext.TRANSPORT_HEADERS,
                new CommonsTransportHeaders(request.getResponseHeaders()));
        msgContext.setProperty(
                HTTPConstants.MC_HTTP_STATUS_CODE,
                new Integer(request.getStatusCode()));
        
        String contentTypeString = request.getResponseHeader(HTTPConstants.HEADER_CONTENT_TYPE);
        if (contentTypeString != null) {
            ContentType contentType;
            try {
                contentType = new ContentType(contentTypeString);
            } catch (ParseException ex) {
                throw AxisFault.makeFault(ex);
            }
            String charSetEnc = contentType.getParameter(HTTPConstants.CHAR_SET_ENCODING);
            MessageContext inMessageContext = msgContext.getOperationContext().getMessageContext(
                    WSDLConstants.MESSAGE_LABEL_IN_VALUE);
            if (inMessageContext != null) {
                inMessageContext.setProperty(Constants.Configuration.CONTENT_TYPE, contentTypeString);
                inMessageContext.setProperty(Constants.Configuration.CHARACTER_SET_ENCODING, charSetEnc);
            } else {
                // Transport details will be stored in a HashMap so that anybody
                // interested can
                // retrieve them
                Map transportInfoMap = new HashMap();
                transportInfoMap.put(Constants.Configuration.CONTENT_TYPE, contentTypeString);
                transportInfoMap.put(Constants.Configuration.CHARACTER_SET_ENCODING, charSetEnc);
                // the HashMap is stored in the outgoing message.
                msgContext.setProperty(Constants.Configuration.TRANSPORT_INFO_MAP, transportInfoMap);
            }
        }

        Map cookies = request.getCookies();
        if (cookies != null) {
            String customCookieId = (String) msgContext.getProperty(Constants.CUSTOM_COOKIE_ID);
            String cookieString = null;
            if (customCookieId != null) {
                cookieString = buildCookieString(cookies, customCookieId);
            }
            if (cookieString == null) {
                cookieString = buildCookieString(cookies, Constants.SESSION_COOKIE);
            }
            if (cookieString == null) {
                cookieString = buildCookieString(cookies, Constants.SESSION_COOKIE_JSESSIONID);
            }
            if (cookieString != null) {
                msgContext.getServiceContext().setProperty(HTTPConstants.COOKIE_STRING, cookieString);
            }
        }
    }

    private String buildCookieString(Map cookies, String name) {
        String value = cookies.get(name);
        return value == null ? null : name + "=" + value;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy