// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_HTTP_HTTP_STREAM_REQUEST_H_
#define NET_HTTP_HTTP_STREAM_REQUEST_H_

#include "base/scoped_ptr.h"
#include "net/base/completion_callback.h"
#include "net/base/host_mapping_rules.h"
#include "net/base/ssl_config_service.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_controller.h"
#include "net/http/http_alternate_protocols.h"
#include "net/http/http_stream_factory.h"
#include "net/http/stream_factory.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/client_socket_handle.h"

namespace net {

class ClientSocketHandle;
class HttpAuthController;
class HttpNetworkSession;
class HttpProxySocketParams;
class HttpStreamFactory;
class SOCKSSocketParams;
class SSLSocketParams;
class StreamRequestDelegate;
class TCPSocketParams;
struct HttpRequestInfo;

// An HttpStreamRequest exists for each stream which is in progress of being
// created for the StreamFactory.
class HttpStreamRequest : public StreamFactory::StreamRequestJob {
 public:
  HttpStreamRequest(HttpStreamFactory* factory,
                    const scoped_refptr<HttpNetworkSession>& session);
  virtual ~HttpStreamRequest();

  // StreamRequest interface
  virtual void Start(const HttpRequestInfo* request_info,
                     SSLConfig* ssl_config,
                     ProxyInfo* proxy_info,
                     StreamFactory::StreamRequestDelegate* delegate,
                     const BoundNetLog& net_log);
  virtual void Cancel();
  virtual int RestartWithCertificate(
      const scoped_refptr<X509Certificate>& client_cert);
  virtual int RestartTunnelWithProxyAuth(const string16& username,
                                         const string16& password);
  virtual LoadState GetLoadState() const;

  virtual bool was_alternate_protocol_available() const {
      return was_alternate_protocol_available_;
  }
  virtual bool was_npn_negotiated() const { return was_npn_negotiated_; }
  virtual bool using_spdy() const { return using_spdy_; }

 private:
  enum AlternateProtocolMode {
    kUnspecified,  // Unspecified, check HttpAlternateProtocols
    kUsingAlternateProtocol,  // Using an alternate protocol
    kDoNotUseAlternateProtocol,  // Failed to connect once, do not try again.
  };

  enum State {
    STATE_RESOLVE_PROXY,
    STATE_RESOLVE_PROXY_COMPLETE,
    STATE_INIT_CONNECTION,
    STATE_INIT_CONNECTION_COMPLETE,
    STATE_WAITING_USER_ACTION,
    STATE_RESTART_TUNNEL_AUTH,
    STATE_RESTART_TUNNEL_AUTH_COMPLETE,
    STATE_CREATE_STREAM,
    STATE_CREATE_STREAM_COMPLETE,
    STATE_DRAIN_BODY_FOR_AUTH_RESTART,
    STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE,
    STATE_NONE
  };

  const HttpRequestInfo& request_info() const;
  ProxyInfo* proxy_info() const;
  SSLConfig* ssl_config() const;

  // Callbacks to the delegate.
  void OnStreamReadyCallback(HttpStreamHandle* stream);
  void OnStreamFailedCallback(int result);
  void OnCertificateErrorCallback(int result, const SSLInfo& ssl_info);
  void OnNeedsProxyAuthCallback(
      const scoped_refptr<HttpAuthController>& auth_controller,
      const HttpResponseInfo& response_info);
  void OnNeedsClientAuthCallback(
      const scoped_refptr<SSLCertRequestInfo>& cert_info);

  void OnIOComplete(int result);
  int RunLoop(int result);
  int DoLoop(int result);

  // Each of these methods corresponds to a State value.  Those with an input
  // argument receive the result from the previous state.  If a method returns
  // ERR_IO_PENDING, then the result from OnIOComplete will be passed to the
  // next state method as the result arg.
  int DoResolveProxy();
  int DoResolveProxyComplete(int result);
  int DoInitConnection();
  int DoInitConnectionComplete(int result);
  int DoWaitingUserAction(int result);
  int DoCreateStream();
  int DoCreateStreamComplete(int result);
  int DoRestartTunnelAuth();
  int DoRestartTunnelAuthComplete(int result);

  // Returns a newly create SSLSocketParams, and sets several
  // fields of ssl_config_.
  scoped_refptr<SSLSocketParams> GenerateSslParams(
      scoped_refptr<TCPSocketParams> tcp_params,
      scoped_refptr<HttpProxySocketParams> http_proxy_params,
      scoped_refptr<SOCKSSocketParams> socks_params,
      ProxyServer::Scheme proxy_scheme,
      bool want_spdy_over_npn);

  // AlternateProtocol API
  void MarkBrokenAlternateProtocolAndFallback();

  // Retrieve SSLInfo from our SSL Socket.
  // This must only be called when we are using an SSLSocket.
  // After calling, the caller can use ssl_info_.
  void GetSSLInfo();

  // Called when we encounter a network error that could be resolved by trying
  // a new proxy configuration.  If there is another proxy configuration to try
  // then this method sets next_state_ appropriately and returns either OK or
  // ERR_IO_PENDING depending on whether or not the new proxy configuration is
  // available synchronously or asynchronously.  Otherwise, the given error
  // code is simply returned.
  int ReconsiderProxyAfterError(int error);

  // Called to handle a certificate error.  Stores the certificate in the
  // allowed_bad_certs list, and checks if the error can be ignored.  Returns
  // OK if it can be ignored, or the error code otherwise.
  int HandleCertificateError(int error);

  // Called to handle a client certificate request.
  int HandleCertificateRequest(int error);

  // Called to possibly recover from an SSL handshake error.  Sets next_state_
  // and returns OK if recovering from the error.  Otherwise, the same error
  // code is returned.
  int HandleSSLHandshakeError(int error);

  // Record histograms of latency until Connect() completes.
  static void LogHttpConnectedMetrics(const ClientSocketHandle& handle);

  const HttpRequestInfo* request_info_;  // Use request_info().
  ProxyInfo* proxy_info_;  // Use proxy_info().
  SSLConfig* ssl_config_;  // Use ssl_config().

  scoped_refptr<HttpNetworkSession> session_;
  CompletionCallbackImpl<HttpStreamRequest> io_callback_;
  scoped_ptr<ClientSocketHandle> connection_;
  scoped_refptr<HttpStreamFactory> factory_;
  StreamFactory::StreamRequestDelegate* delegate_;
  BoundNetLog net_log_;
  State next_state_;
  ProxyService::PacRequest* pac_request_;
  SSLInfo ssl_info_;
  // The hostname and port of the endpoint.  This is not necessarily the one
  // specified by the URL, due to Alternate-Protocol or fixed testing ports.
  HostPortPair endpoint_;

  // True if handling a HTTPS request, or using SPDY with SSL
  bool using_ssl_;

  // True if this network transaction is using SPDY instead of HTTP.
  bool using_spdy_;

  // Force spdy for all connections.
  bool force_spdy_always_;

  // Force spdy only for SSL connections.
  bool force_spdy_over_ssl_;

  // The certificate error while using SPDY over SSL for insecure URLs.
  int spdy_certificate_error_;

  scoped_refptr<HttpAuthController>
    auth_controllers_[HttpAuth::AUTH_NUM_TARGETS];

  AlternateProtocolMode alternate_protocol_mode_;

  // Only valid if |alternate_protocol_mode_| == kUsingAlternateProtocol.
  HttpAlternateProtocols::Protocol alternate_protocol_;

  // True when the tunnel is in the process of being established - we can't
  // read from the socket until the tunnel is done.
  bool establishing_tunnel_;

  scoped_ptr<HttpStreamHandle> stream_;

  // True if finding the connection for this request found an alternate
  // protocol was available.
  bool was_alternate_protocol_available_;

  // True if we negotiated NPN.
  bool was_npn_negotiated_;

  // Indicates that this StreamRequest has been cancelled.  Note that once
  // this has been cancelled, input parameters passed into the StreamRequest
  // can no longer be touched (as they belong to the requestor).
  bool cancelled_;

  DISALLOW_COPY_AND_ASSIGN(HttpStreamRequest);
};

}  // namespace net

#endif  // NET_HTTP_HTTP_STREAM_REQUEST_H_