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

org.parosproxy.paros.network.HttpSender Maven / Gradle / Ivy

/*
 *
 * Paros and its related class files.
 *
 * Paros is an HTTP/HTTPS proxy for assessing web application security.
 * Copyright (C) 2003-2004 Chinotec Technologies Company
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the Clarified Artistic License
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Clarified Artistic License for more details.
 *
 * You should have received a copy of the Clarified Artistic License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
// ZAP: 2011/09/19 Added debugging
// ZAP: 2012/04/23 Removed unnecessary cast.
// ZAP: 2012/05/08 Use custom http client on "Connection: Upgrade" in executeMethod().
//                 Retrieve upgraded socket and save for later use in send() method.
// ZAP: 2012/08/07 Issue 342 Support the HttpSenderListener
// ZAP: 2012/12/27 Do not read request body on Server-Sent Event streams.
// ZAP: 2013/01/03 Resolved Checkstyle issues: removed throws HttpException
//                 declaration where IOException already appears,
//                 introduced two helper methods for notifying listeners.
// ZAP: 2013/01/19 Issue 459: Active scanner locking
// ZAP: 2013/01/23 Clean up of exception handling/logging.
// ZAP: 2013/01/30 Issue 478: Allow to choose to send ZAP's managed cookies on
// a single Cookie request header and set it as the default
// ZAP: 2013/07/10 Issue 720: Cannot send non standard http methods
// ZAP: 2013/07/14 Issue 729: Update NTLM authentication code
// ZAP: 2013/07/25 Added support for sending the message from the perspective of a User
// ZAP: 2013/08/31 Reauthentication when sending a message from the perspective of a User
// ZAP: 2013/09/07 Switched to using HttpState for requesting User for cookie management
// ZAP: 2013/09/26 Issue 716: ZAP flags its own HTTP responses
// ZAP: 2013/09/26 Issue 656: Content-length: 0 in GET requests
// ZAP: 2013/09/29 Deprecating configuring HTTP Authentication through Options
// ZAP: 2013/11/16 Issue 837: Update, always, the HTTP request sent/forward by ZAP's proxy
// ZAP: 2013/12/11 Corrected log.info calls to use debug
// ZAP: 2014/03/04 Issue 1043: Custom active scan dialog
// ZAP: 2014/03/23 Issue 412: Enable unsafe SSL/TLS renegotiation option not saved
// ZAP: 2014/03/23 Issue 416: Normalise how multiple related options are managed throughout ZAP
// and enhance the usability of some options
// ZAP: 2014/03/29 Issue 1132: HttpSender ignores the "Send single cookie request header" option
// ZAP: 2014/08/14 Issue 1291: 407 Proxy Authentication Required while active scanning
// ZAP: 2014/10/25 Issue 1062: Added a getter for the HttpClient.
// ZAP: 2014/10/28 Issue 1390: Force https on cfu call
// ZAP: 2014/11/25 Issue 1411: Changed getUser() visibility
// ZAP: 2014/12/11 Added JavaDoc to constructor and removed the instance variable allowState.
// ZAP: 2015/04/09 Allow to specify the maximum number of retries on I/O error.
// ZAP: 2015/04/09 Allow to specify the maximum number of redirects.
// ZAP: 2015/04/09 Allow to specify if circular redirects are allowed.
// ZAP: 2015/06/12 Issue 1459: Add an HTTP sender listener script
// ZAP: 2016/05/24 Issue 2463: Websocket not proxied when outgoing proxy is set
// ZAP: 2016/05/27 Issue 2484: Circular Redirects
// ZAP: 2016/06/08 Set User-Agent header defined in options as default for (internal) CONNECT
// requests
// ZAP: 2016/06/10 Allow to validate the URI of the redirections before being followed
// ZAP: 2016/08/04 Added removeListener(..)
// ZAP: 2016/12/07 Add initiator constant for AJAX spider requests
// ZAP: 2016/12/12 Add initiator constant for Forced Browse requests
// ZAP: 2017/03/27 Introduce HttpRequestConfig.
// ZAP: 2017/06/12 Allow to ignore listeners.
// ZAP: 2017/06/19 Allow to send a request with custom socket timeout.
// ZAP: 2017/11/20 Add initiator constant for Token Generator requests.
// ZAP: 2017/11/27 Use custom CookieSpec (ZapCookieSpec).
// ZAP: 2017/12/20 Apply socket connect timeout (Issue 4171).
// ZAP: 2018/02/06 Make the lower case changes locale independent (Issue 4327).
// ZAP: 2018/02/19 Added WEB_SOCKET_INITIATOR.
// ZAP: 2018/02/23 Issue 1161: Allow to override the global session tracking setting
//                 Fix Session Tracking button sync
// ZAP: 2018/08/03 Added AUTHENTICATION_HELPER_INITIATOR.
// ZAP: 2018/09/17 Set the user to messages created for redirections (Issue 2531).
// ZAP: 2018/10/12 Deprecate getClient(), it exposes implementation details.
// ZAP: 2019/03/24 Removed commented and unused sendAndReceive method.
// ZAP: 2019/06/01 Normalise line endings.
// ZAP: 2019/06/05 Normalise format/style.
// ZAP: 2019/08/19 Reinstate proxy auth credentials when HTTP state is changed.
// ZAP: 2019/09/17 Use remove() instead of set(null) on IN_LISTENER.
// ZAP: 2019/09/25 Add option to disable cookies
// ZAP: 2020/04/20 Configure if the names should be resolved or not (Issue 29).
// ZAP: 2020/09/04 Added AUTHENTICATION_POLL_INITIATOR
// ZAP: 2020/11/26 Use Log4j 2 classes for logging.
// ZAP: 2020/12/09 Set content encoding to the response body.
// ZAP: 2021/05/14 Remove redundant type arguments and empty statement.
// ZAP: 2022/01/04 Add initiator constant OAST_INITIATOR for OAST requests.
// ZAP: 2022/04/08 Deprecate getSSLConnector() and executeMethod.
// ZAP: 2022/04/10 Add support for unencoded redirects
// ZAP: 2022/04/11 Deprecate set/getUserAgent() and remove userAgent/modifyUserAgent().
// ZAP: 2022/04/11 Prevent null listeners and add JavaDoc to add/removeListener.
// ZAP: 2022/04/23 Use main connection options directly.
// ZAP: 2022/04/24 Notify listeners of all redirects followed.
// ZAP: 2022/04/24 Move network initialisations from ZAP class.
// ZAP: 2022/04/24 Allow to download to file.
// ZAP: 2022/04/27 Expose global HTTP state enabled status.
// ZAP: 2022/04/27 Use latest proxy settings always.
// ZAP: 2022/04/29 Deprecate setAllowCircularRedirects.
// ZAP: 2022/05/04 Always use single cookie request header.
// ZAP: 2022/05/04 Use latest timeout/user-agent always.
// ZAP: 2022/05/20 Address deprecation warnings with ConnectionParam.
// ZAP: 2022/05/29 Remove redundant checks and create SSLConnector always.
// ZAP: 2022/05/30 Use shared connection pool.
// ZAP: 2022/06/03 Remove commented code and make listeners comparator final.
// ZAP: 2022/06/03 Move implementation to HttpSenderParos.
// ZAP: 2022/06/05 Remove usage of HttpException.
// ZAP: 2022/06/07 Address deprecation warnings with HttpSenderParos.
// ZAP: 2022/06/13 Added param digger initiator.
// ZAP: 2022/12/09 Allow to restore HttpSenderImpl state.
// ZAP: 2023/03/31 Deprecate CFU initiator and add file download with configs.
package org.parosproxy.paros.network;

import java.io.IOException;
import java.nio.file.Path;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpState;
import org.zaproxy.zap.network.HttpRedirectionValidator;
import org.zaproxy.zap.network.HttpRequestConfig;
import org.zaproxy.zap.network.HttpSenderContext;
import org.zaproxy.zap.network.HttpSenderImpl;
import org.zaproxy.zap.network.HttpSenderListener;
import org.zaproxy.zap.users.User;

public class HttpSender {
    public static final int PROXY_INITIATOR = 1;
    public static final int ACTIVE_SCANNER_INITIATOR = 2;
    public static final int SPIDER_INITIATOR = 3;
    public static final int FUZZER_INITIATOR = 4;
    public static final int AUTHENTICATION_INITIATOR = 5;
    public static final int MANUAL_REQUEST_INITIATOR = 6;
    /** @deprecated (2.13.0) CFU requests are sent without notifying listeners. */
    @Deprecated(since = "2.13.0")
    public static final int CHECK_FOR_UPDATES_INITIATOR = 7;

    public static final int BEAN_SHELL_INITIATOR = 8;
    public static final int ACCESS_CONTROL_SCANNER_INITIATOR = 9;
    public static final int AJAX_SPIDER_INITIATOR = 10;
    public static final int FORCED_BROWSE_INITIATOR = 11;
    public static final int TOKEN_GENERATOR_INITIATOR = 12;
    public static final int WEB_SOCKET_INITIATOR = 13;
    public static final int AUTHENTICATION_HELPER_INITIATOR = 14;
    public static final int AUTHENTICATION_POLL_INITIATOR = 15;
    public static final int OAST_INITIATOR = 16;
    public static final int PARAM_DIGGER_INITIATOR = 17;

    private static final HttpRequestConfig NO_REDIRECTS = HttpRequestConfig.builder().build();
    private static final HttpRequestConfig FOLLOW_REDIRECTS =
            HttpRequestConfig.builder().setFollowRedirects(true).build();

    @SuppressWarnings("deprecation")
    private static final HttpSenderParos PAROS_IMPL = new HttpSenderParos();

    @SuppressWarnings("rawtypes")
    private static HttpSenderImpl impl = PAROS_IMPL;

    private static Object implState;

    private HttpSenderContext ctx;

    private final int initiator;

    /** Note: Not part of the public API. */
    public static  void setImpl(HttpSenderImpl impl) {
        if (HttpSender.impl != PAROS_IMPL) {
            implState = HttpSender.impl.saveState();
        }

        HttpSender.impl = impl == null ? PAROS_IMPL : impl;

        if (HttpSender.impl != PAROS_IMPL) {
            HttpSender.impl.restoreState(implState);
            implState = null;
        }
    }

    /**
     * Constructs an {@code HttpSender}.
     *
     * 

The {@code initiator} is used to indicate the component that is sending the messages when * the {@code HttpSenderListener}s are notified of messages sent and received. * * @param connectionParam the parameters used to setup the connections to target hosts * @param useGlobalState {@code true} if the messages sent/received should use the global HTTP * state, {@code false} if should use a non shared HTTP state * @param initiator the ID of the initiator of the HTTP messages sent * @see ConnectionParam#getHttpState() * @see HttpSenderListener * @see HttpMessage#getRequestingUser() * @deprecated (2.12.0) Use {@link #HttpSender(int)} instead, refer also to {@link * #setUseGlobalState(boolean)}. */ @Deprecated public HttpSender(ConnectionParam connectionParam, boolean useGlobalState, int initiator) { this(useGlobalState, initiator); } /** * Constructs an {@code HttpSender}. * *

Refer to {@link #setUseGlobalState(boolean)} to know how the HTTP state is managed. * *

The {@code initiator} is used to indicate the component that is sending the messages when * the {@code HttpSenderListener}s are notified of messages sent and received. * * @param initiator the ID of the initiator of the HTTP messages sent * @since 2.12.0 * @see HttpSenderListener */ public HttpSender(int initiator) { this(true, initiator); } private HttpSender(boolean useGlobalState, int initiator) { this.initiator = initiator; HttpSenderContext createdCtx = impl.createContext(this, initiator); if (impl.getContext(this) == null) { ctx = createdCtx; } setUseGlobalState(useGlobalState); setUseCookies(true); } private HttpSenderContext getContext() { if (ctx != null) { return ctx; } return impl.getContext(this); } /** * Gets the {@code SSLConnector} of the client. * * @return the {@code SSLConnector} used by the sender. * @deprecated (2.12.0) It will be removed in a following version. */ @Deprecated public static SSLConnector getSSLConnector() { return PAROS_IMPL.getSslConnector(); } /** * Sets whether or not the global state should be used. Defaults to {@code true}. * *

If {@code enableGlobalState} is {@code true} the {@code HttpSender} will use the HTTP * state given by the connections options iff the HTTP state is enabled there otherwise it * doesn't have any state (i.e. cookies are disabled). If {@code enableGlobalState} is {@code * false} it uses a non shared HTTP state. * *

Note: The actual state used is overridden when {@link * #getUser(HttpMessage)} returns non-{@code null}. * * @param enableGlobalState {@code true} if the global state should be used, {@code false} * otherwise. * @since 2.8.0 * @see #isGlobalStateEnabled() * @see #setUseCookies(boolean) */ public void setUseGlobalState(boolean enableGlobalState) { getContext().setUseGlobalState(enableGlobalState); } /** * Tells whether or not the global HTTP state is enabled. * * @return {@code true} if the global HTTP state is enabled, {@code false} otherwise. * @since 2.12.0 * @see #setUseGlobalState(boolean) */ public boolean isGlobalStateEnabled() { return impl.isGlobalStateEnabled(); } /** * Sets whether or not the requests sent should keep track of cookies. * * @param shouldUseCookies {@code true} if cookies should be used, {@code false} otherwise. * @since 2.9.0 * @see #setUseGlobalState(boolean) */ public void setUseCookies(boolean shouldUseCookies) { getContext().setUseCookies(shouldUseCookies); } /** * Executes the given method. * * @param method the method. * @param state the state, might be {@code null}. * @return the status code. * @throws IOException if an error occurred while executing the method. * @deprecated (2.12.0) Use one of the {@code sendAndReceive} methods. It will be removed in a * following version. */ @Deprecated public int executeMethod(HttpMethod method, HttpState state) throws IOException { HttpSenderContext ctxTemp = getContext(); if (!(ctxTemp instanceof HttpSenderContextParos)) { ctxTemp = PAROS_IMPL.createContext(this, initiator); } return PAROS_IMPL.executeMethodImpl((HttpSenderContextParos) ctxTemp, method, state); } /** @deprecated (2.12.0) No longer needed. */ @Deprecated public void shutdown() {} /** * Downloads the response (body) to the given file. * *

The body in the given {@code message} will be empty. * * @param message the message containing the request to send. * @param file the file where to save the response body. * @throws IOException if an error occurred while sending the request or while downloading. * @since 2.12.0 * @see #setFollowRedirect(boolean) */ public void sendAndReceive(HttpMessage message, Path file) throws IOException { sendAndReceive(message, null, file); } /** * Downloads the response (body) to the given file, using the given request configurations. * *

The body in the given {@code message} will be empty. * * @param message the message containing the request to send. * @param requestConfig the request configurations. * @param file the file where to save the response body. * @throws IOException if an error occurred while sending the request or while downloading. * @since 2.13.0 */ public void sendAndReceive(HttpMessage message, HttpRequestConfig requestConfig, Path file) throws IOException { sendImpl(requestConfig, message, file); } public void sendAndReceive(HttpMessage msg) throws IOException { sendImpl(null, msg, null); } /** * Send and receive a HttpMessage. * * @param msg * @param isFollowRedirect * @throws IOException * @see #sendAndReceive(HttpMessage, HttpRequestConfig) */ public void sendAndReceive(HttpMessage msg, boolean isFollowRedirect) throws IOException { sendImpl(isFollowRedirect ? FOLLOW_REDIRECTS : NO_REDIRECTS, msg, null); } /** * Gets the user set in this {@code HttpSender} if any, otherwise the one in the given {@code * HttpMessage}. * * @param msg usually the message being sent, that might have a user. * @return the user set in the {@code HttpSender} or in the given {@code HttpMessage}. Might be * {@code null} if no user set. * @throws NullPointerException if the given message is {@code null}. * @since 2.4.1 * @see #setUser(User) * @see HttpMessage#getRequestingUser() */ public User getUser(HttpMessage msg) { return getContext().getUser(msg); } public void setFollowRedirect(boolean followRedirect) { getContext().setFollowRedirects(followRedirect); } /** * @return Returns the userAgent. * @deprecated (2.12.0) No longer supported, it returns an empty string. * @see #setUserAgent(String) */ @Deprecated public static String getUserAgent() { return ""; } /** * @param userAgent The userAgent to set. * @deprecated (2.12.0) No longer supported, use a {@link HttpSenderListener} to actually set * the user agent. */ @Deprecated public static void setUserAgent(String userAgent) {} /** * Adds the given listener to be notified of each message sent/received by each {@code * HttpSender}. * *

The listener might be notified concurrently. * * @param listener the listener to add. * @since 2.0.0 * @throws NullPointerException if the given listener is {@code null}. */ public static void addListener(HttpSenderListener listener) { impl.addListener(listener); } /** * Removes the given listener. * * @param listener the listener to remove. * @since 2.0.0 * @throws NullPointerException if the given listener is {@code null}. */ public static void removeListener(HttpSenderListener listener) { impl.removeListener(listener); } /** * Set the user to scan as. If null then the current session will be used. * * @param user */ public void setUser(User user) { getContext().setUser(user); } /** * @return the HTTP client implementation. * @deprecated (2.8.0) Do not use, this exposes implementation details which might change * without warning. It will be removed in a following version. */ @Deprecated public HttpClient getClient() { return PAROS_IMPL.getClient(); } /** * Sets whether or not the authentication headers ("Authorization" and "Proxy-Authorization") * already present in the request should be removed if received an authentication challenge * (status codes 401 and 407). * *

If {@code true} new authentication headers will be generated and the old ones removed * otherwise the authentication headers already present in the request will be used to * authenticate. * *

Default is {@code false}, i.e. use the headers already present in the request header. * *

Processes that reuse messages previously sent should consider setting this to {@code * true}, otherwise new authentication challenges might fail. * * @param removeHeaders {@code true} if the the authentication headers already present should be * removed when challenged, {@code false} otherwise */ public void setRemoveUserDefinedAuthHeaders(boolean removeHeaders) { getContext().setRemoveUserDefinedAuthHeaders(removeHeaders); } /** * Sets the maximum number of retries of an unsuccessful request caused by I/O errors. * *

The default number of retries is 3. * * @param retries the number of retries * @throws IllegalArgumentException if {@code retries} is negative. * @since 2.4.0 */ public void setMaxRetriesOnIOError(int retries) { getContext().setMaxRetriesOnIoError(retries); } /** * Sets the maximum number of redirects that will be followed before failing with an exception. * *

The default maximum number of redirects is 100. * * @param maxRedirects the maximum number of redirects * @throws IllegalArgumentException if {@code maxRedirects} is negative. * @since 2.4.0 */ public void setMaxRedirects(int maxRedirects) { getContext().setMaxRedirects(maxRedirects); } /** * Sets whether or not circular redirects are allowed. * *

Circular redirects happen when a request redirects to itself, or when a same request was * already accessed in a chain of redirects. * *

Since 2.5.0, the default is to allow circular redirects. * * @param allow {@code true} if circular redirects should be allowed, {@code false} otherwise * @since 2.4.0 * @deprecated (2.12.0) No longer supported, the circular redirects are allowed always. If * needed they can be prevented with a custom {@link HttpRedirectionValidator}. */ @Deprecated public void setAllowCircularRedirects(boolean allow) {} /** * Sends the request of given HTTP {@code message} with the given configurations. * * @param message the message that will be sent * @param requestConfig the request configurations. * @throws IllegalArgumentException if any of the parameters is {@code null} * @throws IOException if an error occurred while sending the message or following the * redirections * @since 2.6.0 * @see #sendAndReceive(HttpMessage, boolean) */ public void sendAndReceive(HttpMessage message, HttpRequestConfig requestConfig) throws IOException { sendImpl(requestConfig, message, null); } @SuppressWarnings("unchecked") private void sendImpl(HttpRequestConfig requestConfig, HttpMessage message, Path file) throws IOException { if (ctx == null) { impl.sendAndReceive(this, requestConfig, message, file); } else { impl.sendAndReceive(ctx, requestConfig, message, file); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy