美文网首页网络协议设计与实现Android技术知识程序员
Cronet android 设计与实现分析——备选服务机制

Cronet android 设计与实现分析——备选服务机制

作者: hanpfei | 来源:发表于2016-10-10 19:16 被阅读976次

    前面我们分析到,在URLRequestHttpJob::StartTransactionInternal()中,会通过URLRequestContextHttpTransactionFactory创建HttpTransaction,在URLRequestContextBuilder::Build()中创建HttpTransactionFactory的过程如下:

      storage->set_http_network_session(
          base::WrapUnique(new HttpNetworkSession(network_session_params)));
    
      std::unique_ptr<HttpTransactionFactory> http_transaction_factory;
      if (http_cache_enabled_) {
        std::unique_ptr<HttpCache::BackendFactory> http_cache_backend;
        if (http_cache_params_.type != HttpCacheParams::IN_MEMORY) {
          BackendType backend_type =
              http_cache_params_.type == HttpCacheParams::DISK
                  ? CACHE_BACKEND_DEFAULT
                  : CACHE_BACKEND_SIMPLE;
          http_cache_backend.reset(new HttpCache::DefaultBackend(
              DISK_CACHE, backend_type, http_cache_params_.path,
              http_cache_params_.max_size, context->GetFileTaskRunner()));
        } else {
          http_cache_backend =
              HttpCache::DefaultBackend::InMemory(http_cache_params_.max_size);
        }
    
        LOG(INFO) << "Cache is enabled, to create HttpCache";
        http_transaction_factory.reset(new HttpCache(
            storage->http_network_session(), std::move(http_cache_backend), true));
      } else {
          LOG(INFO) << "Cache is disabled, to create HttpNetworkLayer";
        http_transaction_factory.reset(
            new HttpNetworkLayer(storage->http_network_session()));
      }
      storage->set_http_transaction_factory(std::move(http_transaction_factory));
    

    Chromium net中有两个HttpTransactionFactory的实现,分别是HttpCacheHttpNetworkLayer,它们分别在cache被打开和被关闭时用到。这里还会创建HttpNetworkSession。而在cache打开时,在创建HttpCache的同时,还会为它创建http_cache_backend。

    HttpCache的创建过程(chromium_android/src/net/http/http_cache.cc)如下:

    //-----------------------------------------------------------------------------
    HttpCache::HttpCache(HttpNetworkSession* session,
                         std::unique_ptr<BackendFactory> backend_factory,
                         bool set_up_quic_server_info)
        : HttpCache(base::WrapUnique(new HttpNetworkLayer(session)),
                    std::move(backend_factory),
                    set_up_quic_server_info) {}
    
    HttpCache::HttpCache(std::unique_ptr<HttpTransactionFactory> network_layer,
                         std::unique_ptr<BackendFactory> backend_factory,
                         bool set_up_quic_server_info)
        : net_log_(nullptr),
          backend_factory_(std::move(backend_factory)),
          building_backend_(false),
          bypass_lock_for_test_(false),
          fail_conditionalization_for_test_(false),
          mode_(NORMAL),
          network_layer_(std::move(network_layer)),
          clock_(new base::DefaultClock()),
          weak_factory_(this) {
      HttpNetworkSession* session = network_layer_->GetSession();
      // Session may be NULL in unittests.
      // TODO(mmenke): Seems like tests could be changed to provide a session,
      // rather than having logic only used in unit tests here.
      if (session) {
        net_log_ = session->net_log();
        if (set_up_quic_server_info &&
            !session->quic_stream_factory()->has_quic_server_info_factory()) {
          // QuicStreamFactory takes ownership of QuicServerInfoFactoryAdaptor.
          session->quic_stream_factory()->set_quic_server_info_factory(
              new QuicServerInfoFactoryAdaptor(this));
        }
      }
    }
    

    这里还是会创建HttpNetworkLayer。HttpTransactionFactory相关的几个类之间的关系如下:

    HttpTransactionFactory

    HttpNetworkTransaction表示一个直接的网络事务,可以理解为一个网络连接。HttpNetworkSession用于管理网络连接。HttpNetworkLayer主要用于创建HttpNetworkTransactionHttpCacheHttpCache::Transaction用于处理缓存。HttpCache::Transaction表示一个启用了缓存的网络事务,它会借助于HttpCache保存的HttpNetworkLayer引用创建HttpNetworkTransaction,借助于HttpNetworkTransaction访问网络,并根据需要将结果缓存起来。HttpCache则对缓存进行管理。HttpNetworkLayerHttpCache都是HttpTransactionFactory,而HttpNetworkTransactionHttpCache::Transaction都是HttpTransaction

    我们先不关心启用cache时,HTTP请求的处理流程,来看HttpNetworkLayer。则在URLRequestHttpJob::StartTransactionInternal()中将通过HttpNetworkLayer创建类型为HttpNetworkTransactionHttpTransaction

    HttpNetworkLayer::HttpNetworkLayer(HttpNetworkSession* session)
        : session_(session),
          suspended_(false) {
      DCHECK(session_);
    #if defined(OS_WIN)
      base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
      if (power_monitor)
        power_monitor->AddObserver(this);
    #endif
    }
    
    HttpNetworkLayer::~HttpNetworkLayer() {
    #if defined(OS_WIN)
      base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
      if (power_monitor)
        power_monitor->RemoveObserver(this);
    #endif
    }
    
    int HttpNetworkLayer::CreateTransaction(
        RequestPriority priority,
        std::unique_ptr<HttpTransaction>* trans) {
      if (suspended_)
        return ERR_NETWORK_IO_SUSPENDED;
    
      trans->reset(new HttpNetworkTransaction(priority, GetSession()));
      return OK;
    }
    
    HttpCache* HttpNetworkLayer::GetCache() {
      return NULL;
    }
    
    HttpNetworkSession* HttpNetworkLayer::GetSession() {
      return session_;
    }
    
    void HttpNetworkLayer::OnSuspend() {
      suspended_ = true;
      session_->CloseIdleConnections();
    }
    
    void HttpNetworkLayer::OnResume() {
      suspended_ = false;
    }
    

    URLRequestHttpJob::StartTransactionInternal()创建了HttpTransaction之后,它会执行HttpTransaction的Start()来启动对HTTP事务的处理,HttpNetworkTransaction的Start()定义如下:

    int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info,
                                      const CompletionCallback& callback,
                                      const BoundNetLog& net_log) {
      net_log_ = net_log;
      request_ = request_info;
    
      // Now that we have an HttpRequestInfo object, update server_ssl_config_.
      session_->GetSSLConfig(*request_, &server_ssl_config_, &proxy_ssl_config_);
    
      if (request_->load_flags & LOAD_DISABLE_CERT_REVOCATION_CHECKING) {
        server_ssl_config_.rev_checking_enabled = false;
        proxy_ssl_config_.rev_checking_enabled = false;
      }
    
      if (request_->load_flags & LOAD_PREFETCH)
        response_.unused_since_prefetch = true;
    
      next_state_ = STATE_NOTIFY_BEFORE_CREATE_STREAM;
      int rv = DoLoop(OK);
      if (rv == ERR_IO_PENDING)
        callback_ = callback;
      return rv;
    }
    

    Chromium net将所有HTTP请求的处理抽象为几个步骤,并通过一个循环DoLoop()来一步一步地执行。DoLoop()的定义 (chromium_android/src/net/http/http_network_transaction.cc) 如下:

    int HttpNetworkTransaction::DoLoop(int result) {
      DCHECK(next_state_ != STATE_NONE);
    
      int rv = result;
      do {
        State state = next_state_;
        next_state_ = STATE_NONE;
        switch (state) {
          case STATE_NOTIFY_BEFORE_CREATE_STREAM:
            DCHECK_EQ(OK, rv);
            rv = DoNotifyBeforeCreateStream();
            break;
          case STATE_CREATE_STREAM:
            DCHECK_EQ(OK, rv);
            rv = DoCreateStream();
            break;
          case STATE_CREATE_STREAM_COMPLETE:
            rv = DoCreateStreamComplete(rv);
            break;
          case STATE_INIT_STREAM:
            DCHECK_EQ(OK, rv);
            rv = DoInitStream();
            break;
          case STATE_INIT_STREAM_COMPLETE:
            rv = DoInitStreamComplete(rv);
            break;
          case STATE_GENERATE_PROXY_AUTH_TOKEN:
            DCHECK_EQ(OK, rv);
            rv = DoGenerateProxyAuthToken();
            break;
          case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE:
            rv = DoGenerateProxyAuthTokenComplete(rv);
            break;
          case STATE_GENERATE_SERVER_AUTH_TOKEN:
            DCHECK_EQ(OK, rv);
            rv = DoGenerateServerAuthToken();
            break;
          case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE:
            rv = DoGenerateServerAuthTokenComplete(rv);
            break;
          case STATE_GET_PROVIDED_TOKEN_BINDING_KEY:
            DCHECK_EQ(OK, rv);
            rv = DoGetProvidedTokenBindingKey();
            break;
          case STATE_GET_PROVIDED_TOKEN_BINDING_KEY_COMPLETE:
            rv = DoGetProvidedTokenBindingKeyComplete(rv);
            break;
          case STATE_GET_REFERRED_TOKEN_BINDING_KEY:
            DCHECK_EQ(OK, rv);
            rv = DoGetReferredTokenBindingKey();
            break;
          case STATE_GET_REFERRED_TOKEN_BINDING_KEY_COMPLETE:
            rv = DoGetReferredTokenBindingKeyComplete(rv);
            break;
          case STATE_INIT_REQUEST_BODY:
            DCHECK_EQ(OK, rv);
            rv = DoInitRequestBody();
            break;
          case STATE_INIT_REQUEST_BODY_COMPLETE:
            rv = DoInitRequestBodyComplete(rv);
            break;
          case STATE_BUILD_REQUEST:
            DCHECK_EQ(OK, rv);
            net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST);
            rv = DoBuildRequest();
            break;
          case STATE_BUILD_REQUEST_COMPLETE:
            rv = DoBuildRequestComplete(rv);
            break;
          case STATE_SEND_REQUEST:
            DCHECK_EQ(OK, rv);
            rv = DoSendRequest();
            break;
          case STATE_SEND_REQUEST_COMPLETE:
            rv = DoSendRequestComplete(rv);
            net_log_.EndEventWithNetErrorCode(
                NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST, rv);
            break;
          case STATE_READ_HEADERS:
            DCHECK_EQ(OK, rv);
            net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_READ_HEADERS);
            rv = DoReadHeaders();
            break;
          case STATE_READ_HEADERS_COMPLETE:
            rv = DoReadHeadersComplete(rv);
            net_log_.EndEventWithNetErrorCode(
                NetLog::TYPE_HTTP_TRANSACTION_READ_HEADERS, rv);
            break;
          case STATE_READ_BODY:
            DCHECK_EQ(OK, rv);
            net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_READ_BODY);
            rv = DoReadBody();
            break;
          case STATE_READ_BODY_COMPLETE:
            rv = DoReadBodyComplete(rv);
            net_log_.EndEventWithNetErrorCode(
                NetLog::TYPE_HTTP_TRANSACTION_READ_BODY, rv);
            break;
          case STATE_DRAIN_BODY_FOR_AUTH_RESTART:
            DCHECK_EQ(OK, rv);
            net_log_.BeginEvent(
                NetLog::TYPE_HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART);
            rv = DoDrainBodyForAuthRestart();
            break;
          case STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE:
            rv = DoDrainBodyForAuthRestartComplete(rv);
            net_log_.EndEventWithNetErrorCode(
                NetLog::TYPE_HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART, rv);
            break;
          default:
            NOTREACHED() << "bad state";
            rv = ERR_FAILED;
            break;
        }
      } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
    
      return rv;
    }
    

    接下来我们逐个地分析这些步骤。

    Stream的创建

    DoNotifyBeforeCreateStream()执行before_network_start_callback:

    int HttpNetworkTransaction::DoNotifyBeforeCreateStream() {
      next_state_ = STATE_CREATE_STREAM;
      bool defer = false;
      if (!before_network_start_callback_.is_null())
        before_network_start_callback_.Run(&defer);
      if (!defer)
        return OK;
      return ERR_IO_PENDING;
    }
    

    在DoCreateStream()中创建Stream:

    int HttpNetworkTransaction::DoCreateStream() {
      // TODO(mmenke): Remove ScopedTracker below once crbug.com/424359 is fixed.
      tracked_objects::ScopedTracker tracking_profile(
          FROM_HERE_WITH_EXPLICIT_FUNCTION(
              "424359 HttpNetworkTransaction::DoCreateStream"));
    
      response_.network_accessed = true;
    
      next_state_ = STATE_CREATE_STREAM_COMPLETE;
      if (ForWebSocketHandshake()) {
        stream_request_.reset(
            session_->http_stream_factory_for_websocket()
                ->RequestWebSocketHandshakeStream(
                      *request_,
                      priority_,
                      server_ssl_config_,
                      proxy_ssl_config_,
                      this,
                      websocket_handshake_stream_base_create_helper_,
                      net_log_));
      } else {
        stream_request_.reset(
            session_->http_stream_factory()->RequestStream(
                *request_,
                priority_,
                server_ssl_config_,
                proxy_ssl_config_,
                this,
                net_log_));
      }
      DCHECK(stream_request_.get());
      return ERR_IO_PENDING;
    }
    
    ......
    
    bool HttpNetworkTransaction::ForWebSocketHandshake() const {
      return websocket_handshake_stream_base_create_helper_ &&
             request_->url.SchemeIsWSOrWSS();
    }
    

    当请求是一个Websocket请求时,通过HttpNetworkSession的http_stream_factory_for_websocket创建Stream,而其他情况下,则会通过HttpNetworkSession的http_stream_factory创建Stream。

    HttpNetworkSession的创建过程 (chromium_android/src/net/http/http_network_session.cc) 可以看到,http_stream_factory_for_websocket和http_stream_factory都是HttpStreamFactoryImpl

          http_stream_factory_(new HttpStreamFactoryImpl(this, false)),
          http_stream_factory_for_websocket_(new HttpStreamFactoryImpl(this, true)),
    

    为Websocket之外的其它请求创建Stream的过程 (chromium_android/src/net/http/http_stream_factory_impl.cc) 为:

    HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session,
                                                 bool for_websockets)
        : session_(session),
          job_factory_(new DefaultJobFactory()),
          for_websockets_(for_websockets) {}
    
    HttpStreamRequest* HttpStreamFactoryImpl::RequestStream(
        const HttpRequestInfo& request_info,
        RequestPriority priority,
        const SSLConfig& server_ssl_config,
        const SSLConfig& proxy_ssl_config,
        HttpStreamRequest::Delegate* delegate,
        const BoundNetLog& net_log) {
      DCHECK(!for_websockets_);
      return RequestStreamInternal(request_info, priority, server_ssl_config,
                                   proxy_ssl_config, delegate, nullptr,
                                   HttpStreamRequest::HTTP_STREAM, net_log);
    }
    
    ......
    
    HttpStreamRequest* HttpStreamFactoryImpl::RequestStreamInternal(
        const HttpRequestInfo& request_info,
        RequestPriority priority,
        const SSLConfig& server_ssl_config,
        const SSLConfig& proxy_ssl_config,
        HttpStreamRequest::Delegate* delegate,
        WebSocketHandshakeStreamBase::CreateHelper*
            websocket_handshake_stream_create_helper,
        HttpStreamRequest::StreamType stream_type,
        const BoundNetLog& net_log) {
      JobController* job_controller =
          new JobController(this, delegate, session_, job_factory_.get());
      job_controller_set_.insert(base::WrapUnique(job_controller));
    
      Request* request = job_controller->Start(
          request_info, delegate, websocket_handshake_stream_create_helper, net_log,
          stream_type, priority, server_ssl_config, proxy_ssl_config);
    
      return request;
    }
    

    在HttpStreamFactoryImpl::RequestStreamInternal()中,主要是创建了一个JobController,然后用job_controller->Start()创建了Request,也就是HttpStreamRequest。

    由HttpStreamFactoryImpl的构造函数可以看到,job_factory_是DefaultJobFactory,这个类的实现也相当简单(chromium_android/src/net/http/http_stream_factory_impl.cc) :

    namespace {
    // Default JobFactory for creating HttpStreamFactoryImpl::Jobs.
    class DefaultJobFactory : public HttpStreamFactoryImpl::JobFactory {
     public:
      DefaultJobFactory() {}
    
      ~DefaultJobFactory() override {}
    
      HttpStreamFactoryImpl::Job* CreateJob(
          HttpStreamFactoryImpl::Job::Delegate* delegate,
          HttpStreamFactoryImpl::JobType job_type,
          HttpNetworkSession* session,
          const HttpRequestInfo& request_info,
          RequestPriority priority,
          const SSLConfig& server_ssl_config,
          const SSLConfig& proxy_ssl_config,
          HostPortPair destination,
          GURL origin_url,
          NetLog* net_log) override {
        return new HttpStreamFactoryImpl::Job(
            delegate, job_type, session, request_info, priority, server_ssl_config,
            proxy_ssl_config, destination, origin_url, net_log);
      }
    
      HttpStreamFactoryImpl::Job* CreateJob(
          HttpStreamFactoryImpl::Job::Delegate* delegate,
          HttpStreamFactoryImpl::JobType job_type,
          HttpNetworkSession* session,
          const HttpRequestInfo& request_info,
          RequestPriority priority,
          const SSLConfig& server_ssl_config,
          const SSLConfig& proxy_ssl_config,
          HostPortPair destination,
          GURL origin_url,
          AlternativeService alternative_service,
          NetLog* net_log) override {
        return new HttpStreamFactoryImpl::Job(
            delegate, job_type, session, request_info, priority, server_ssl_config,
            proxy_ssl_config, destination, origin_url, alternative_service,
            net_log);
      }
    };
    }  // anonymous namespace
    

    JobController的Start()定义 (chromium_android/src/net/http/http_stream_factory_impl_job_controller.cc) 如下:

    HttpStreamFactoryImpl::Request* HttpStreamFactoryImpl::JobController::Start(
        const HttpRequestInfo& request_info,
        HttpStreamRequest::Delegate* delegate,
        WebSocketHandshakeStreamBase::CreateHelper*
            websocket_handshake_stream_create_helper,
        const BoundNetLog& net_log,
        HttpStreamRequest::StreamType stream_type,
        RequestPriority priority,
        const SSLConfig& server_ssl_config,
        const SSLConfig& proxy_ssl_config) {
      DCHECK(factory_);
      DCHECK(!request_);
    
      request_ = new Request(request_info.url, this, delegate,
                             websocket_handshake_stream_create_helper, net_log,
                             stream_type);
    
      CreateJobs(request_info, priority, server_ssl_config, proxy_ssl_config,
                 delegate, stream_type, net_log);
    
      return request_;
    }
    

    在这里主要是创建了一个Request,然后调用CreateJobs()创建了一些Jobs:

    void HttpStreamFactoryImpl::JobController::CreateJobs(
        const HttpRequestInfo& request_info,
        RequestPriority priority,
        const SSLConfig& server_ssl_config,
        const SSLConfig& proxy_ssl_config,
        HttpStreamRequest::Delegate* delegate,
        HttpStreamRequest::StreamType stream_type,
        const BoundNetLog& net_log) {
      DCHECK(!main_job_);
      DCHECK(!alternative_job_);
      HostPortPair destination(HostPortPair::FromURL(request_info.url));
      GURL origin_url = ApplyHostMappingRules(request_info.url, &destination);
    
      main_job_.reset(job_factory_->CreateJob(
          this, MAIN, session_, request_info, priority, server_ssl_config,
          proxy_ssl_config, destination, origin_url, net_log.net_log()));
      AttachJob(main_job_.get());
    
      // Create an alternative job if alternative service is set up for this domain.
      const AlternativeService alternative_service =
          GetAlternativeServiceFor(request_info, delegate, stream_type);
    
      if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
        // Never share connection with other jobs for FTP requests.
        DVLOG(1) << "Selected alternative service (host: "
                 << alternative_service.host_port_pair().host()
                 << " port: " << alternative_service.host_port_pair().port() << ")";
    
        DCHECK(!request_info.url.SchemeIs("ftp"));
        HostPortPair alternative_destination(alternative_service.host_port_pair());
        ignore_result(
            ApplyHostMappingRules(request_info.url, &alternative_destination));
    
        alternative_job_.reset(job_factory_->CreateJob(
            this, ALTERNATIVE, session_, request_info, priority, server_ssl_config,
            proxy_ssl_config, alternative_destination, origin_url,
            alternative_service, net_log.net_log()));
        AttachJob(alternative_job_.get());
    
        main_job_is_blocked_ = true;
        alternative_job_->Start(request_->stream_type());
      }
      // Even if |alternative_job| has already finished, it will not have notified
      // the request yet, since we defer that to the next iteration of the
      // MessageLoop, so starting |main_job_| is always safe.
      main_job_->Start(request_->stream_type());
    }
    
     ......
    
    GURL HttpStreamFactoryImpl::JobController::ApplyHostMappingRules(
        const GURL& url,
        HostPortPair* endpoint) {
      const HostMappingRules* mapping_rules = session_->params().host_mapping_rules;
      if (mapping_rules && mapping_rules->RewriteHost(endpoint)) {
        url::Replacements<char> replacements;
        const std::string port_str = base::UintToString(endpoint->port());
        replacements.SetPort(port_str.c_str(), url::Component(0, port_str.size()));
        replacements.SetHost(endpoint->host().c_str(),
                             url::Component(0, endpoint->host().size()));
        return url.ReplaceComponents(replacements);
      }
      return url;
    }
    

    这里的过程如下:

    1. 应用主机映射规则,对url做修饰。
    2. 通过job_factory创建main_job。
    3. 查找备选服务。
    4. 找到了备选服务,则创建alternative_job,并Start它。
    5. Start main_job。

    我们前面提到的一些Jobs主要是指main_job,和可能会创建的alternative_job。

    接着我们来看Job的Start()方法(chromium_android/src/net/http/http_stream_factory_impl_job.cc) :

    void HttpStreamFactoryImpl::Job::Start(
        HttpStreamRequest::StreamType stream_type) {
      stream_type_ = stream_type;
      StartInternal();
    }
    
    .......
    
    int HttpStreamFactoryImpl::Job::RunLoop(int result) {
      TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("net"),
                   "HttpStreamFactoryImpl::Job::RunLoop");
      LOG(INFO) << "HttpStreamFactoryImpl Job DoLoop start " << "job type " << job_type_;
      result = DoLoop(result);
      LOG(INFO) << "HttpStreamFactoryImpl Job DoLoop end " << "result " << result;
    
      if (result == ERR_IO_PENDING)
        return result;
    
      if (job_type_ == PRECONNECT) {
        base::ThreadTaskRunnerHandle::Get()->PostTask(
            FROM_HERE,
            base::Bind(&HttpStreamFactoryImpl::Job::OnPreconnectsComplete,
                       ptr_factory_.GetWeakPtr()));
        return ERR_IO_PENDING;
      }
    
      if (IsCertificateError(result)) {
        // Retrieve SSL information from the socket.
        GetSSLInfo();
    
        next_state_ = STATE_WAITING_USER_ACTION;
        base::ThreadTaskRunnerHandle::Get()->PostTask(
            FROM_HERE,
            base::Bind(&HttpStreamFactoryImpl::Job::OnCertificateErrorCallback,
                       ptr_factory_.GetWeakPtr(), result, ssl_info_));
        return ERR_IO_PENDING;
      }
    
      switch (result) {
        case ERR_PROXY_AUTH_REQUESTED: {
          UMA_HISTOGRAM_BOOLEAN("Net.ProxyAuthRequested.HasConnection",
                                connection_.get() != NULL);
          if (!connection_.get())
            return ERR_PROXY_AUTH_REQUESTED_WITH_NO_CONNECTION;
          CHECK(connection_->socket());
          CHECK(establishing_tunnel_);
    
          next_state_ = STATE_WAITING_USER_ACTION;
          ProxyClientSocket* proxy_socket =
              static_cast<ProxyClientSocket*>(connection_->socket());
          base::ThreadTaskRunnerHandle::Get()->PostTask(
              FROM_HERE,
              base::Bind(&Job::OnNeedsProxyAuthCallback, ptr_factory_.GetWeakPtr(),
                         *proxy_socket->GetConnectResponseInfo(),
                         base::RetainedRef(proxy_socket->GetAuthController())));
          return ERR_IO_PENDING;
        }
    
        case ERR_SSL_CLIENT_AUTH_CERT_NEEDED:
          base::ThreadTaskRunnerHandle::Get()->PostTask(
              FROM_HERE,
              base::Bind(
                  &Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(),
                  base::RetainedRef(
                      connection_->ssl_error_response_info().cert_request_info)));
          return ERR_IO_PENDING;
    
        case ERR_HTTPS_PROXY_TUNNEL_RESPONSE: {
          DCHECK(connection_.get());
          DCHECK(connection_->socket());
          DCHECK(establishing_tunnel_);
    
          ProxyClientSocket* proxy_socket =
              static_cast<ProxyClientSocket*>(connection_->socket());
          base::ThreadTaskRunnerHandle::Get()->PostTask(
              FROM_HERE, base::Bind(&Job::OnHttpsProxyTunnelResponseCallback,
                                    ptr_factory_.GetWeakPtr(),
                                    *proxy_socket->GetConnectResponseInfo(),
                                    proxy_socket->CreateConnectResponseStream()));
          return ERR_IO_PENDING;
        }
    
        case OK:
          job_status_ = STATUS_SUCCEEDED;
          MaybeMarkAlternativeServiceBroken();
          next_state_ = STATE_DONE;
          if (new_spdy_session_.get()) {
            base::ThreadTaskRunnerHandle::Get()->PostTask(
                FROM_HERE, base::Bind(&Job::OnNewSpdySessionReadyCallback,
                                      ptr_factory_.GetWeakPtr()));
          } else if (delegate_->for_websockets()) {
            DCHECK(websocket_stream_);
            base::ThreadTaskRunnerHandle::Get()->PostTask(
                FROM_HERE, base::Bind(&Job::OnWebSocketHandshakeStreamReadyCallback,
                                      ptr_factory_.GetWeakPtr()));
          } else if (stream_type_ == HttpStreamRequest::BIDIRECTIONAL_STREAM) {
            if (!bidirectional_stream_impl_) {
              base::ThreadTaskRunnerHandle::Get()->PostTask(
                  FROM_HERE, base::Bind(&Job::OnStreamFailedCallback,
                                        ptr_factory_.GetWeakPtr(), ERR_FAILED));
            } else {
              base::ThreadTaskRunnerHandle::Get()->PostTask(
                  FROM_HERE,
                  base::Bind(&Job::OnBidirectionalStreamImplReadyCallback,
                             ptr_factory_.GetWeakPtr()));
            }
          } else {
            DCHECK(stream_.get());
            job_stream_ready_start_time_ = base::TimeTicks::Now();
            base::ThreadTaskRunnerHandle::Get()->PostTask(
                FROM_HERE,
                base::Bind(&Job::OnStreamReadyCallback, ptr_factory_.GetWeakPtr()));
          }
          return ERR_IO_PENDING;
    
        default:
          if (job_status_ != STATUS_BROKEN) {
            DCHECK_EQ(STATUS_RUNNING, job_status_);
            job_status_ = STATUS_FAILED;
            MaybeMarkAlternativeServiceBroken();
          }
          base::ThreadTaskRunnerHandle::Get()->PostTask(
              FROM_HERE, base::Bind(&Job::OnStreamFailedCallback,
                                    ptr_factory_.GetWeakPtr(), result));
          return ERR_IO_PENDING;
      }
    }
    
    ......
    
    int HttpStreamFactoryImpl::Job::StartInternal() {
      CHECK_EQ(STATE_NONE, next_state_);
      next_state_ = STATE_START;
      int rv = RunLoop(OK);
      DCHECK_EQ(ERR_IO_PENDING, rv);
      return rv;
    }
    

    执行调用流程大体如下:

    HttpStreamFactoryImpl_Job

    HttpStreamFactoryImpl::Job::RunLoop()中,主要是调用了HttpStreamFactoryImpl::Job::DoLoop(),并针对其执行结果,调用响应的回调函数,如:

    void HttpStreamFactoryImpl::Job::OnStreamFailedCallback(int result) {
      DCHECK_NE(job_type_, PRECONNECT);
    
      MaybeCopyConnectionAttemptsFromSocketOrHandle();
    
      delegate_->OnStreamFailed(this, result, server_ssl_config_);
      // |this| may be deleted after this call.
    }
    
    void HttpStreamFactoryImpl::Job::OnCertificateErrorCallback(
        int result, const SSLInfo& ssl_info) {
      DCHECK_NE(job_type_, PRECONNECT);
    
      MaybeCopyConnectionAttemptsFromSocketOrHandle();
    
      delegate_->OnCertificateError(this, result, server_ssl_config_, ssl_info);
      // |this| may be deleted after this call.
    }
    

    从前面的HttpStreamFactoryImpl::JobController::CreateJobs()中可以看到,delegate_正是HttpStreamFactoryImpl::JobController

    而在HttpStreamFactoryImpl::Job::DoLoop()中,则是处理Stream建立的事情。与HttpNetworkTransactionStart() 执行的DoLoop()类似,HttpStreamFactoryImpl::Job::DoLoop()也是将Stream创建的过程抽象为一系列的步骤,通过一个循环,以一种类似于状态机模式的方式逐步骤执行:

    int HttpStreamFactoryImpl::Job::DoLoop(int result) {
      DCHECK_NE(next_state_, STATE_NONE);
      int rv = result;
      do {
        State state = next_state_;
        next_state_ = STATE_NONE;
        switch (state) {
          case STATE_START:
            DCHECK_EQ(OK, rv);
            rv = DoStart();
            break;
          case STATE_RESOLVE_PROXY:
            DCHECK_EQ(OK, rv);
            rv = DoResolveProxy();
            break;
          case STATE_RESOLVE_PROXY_COMPLETE:
            rv = DoResolveProxyComplete(rv);
            break;
          case STATE_WAIT:
            DCHECK_EQ(OK, rv);
            rv = DoWait();
            break;
          case STATE_WAIT_COMPLETE:
            rv = DoWaitComplete(rv);
            break;
          case STATE_INIT_CONNECTION:
            DCHECK_EQ(OK, rv);
            rv = DoInitConnection();
            break;
          case STATE_INIT_CONNECTION_COMPLETE:
            rv = DoInitConnectionComplete(rv);
            break;
          case STATE_WAITING_USER_ACTION:
            rv = DoWaitingUserAction(rv);
            break;
          case STATE_RESTART_TUNNEL_AUTH:
            DCHECK_EQ(OK, rv);
            rv = DoRestartTunnelAuth();
            break;
          case STATE_RESTART_TUNNEL_AUTH_COMPLETE:
            rv = DoRestartTunnelAuthComplete(rv);
            break;
          case STATE_CREATE_STREAM:
            DCHECK_EQ(OK, rv);
            rv = DoCreateStream();
            break;
          case STATE_CREATE_STREAM_COMPLETE:
            rv = DoCreateStreamComplete(rv);
            break;
          default:
            NOTREACHED() << "bad state";
            rv = ERR_FAILED;
            break;
        }
      } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
      return rv;
    }
    

    以执行一个QUIC请求为例,创建Stream的整个执行流程大体如下:


    CreateStream

    备选服务机制

    HttpStreamFactoryImpl::JobControllerCreateJobs()中我们看到,在通过job_factory创建main_job之后,会查找备选服务,在找到了备选服务时,还会为它创建job,并Start。那备选服务又是一套什么样的机制呢?

    我们从两个方面来探究这套机制究竟是什么样的,及它又被用来做什么,一是备选服务的信息是从哪里及如何获取的,二是备选服务对HttpStreamFactoryImpl::Job::Job的操作的影响。

    获取备选服务信息

    我们先来看备选服务信息的获取。在HttpStreamFactoryImpl::JobControllerCreateJobs()中,通过GetAlternativeServiceFor()来获取备选服务的信息:

    AlternativeService
    HttpStreamFactoryImpl::JobController::GetAlternativeServiceFor(
        const HttpRequestInfo& request_info,
        HttpStreamRequest::Delegate* delegate,
        HttpStreamRequest::StreamType stream_type) {
      AlternativeService alternative_service =
          GetAlternativeServiceForInternal(request_info, delegate, stream_type);
      AlternativeServiceType type;
      if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL) {
        type = NO_ALTERNATIVE_SERVICE;
      } else if (alternative_service.protocol == QUIC) {
        if (request_info.url.host() == alternative_service.host) {
          type = QUIC_SAME_DESTINATION;
        } else {
          type = QUIC_DIFFERENT_DESTINATION;
        }
      } else {
        if (request_info.url.host() == alternative_service.host) {
          type = NOT_QUIC_SAME_DESTINATION;
        } else {
          type = NOT_QUIC_DIFFERENT_DESTINATION;
        }
      }
      UMA_HISTOGRAM_ENUMERATION("Net.AlternativeServiceTypeForRequest", type,
                                MAX_ALTERNATIVE_SERVICE_TYPE);
      return alternative_service;
    }
    

    这里主要通过GetAlternativeServiceForInternal()获取备选服务的信息:

    AlternativeService
    HttpStreamFactoryImpl::JobController::GetAlternativeServiceForInternal(
        const HttpRequestInfo& request_info,
        HttpStreamRequest::Delegate* delegate,
        HttpStreamRequest::StreamType stream_type) {
      GURL original_url = request_info.url;
    
      if (!original_url.SchemeIs("https"))
        return AlternativeService();
    
      url::SchemeHostPort origin(original_url);
      HttpServerProperties& http_server_properties =
          *session_->http_server_properties();
      const AlternativeServiceVector alternative_service_vector =
          http_server_properties.GetAlternativeServices(origin);
      if (alternative_service_vector.empty())
        return AlternativeService();
    
      bool quic_advertised = false;
      bool quic_all_broken = true;
    
      // First Alt-Svc that is not marked as broken.
      AlternativeService first_alternative_service;
    
      for (const AlternativeService& alternative_service :
           alternative_service_vector) {
        DCHECK(IsAlternateProtocolValid(alternative_service.protocol));
        if (!quic_advertised && alternative_service.protocol == QUIC)
          quic_advertised = true;
        if (http_server_properties.IsAlternativeServiceBroken(
                alternative_service)) {
          HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_BROKEN);
          continue;
        }
    
        // Some shared unix systems may have user home directories (like
        // http://foo.com/~mike) which allow users to emit headers.  This is a bad
        // idea already, but with Alternate-Protocol, it provides the ability for a
        // single user on a multi-user system to hijack the alternate protocol.
        // These systems also enforce ports <1024 as restricted ports.  So don't
        // allow protocol upgrades to user-controllable ports.
        const int kUnrestrictedPort = 1024;
        if (!session_->params().enable_user_alternate_protocol_ports &&
            (alternative_service.port >= kUnrestrictedPort &&
             origin.port() < kUnrestrictedPort))
          continue;
    
        if (alternative_service.protocol >= NPN_SPDY_MINIMUM_VERSION &&
            alternative_service.protocol <= NPN_SPDY_MAXIMUM_VERSION) {
          if (origin.host() != alternative_service.host &&
              !session_->params()
                   .enable_http2_alternative_service_with_different_host) {
            continue;
          }
    
          // Cache this entry if we don't have a non-broken Alt-Svc yet.
          if (first_alternative_service.protocol ==
              UNINITIALIZED_ALTERNATE_PROTOCOL)
            first_alternative_service = alternative_service;
          continue;
        }
    
        DCHECK_EQ(QUIC, alternative_service.protocol);
        if (origin.host() != alternative_service.host &&
            !session_->params()
                 .enable_quic_alternative_service_with_different_host) {
          continue;
        }
    
        quic_all_broken = false;
        if (!session_->params().enable_quic)
          continue;
    
        if (!IsQuicWhitelistedForHost(origin.host()))
          continue;
    
        if (stream_type == HttpStreamRequest::BIDIRECTIONAL_STREAM &&
            session_->params().quic_disable_bidirectional_streams) {
          continue;
        }
    
        if (session_->quic_stream_factory()->IsQuicDisabled(
                alternative_service.port))
          continue;
    
        if (!original_url.SchemeIs("https"))
          continue;
    
        // Check whether there is an existing QUIC session to use for this origin.
        HostPortPair mapped_origin(origin.host(), origin.port());
        ignore_result(ApplyHostMappingRules(original_url, &mapped_origin));
        QuicServerId server_id(mapped_origin, request_info.privacy_mode);
    
        HostPortPair destination(alternative_service.host_port_pair());
        ignore_result(ApplyHostMappingRules(original_url, &destination));
    
        if (session_->quic_stream_factory()->CanUseExistingSession(server_id,
                                                                   destination)) {
          return alternative_service;
        }
    
        // Cache this entry if we don't have a non-broken Alt-Svc yet.
        if (first_alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL)
          first_alternative_service = alternative_service;
      }
    
      // Ask delegate to mark QUIC as broken for the origin.
      if (quic_advertised && quic_all_broken && delegate != nullptr)
        delegate->OnQuicBroken();
    
      return first_alternative_service;
    }
    

    这个函数的执行流程如下:

    1. 检查Url的scheme是否为https,若不是,直接返回空的AlternativeService。即备选服务只用于https。
    2. 从session_获取HttpServerProperties http_server_properties。
    3. 从http_server_properties获取所有的备选服务信息。
    4. 遍历上一步找到的备选服务,找到一个可用的。

    在Chromium net中,以https为scheme的请求有多种,一是常规的HTTP/1.1 + TLS的请求,二是SPDY/HTTP2请求,三是QUIC协议的请求。备选服务主要用于后两种协议的请求。这里会根据同源策略、端口、协议是否打开及主机是否在白名单等判断一个备选服务是否可用。

    我们可以看到,备选服务的所有信息,都来源于http_server_properties。http_server_properties来源于HttpNetworkSession。HttpNetworkSession的http_server_properties在URLRequestContextBuilder::Build()中创建:

    void URLRequestContextBuilder::SetHttpNetworkSessionComponents(
        const URLRequestContext* context,
        HttpNetworkSession::Params* params) {
      params->host_resolver = context->host_resolver();
      params->cert_verifier = context->cert_verifier();
      params->transport_security_state = context->transport_security_state();
      params->cert_transparency_verifier = context->cert_transparency_verifier();
      params->ct_policy_enforcer = context->ct_policy_enforcer();
      params->proxy_service = context->proxy_service();
      params->ssl_config_service = context->ssl_config_service();
      params->http_auth_handler_factory = context->http_auth_handler_factory();
      params->http_server_properties = context->http_server_properties();
      params->net_log = context->net_log();
      params->channel_id_service = context->channel_id_service();
    }
    
    std::unique_ptr<URLRequestContext> URLRequestContextBuilder::Build() {
    ......
      if (http_server_properties_) {
        storage->set_http_server_properties(std::move(http_server_properties_));
      } else {
        storage->set_http_server_properties(
            std::unique_ptr<HttpServerProperties>(new HttpServerPropertiesImpl()));
      }
    ......
      storage->set_http_network_session(
          base::WrapUnique(new HttpNetworkSession(network_session_params)));
    

    Cronet库的初始化过程中,会执行CronetURLRequestContextAdapter::InitializeOnNetworkThread(),在这个方法中,通过URLRequestContextBuilder构建了URLRequestContext之后,会向其中添加备选服务的信息:

    void CronetURLRequestContextAdapter::InitializeOnNetworkThread(
        std::unique_ptr<URLRequestContextConfig> config,
        const base::android::ScopedJavaGlobalRef<jobject>&
            jcronet_url_request_context) {
    ......
      if (config->enable_quic) {
        for (auto hint = config->quic_hints.begin();
             hint != config->quic_hints.end(); ++hint) {
          const URLRequestContextConfig::QuicHint& quic_hint = **hint;
          if (quic_hint.host.empty()) {
            LOG(ERROR) << "Empty QUIC hint host: " << quic_hint.host;
            continue;
          }
    
          url::CanonHostInfo host_info;
          std::string canon_host(net::CanonicalizeHost(quic_hint.host, &host_info));
          if (!host_info.IsIPAddress() &&
              !net::IsCanonicalizedHostCompliant(canon_host)) {
            LOG(ERROR) << "Invalid QUIC hint host: " << quic_hint.host;
            continue;
          }
    
          if (quic_hint.port <= std::numeric_limits<uint16_t>::min() ||
              quic_hint.port > std::numeric_limits<uint16_t>::max()) {
            LOG(ERROR) << "Invalid QUIC hint port: "
                       << quic_hint.port;
            continue;
          }
    
          if (quic_hint.alternate_port <= std::numeric_limits<uint16_t>::min() ||
              quic_hint.alternate_port > std::numeric_limits<uint16_t>::max()) {
            LOG(ERROR) << "Invalid QUIC hint alternate port: "
                       << quic_hint.alternate_port;
            continue;
          }
    
          url::SchemeHostPort quic_server("https", canon_host, quic_hint.port);
          net::AlternativeService alternative_service(
              net::AlternateProtocol::QUIC, "",
              static_cast<uint16_t>(quic_hint.alternate_port));
          context_->http_server_properties()->SetAlternativeService(
              quic_server, alternative_service, base::Time::Max());
        }
      }
    

    这里更是限定了只允许给QUIC协议添加备选服务。而这里添加的备选服务的信息都来自于URLRequestContextConfig。

    继续来看给HttpServerProperties添加备选服务信息的过程 (chromium_android/src/net/http/http_server_properties_impl.cc):

    bool HttpServerPropertiesImpl::SetAlternativeService(
        const url::SchemeHostPort& origin,
        const AlternativeService& alternative_service,
        base::Time expiration) {
      return SetAlternativeServices(
          origin,
          AlternativeServiceInfoVector(
              /*size=*/1, AlternativeServiceInfo(alternative_service, expiration)));
    }
    
    bool HttpServerPropertiesImpl::SetAlternativeServices(
        const url::SchemeHostPort& origin,
        const AlternativeServiceInfoVector& alternative_service_info_vector) {
      AlternativeServiceMap::iterator it = alternative_service_map_.Peek(origin);
    
      if (alternative_service_info_vector.empty()) {
        RemoveCanonicalHost(origin);
        if (it == alternative_service_map_.end())
          return false;
    
        alternative_service_map_.Erase(it);
        return true;
      }
    
      bool changed = true;
      if (it != alternative_service_map_.end()) {
        DCHECK(!it->second.empty());
        if (it->second.size() == alternative_service_info_vector.size()) {
          const base::Time now = base::Time::Now();
          changed = false;
          auto new_it = alternative_service_info_vector.begin();
          for (const auto& old : it->second) {
            // Persist to disk immediately if new entry has different scheme, host,
            // or port.
            if (old.alternative_service != new_it->alternative_service) {
              changed = true;
              break;
            }
            // Also persist to disk if new expiration it more that twice as far or
            // less than half as far in the future.
            base::Time old_time = old.expiration;
            base::Time new_time = new_it->expiration;
            if (new_time - now > 2 * (old_time - now) ||
                2 * (new_time - now) < (old_time - now)) {
              changed = true;
              break;
            }
            ++new_it;
          }
        }
      }
    
      const bool previously_no_alternative_services =
          (GetAlternateProtocolIterator(origin) == alternative_service_map_.end());
    
      alternative_service_map_.Put(origin, alternative_service_info_vector);
    
      if (previously_no_alternative_services &&
          !GetAlternativeServices(origin).empty()) {
        // TODO(rch): Consider the case where multiple requests are started
        // before the first completes. In this case, only one of the jobs
        // would reach this code, whereas all of them should should have.
        HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING);
      }
    
      // If this host ends with a canonical suffix, then set it as the
      // canonical host.
      const char* kCanonicalScheme = "https";
      if (origin.scheme() == kCanonicalScheme) {
        const std::string* canonical_suffix = GetCanonicalSuffix(origin.host());
        if (canonical_suffix != nullptr) {
          url::SchemeHostPort canonical_server(kCanonicalScheme, *canonical_suffix,
                                               origin.port());
          canonical_host_to_origin_map_[canonical_server] = origin;
        }
      }
      return changed;
    }
    

    HttpServerPropertiesImpl用一个Map来管理备选服务的信息,key为原始服务的scheme+host+port,用url::SchemeHostPort来表示,而value则为AlternativeServiceInfoVector,即备选服务信息的列表。

    用户添加备选服务信息

    在CronetEngine.Builder中 (chromium_android/src/components/cronet/android/api/src/org/chromium/net/CronetEngine.java),提供了接口,来添加QUIC服务器的一些信息:

    public abstract class CronetEngine {
        /**
         * A builder for {@link CronetEngine}s, which allows runtime configuration of
         * {@code CronetEngine}. Configuration options are set on the builder and
         * then {@link #build} is called to create the {@code CronetEngine}.
         */
        public static class Builder {
    
    ......
    
            /**
             * A hint that a host supports QUIC.
             * @hide only used by internal implementation.
             */
            public static class QuicHint {
                // The host.
                public final String mHost;
                // Port of the server that supports QUIC.
                public final int mPort;
                // Alternate protocol port.
                public final int mAlternatePort;
    
                QuicHint(String host, int port, int alternatePort) {
                    mHost = host;
                    mPort = port;
                    mAlternatePort = alternatePort;
                }
            }
    
    ......
    
            /**
             * Adds hint that {@code host} supports QUIC.
             * Note that {@link #enableHttpCache enableHttpCache}
             * ({@link #HTTP_CACHE_DISK}) is needed to take advantage of 0-RTT
             * connection establishment between sessions.
             *
             * @param host hostname of the server that supports QUIC.
             * @param port host of the server that supports QUIC.
             * @param alternatePort alternate port to use for QUIC.
             * @return the builder to facilitate chaining.
             */
            public Builder addQuicHint(String host, int port, int alternatePort) {
                if (host.contains("/")) {
                    throw new IllegalArgumentException("Illegal QUIC Hint Host: " + host);
                }
                mQuicHints.add(new QuicHint(host, port, alternatePort));
                return this;
            }
    
            /**
             * @hide only used by internal implementation.
             */
            public List<QuicHint> quicHints() {
                return mQuicHints;
            }
    

    在CronetUrlRequestContext创建中,创建native UrlRequestContextConfig时会将所有的QUIC hint信息传递给native层。

        @VisibleForTesting
        public static long createNativeUrlRequestContextConfig(
                final Context context, CronetEngine.Builder builder) {
            final long urlRequestContextConfig = nativeCreateRequestContextConfig(
                    builder.getUserAgent(), builder.storagePath(), builder.quicEnabled(),
                    builder.getDefaultQuicUserAgentId(context), builder.http2Enabled(),
                    builder.sdchEnabled(), builder.dataReductionProxyKey(),
                    builder.dataReductionProxyPrimaryProxy(), builder.dataReductionProxyFallbackProxy(),
                    builder.dataReductionProxySecureProxyCheckUrl(), builder.cacheDisabled(),
                    builder.httpCacheMode(), builder.httpCacheMaxSize(), builder.experimentalOptions(),
                    builder.mockCertVerifier(), builder.networkQualityEstimatorEnabled(),
                    builder.publicKeyPinningBypassForLocalTrustAnchorsEnabled(),
                    builder.certVerifierData());
            for (Builder.QuicHint quicHint : builder.quicHints()) {
                nativeAddQuicHint(urlRequestContextConfig, quicHint.mHost, quicHint.mPort,
                        quicHint.mAlternatePort);
            }
            for (Builder.Pkp pkp : builder.publicKeyPins()) {
                nativeAddPkp(urlRequestContextConfig, pkp.mHost, pkp.mHashes, pkp.mIncludeSubdomains,
                        pkp.mExpirationDate.getTime());
            }
            return urlRequestContextConfig;
        }
    

    nativeAddQuicHint()在chromium_android/src/components/cronet/android/cronet_url_request_context_adapter.cc中定义:

    // Add a QUIC hint to a URLRequestContextConfig.
    static void AddQuicHint(JNIEnv* env,
                            const JavaParamRef<jclass>& jcaller,
                            jlong jurl_request_context_config,
                            const JavaParamRef<jstring>& jhost,
                            jint jport,
                            jint jalternate_port) {
      URLRequestContextConfig* config =
          reinterpret_cast<URLRequestContextConfig*>(jurl_request_context_config);
      config->quic_hints.push_back(
          base::WrapUnique(new URLRequestContextConfig::QuicHint(
              base::android::ConvertJavaStringToUTF8(env, jhost), jport,
              jalternate_port)));
    }
    

    备选服务对HttpStreamFactoryImpl::Job::Job的操作的影响

    为备选服务和为常规服务会以略有不同的方式创建Job:

    HttpStreamFactoryImpl::Job::Job(Delegate* delegate,
                                    JobType job_type,
                                    HttpNetworkSession* session,
                                    const HttpRequestInfo& request_info,
                                    RequestPriority priority,
                                    const SSLConfig& server_ssl_config,
                                    const SSLConfig& proxy_ssl_config,
                                    HostPortPair destination,
                                    GURL origin_url,
                                    NetLog* net_log)
        : Job(delegate,
              job_type,
              session,
              request_info,
              priority,
              server_ssl_config,
              proxy_ssl_config,
              destination,
              origin_url,
              AlternativeService(),
              net_log) {}
    
    HttpStreamFactoryImpl::Job::Job(Delegate* delegate,
                                    JobType job_type,
                                    HttpNetworkSession* session,
                                    const HttpRequestInfo& request_info,
                                    RequestPriority priority,
                                    const SSLConfig& server_ssl_config,
                                    const SSLConfig& proxy_ssl_config,
                                    HostPortPair destination,
                                    GURL origin_url,
                                    AlternativeService alternative_service,
                                    NetLog* net_log)
        : request_info_(request_info),
          priority_(priority),
          server_ssl_config_(server_ssl_config),
          proxy_ssl_config_(proxy_ssl_config),
          net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_HTTP_STREAM_JOB)),
          io_callback_(base::Bind(&Job::OnIOComplete, base::Unretained(this))),
          connection_(new ClientSocketHandle),
          session_(session),
          next_state_(STATE_NONE),
          pac_request_(NULL),
          destination_(destination),
          origin_url_(origin_url),
          alternative_service_(alternative_service),
          delegate_(delegate),
          job_type_(job_type),
          using_ssl_(false),
          using_spdy_(false),
          using_quic_(false),
          quic_request_(session_->quic_stream_factory()),
          using_existing_quic_session_(false),
          spdy_certificate_error_(OK),
          establishing_tunnel_(false),
          was_npn_negotiated_(false),
          protocol_negotiated_(kProtoUnknown),
          num_streams_(0),
          spdy_session_direct_(false),
          job_status_(STATUS_RUNNING),
          other_job_status_(STATUS_RUNNING),
          stream_type_(HttpStreamRequest::BIDIRECTIONAL_STREAM),
          ptr_factory_(this) {
      DCHECK(session);
    
      if (IsSpdyAlternative()) {
        DCHECK(origin_url_.SchemeIs("https"));
      }
      if (IsQuicAlternative()) {
        DCHECK(session_->params().enable_quic);
        using_quic_ = true;
      }
    }
    
    ......
    
    bool HttpStreamFactoryImpl::Job::IsQuicAlternative() const {
      return alternative_service_.protocol == QUIC;
    }
    

    为QUIC备选服务创建的Job,在创建时期,就会将using_quic_置为true。这个标记的设置将对后续创建Stream的过程产生决定性的影响。

    总结一下,备选服务机制像是一种过渡方案,用于在协议开发早期,还没有确定协议协商机制的情况下。在chromium net中,scheme为https的请求所用的协议有可能是HTTP/1.1+TLS、HTTP2和QUIC这三种的任一种,其中前两种都是基于TCP的,而QUIC是基于UDP的。当前前面的两种协议已经有了NPN和ALPN这样的协议协商的机制,而传给chromium net一个scheme为https的QUIC请求的URL,它也是不知道要用QUIC协议来做请求的。而备选服务机制,则允许chromium net的用户指定,对某些主机的访问采用特定的协议进行。此外,在HttpStreamFactoryImpl::JobController的CreateJobs()中,在为备选服务创建Job之外,还是会创建main_job,即是说,传给chromium net一个以https为scheme的Url,它一定会尝试用TCP的方式建立连接的,只是对于请求QUIC协议的服务,这个连接将会失败,而真正取回数据的将是alternative_job。

    相关文章

      网友评论

      • 神龙摆尾me:请问开篇第一句说的前面是哪个前面呐? 上一篇文章地址能发一下嘛?

      本文标题:Cronet android 设计与实现分析——备选服务机制

      本文链接:https://www.haomeiwen.com/subject/zzwpyttx.html