Bootstrap

谷歌chrome浏览器的源码分析(六)

消息的流通过程,是一个不同类相互交流的过程,如果不了解这个过程,根本就不知道这些类是怎么样相互协作的。由于上一次说到ViewHostMsg_RequestResource消息已经发送出来,它的处理过徎其实就是一般资源的消息处理过程,下面就来看看这个消息的处理过程,如下:

1.       base::MessagePumpWin::Run函数消息

2.       base::MessagePumpWin::RunWithDispatcher

3.       base::MessagePumpWin::DoRunLoop

4.       base::MessagePumpWin::WaitForWork

5.       base::MessagePumpWin::SignalWatcher

6.       IPC::Channel::OnObjectSignaled 通道的消息处理。

7.       IPC::Channel::ProcessIncomingMessages

8.       IPC::SyncChannel::SyncContext::OnMessageReceived

9.       IPC::ChannelProxy::Context::TryFilters

10.    ResourceMessageFilter::OnMessageReceived

11.    IPC::MessageWithTuple<Tuple2<int,ViewHostMsg_Resource_Request> >::Dispatch<ResourceMessageFilter,int,ViewHostMsg_Resource_Request const &>

12.    ResourceMessageFilter::OnRequestResource

13.    ResourceDispatcherHost::BeginRequest

 

消息通过上面12个函数的流转,到达到处理这个消息的函数ResourceDispatcherHost::BeginRequest,它在里面就会把消息变成一个网络下载请求,函数代码如下:

#001  void ResourceDispatcherHost::BeginRequest(

#002      Receiver* receiver,

#003      HANDLE render_process_handle,

#004      int render_process_host_id,

#005      int render_view_id,

#006      int request_id,

#007      const ViewHostMsg_Resource_Request& request_data,

#008      URLRequestContext* request_context,

#009      IPC::Message* sync_result) {

 

是否已经传送完成,或者关闭。

#010    if (is_shutdown_ ||

#011        !ShouldServiceRequest(render_process_host_id, request_data)) {

#012      // Tell the renderer that this request was disallowed.

#013      receiver->Send(new ViewMsg_Resource_RequestComplete(

#014          render_view_id,

#015          request_id,

#016          URLRequestStatus(URLRequestStatus::FAILED, net::ERR_ABORTED)));

#017      return;

#018    }

#019 

 

保证所有插件已经加载。

#020    // Ensure the Chrome plugins are loaded, as they may intercept network

#021    // requests.  Does nothing if they are already loaded.

#022    // TODO(mpcomplete): This takes 200 ms!  Investigate parallelizing this by

#023    // starting the load earlier in a BG thread.

#024    plugin_service_->LoadChromePlugins(this);

#025 

 

构造事件处理器。

#026    // Construct the event handler.

#027    scoped_refptr<EventHandler> handler;

#028    if (sync_result) {

#029      handler = new SyncEventHandler(receiver, request_data.url, sync_result);

#030    } else {

#031      handler = new AsyncEventHandler(receiver,

#032                                     render_process_host_id,

#033                                     render_view_id,

#034                                     render_process_handle,

#035                                     request_data.url,

#036                                     this);

#037    }

#038 

#039    if (HandleExternalProtocol(request_id, render_process_host_id, render_view_id,

#040                               request_data.url, request_data.resource_type,

#041                               handler)) {

#042      return;

#043    }

#044 

 

构造下载请求。

#045    // Construct the request.

#046    URLRequest* request = new URLRequest(request_data.url, this);

#047    request->set_method(request_data.method);

#048    request->set_policy_url(request_data.policy_url);

#049    request->set_referrer(request_data.referrer.spec());

#050    request->SetExtraRequestHeaders(request_data.headers);

#051    request->set_load_flags(request_data.load_flags);

#052    request->set_context(request_context);

#053    request->set_origin_pid(request_data.origin_pid);

#054 

 

设置上传数据。

#055    // Set upload data.

#056    uint64 upload_size = 0;

#057    if (!request_data.upload_content.empty()) {

#058      scoped_refptr<net::UploadData> upload = new net::UploadData();

#059      upload->set_elements(request_data.upload_content);  // Deep copy.

#060      request->set_upload(upload);

#061      upload_size = upload->GetContentLength();

#062    }

#063 

 

安装一个CrossSiteEventHandler事件处理器。

#064    // Install a CrossSiteEventHandler if this request is coming from a

#065    // RenderViewHost with a pending cross-site request.  We only check this for

#066    // MAIN_FRAME requests.

#067    // TODO(mpcomplete): remove "render_process_host_id != -1"

#068    //                   when http://b/viewIssue?id=1080959 is fixed.

#069    if (request_data.resource_type == ResourceType::MAIN_FRAME &&

#070        render_process_host_id != -1 &&

#071        Singleton<CrossSiteRequestManager>::get()->

#072            HasPendingCrossSiteRequest(render_process_host_id, render_view_id)) {

#073      // Wrap the event handler to be sure the current page's onunload handler

#074      // has a chance to run before we render the new page.

#075      handler = new CrossSiteEventHandler(handler,

#076                                         render_process_host_id,

#077                                         render_view_id,

#078                                         this);

#079    }

#080 

#081    if (safe_browsing_->enabled() &&

#082        safe_browsing_->CanCheckUrl(request_data.url)) {

#083      handler = new SafeBrowsingEventHandler(handler,

#084                                            render_process_host_id,

#085                                            render_view_id,

#086                                            request_data.url,

#087                                            request_data.resource_type,

#088                                            safe_browsing_,

#089                                            this);

#090    }

#091 

 

创建一个缓冲区处理。

#092    // Insert a buffered event handler before the actual one.

#093    handler = new BufferedEventHandler(handler, this, request);

#094 

#095    // Make extra info and read footer (contains request ID).

#096    ExtraRequestInfo* extra_info =

#097        new ExtraRequestInfo(handler,

#098                             request_id,

#099                            render_process_host_id,

#100                             render_view_id,

#101                            request_data.mixed_content,

#102                            request_data.resource_type,

#103                             upload_size);

#104    extra_info->allow_download =

#105        ResourceType::IsFrame(request_data.resource_type);

#106    request->set_user_data(extra_info);  // takes pointer ownership

#107 

 

开始调用内部处理请求函数。

#108    BeginRequestInternal(request, request_data.mixed_content);

#109  }

 

通过上面的分析,已经知道消息转换为一个请求任务URLRequest,这个任务就需要交给后面的工作进程来处理了,它是通过函数BeginRequestInternal来把任务进一步发送出去。





上一次说到怎么样开始把任务发送出去,也就是调用函数BeginRequestInternal来把URL请求发送,它的代码如下:

#001  void ResourceDispatcherHost::BeginRequestInternal(URLRequest* request,

#002                                                   bool mixed_content) {

 

获取请求信息。

#003    ExtraRequestInfo* info = ExtraInfoForRequest(request);

 

生成全局ID,然后保存到正在下载请求队列里。

#004    GlobalRequestID global_id(info->render_process_host_id, info->request_id);

#005    pending_requests_[global_id] = request;

#006    if (mixed_content) {

#007      // We don't start the request in that case.  The SSLManager will potentially

#008      // change the request (potentially to indicate its content should be

#009      // filtered) and start it itself.

#010      SSLManager::OnMixedContentRequest(this, request, ui_loop_);

#011      return;

#012    }

 

这里开始处理请求。

#013    request->Start();

#014 

 

启动上传状态更新定时器。

#015    // Make sure we have the load state monitor running

#016    if (!update_load_states_timer_.IsRunning()) {

#017      update_load_states_timer_.Start(

#018          TimeDelta::FromMilliseconds(kUpdateLoadStatesIntervalMsec),

#019          this, &ResourceDispatcherHost::UpdateLoadStates);

#020    }

#021  }

 

通过上面的函数可以看到主要调用URLRequest::Start()来处理下载的请求,它的代码如下:

#001  void URLRequest::Start() {

#002    DCHECK(!is_pending_);

#003    DCHECK(!job_);

#004 

 

创建一个下载的工作任务。

#005    job_ = GetJobManager()->CreateJob(this);

#006    job_->SetExtraRequestHeaders(extra_request_headers_);

#007 

 

判断是否有数据需要上传。

#008    if (upload_.get())

#009      job_->SetUpload(upload_.get());

#010 

 

设置请下开始下载的时间,以便后面检查超时的状态。

#011    is_pending_ = true;

#012    response_info_.request_time = Time::Now();

#013 

#014    // Don't allow errors to be sent from within Start().

#015    // TODO(brettw) this may cause NotifyDone to be sent synchronously,

#016    // we probably don't want this: they should be sent asynchronously so

#017    // the caller does not get reentered.

 

这里把工作任务启动运行。

#018    job_->Start();

#019  }

 

由于这里是对URL的HTTP请求下载数据,所以这里的job_是类URLRequestHttpJob的实例,也就是调用函数URLRequestHttpJob::Start(),在函数URLRequestHttpJob::Start()的处理过程序如下:

1.       URLRequestHttpJob::StartTransaction()

2.       net::HttpCache::Transaction::Start

3.       net::HttpCache::Transaction::BeginNetworkRequest()

4.       net::HttpTransactionWinHttp::Start

5.       net::HttpTransactionWinHttp::DidResolveProxy()

6.       net::HttpTransactionWinHttp::OpenRequest

7.       net::HttpTransactionWinHttp::SendRequest()

8.       net::WinHttpRequestThrottle::SubmitRequest

9.       net::WinHttpRequestThrottle::SendRequest

通过上面9个函数的调用处理,然后就会通过Windows的HTTP API进行发送请求和下载数据。我们来分析一下最后的函数WinHttpRequestThrottle::SendRequest,看看怎么样调用Windows HTTP API函数来获取数据的,它的代码如下:

#001  BOOL WinHttpRequestThrottle::SendRequest(HINTERNET request_handle,

#002                                          DWORD total_size,

#003                                          DWORD_PTR context,

#004                                          bool report_async_error) {

 

下面就是调用Windows的API函数WinHttpSendRequest来发送请求,当然在调用这个函数之前,需要调用函数WinHttpOpenRequest先打开一个TCP连接。

#005    BOOL ok = WinHttpSendRequest(request_handle,

#006                                WINHTTP_NO_ADDITIONAL_HEADERS,

#007                                 0,

#008                                WINHTTP_NO_REQUEST_DATA,

#009                                 0,

#010                                 total_size,

#011                                 context);

#012    if (!ok && report_async_error) {

#013      WINHTTP_ASYNC_RESULT async_result = { API_SEND_REQUEST, GetLastError() };

 

出错处理,就调用外面的回调函数。

#014      HttpTransactionWinHttp::StatusCallback(

#015          request_handle, context,

#016          WINHTTP_CALLBACK_STATUS_REQUEST_ERROR,

#017          &async_result, sizeof(async_result));

#018    }

#019    return ok;

#020  }

 

通过前面一系列的分析学会chrome浏览器怎么样输入URL地址,以及怎么样进行URL自动完成,然后把URL发送到渲染进程去处理,最后渲染进程又把资源下载请求发送到资源下载进程里处理,最后资源下载进程通过Windows HTTP API函数进行TCP连接,以及HTTP数据的上传和下载。浏览器向网站发送请求的过程已经分析完成了,那么HTTP API收到网页的数据后,又是怎么样处理的呢?下一次再来分析这个问题。




上一次说到使用WinHTTP函数来创建HTTP连接,并且使用它来发送请求,那么数据接收回来,当然也是从它那里接收的,因此下面来分析它的接收数据的几个函数,然后再来看看chrome里收到数据后发送到那里处理。

在WinHTTP里使用WinHttpOpen函数来创建HTTP连接,然后使用WinHttpConnect连接需要下载数据的网站,接着调函数WinHttpOpenRequest来创建一个请求,使用函数WinHttpSendRequest发送HTTP请求,如果有数据发送到服务器,就通过WinHttpWriteData函数来发送,最后调用函数WinHttpReceiveResponse来查询状态码,WinHttpQueryDataAvailable函数查询可以接收到的数据大小,调用函数WinHttpQueryHeaders来查询传送回来的html头的大小,调用函数WinHttpReadData来接收到WEB服务器发送回来的数据。

 

chrome就是通过下面的函数来实现数据接收的,如下:

#001  void HttpTransactionWinHttp::HandleStatusCallback(DWORD status,

#002                                                   DWORD_PTR result,

#003                                                   DWORD error,

#004                                                   DWORD secure_failure) {

#005    int rv = ERR_FAILED;

#006 

#007    switch (status) {

#008      case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:

#009        rv = DidReceiveError(error, secure_failure);

#010        break;

#011      case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:

#012        PopulateSSLInfo(secure_failure);

#013        rv = DidSendRequest();

#014        break;

#015      case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:

#016        rv = DidWriteData(static_cast<DWORD>(result));

#017        break;

 

接收到HTTP协议头的数据 。

#018      case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:

#019        rv = DidReceiveHeaders();

#020        break;

 

接收HTTP协议的数据 。

#021      case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:

#022        rv = DidReadData(static_cast<DWORD>(result));

#023        break;

#024      default:

#025        NOTREACHED() << "unexpected status code";

#026    }

#027 

#028    if (rv == ERR_IO_PENDING) {

#029      session_callback_->AddRef();  // balanced when callback runs.

#030    } else if (callback_) {

#031      DoCallback(rv);

#032    }

#033  }

 

通过上面的分析知道数据怎么样从WinHTTP接收到,并且发送到下一个阶段处理。






上一次说到通过WinHTTP来接收网络数据,但没有具体介绍怎么样接收,现在就来分析这方面的代码。首先是通过函数WinHttpQueryHeaders来查询HTTP协议头的大小,接着还是通过函数WinHttpQueryHeaders把数据接收到缓冲区里。下面这段代码,就是做这样的事情:

#001  int HttpTransactionWinHttp::DidReceiveHeaders() {

#002    session_callback_->set_load_state(LOAD_STATE_IDLE);

#003 

 

第一次调用函数WinHttpQueryHeaders查看接收到协议头的大小。

#004    DWORD size = 0;

#005    if (!WinHttpQueryHeaders(request_handle_,

#006                            WINHTTP_QUERY_RAW_HEADERS,

#007                            WINHTTP_HEADER_NAME_BY_INDEX,

#008                             NULL,

#009                             &size,

#010                            WINHTTP_NO_HEADER_INDEX)) {

#011      DWORD error = GetLastError();

#012      if (error != ERROR_INSUFFICIENT_BUFFER) {

#013        DLOG(ERROR) << "WinHttpQueryHeaders failed: " << GetLastError();

#014        return TranslateLastOSError();

#015      }

#016      // OK, size should tell us how much to allocate...

#017      DCHECK(size > 0);

#018    }

#019 

 

第二次调用函数WinHttpQueryHeaders来接收协议头的数据。

#020    std::wstring raw_headers;

#021 

#022    // 'size' is the number of bytes rather than the number of characters.

#023    DCHECK(size % 2 == 0);

#024    if (!WinHttpQueryHeaders(request_handle_,

#025                            WINHTTP_QUERY_RAW_HEADERS,

#026                            WINHTTP_HEADER_NAME_BY_INDEX,

#027                            WriteInto(&raw_headers, size/2 + 1),

#028                             &size,

#029                            WINHTTP_NO_HEADER_INDEX)) {

#030      DLOG(ERROR) << "WinHttpQueryHeaders failed: " << GetLastError();

#031      return TranslateLastOSError();

#032    }

#033 

 

设置回应的一些状态。

#034    response_.response_time = Time::Now();

#035 

#036    // From experimentation, it appears that WinHttp translates non-ASCII bytes

#037    // found in the response headers to UTF-16 assuming that they are encoded

#038    // using the default system charset.  We attempt to undo that here.

#039    response_.headers =

#040        new HttpResponseHeaders(base::SysWideToNativeMB(raw_headers));

#041 

#042    // WinHTTP truncates a response longer than 2GB.  Perhaps it stores the

#043    // response's content length in a signed 32-bit integer.  We fail rather

#044    // than reading a truncated response.

#045    if (response_.headers->GetContentLength() > 0x80000000)

#046      return ERR_FILE_TOO_BIG;

#047 

#048    response_.vary_data.Init(*request_, *response_.headers);

#049    PopulateAuthChallenge();

#050 

#051    // Unfortunately, WinHttp does not close the connection when a non-keepalive

#052    // response is _not_ followed by the server closing the connection. So, we

#053    // attempt to hack around this bug.

#054    if (!response_.headers->IsKeepAlive())

#055      content_length_remaining_ = response_.headers->GetContentLength();

#056 

#057    return OK;

#058  }

 

通过上面的函数处理,就可以收到HTTP协议头的数据,这样就可以进一步处理了。那么接着下来就是收到HTTP协议里的数据,这个主要通过下面的函数来接收到的,如下:

#001  BOOL HttpTransactionWinHttp::SessionCallback::ReadData(

#002      HINTERNET request_handle) {

#003    DCHECK(bytes_available_ >= 0);

#004    char* buf = read_buf_;

#005    read_buf_ = NULL;

#006    int bytes_to_read = std::min(bytes_available_, read_buf_len_);

#007    read_buf_len_ = 0;

#008    if (!bytes_to_read)

#009      bytes_to_read = 1;

#010 

#011    // Because of how WinHTTP fills memory when used asynchronously, Purify isn't

#012    // able to detect that it's been initialized, so it scans for 0xcd in the

#013    // buffer and reports UMRs (uninitialized memory reads) for those individual

#014    // bytes. We override that to avoid the false error reports.

#015    // See http://b/issue?id=1173916.

#016    base::MemoryDebug::MarkAsInitialized(buf, bytes_to_read);

#017    return WinHttpReadData(request_handle, buf, bytes_to_read, NULL);

#018  }

上面通过判断可以接收到多少字节,然后通过函数WinHttpReadData把数据保存到缓冲区read_buf_里,在这个缓冲区里保存了所有网络接收到的数据,那么这些数据又将要流向何方呢?下一次再来分析这个问题。





上次说到函数WinHttpReadData接收数据到缓冲区里,那么这些数据又是怎么样传送给下一步处理的呢?带着这个问题,我们来分析下面这段代码,如下:

#001  void HttpTransactionWinHttp::HandleStatusCallback(DWORD status,

#002                                                   DWORD_PTR result,

#003                                                   DWORD error,

#004                                                   DWORD secure_failure) {

#005    int rv = ERR_FAILED;

#006 

#007    switch (status) {

#008      case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:

#009        rv = DidReceiveError(error, secure_failure);

#010        break;

......

#027 

#028    if (rv == ERR_IO_PENDING) {

#029      session_callback_->AddRef();  // balanced when callback runs.

#030    } else if (callback_) {

#031      DoCallback(rv);

#032    }

#033  }

 

通过上面的函数可以看到,当数据接收完成后,就会调用DoCallback函数处理接收到的数据。DoCallback函数的代码如下:

 

#001  void HttpTransactionWinHttp::DoCallback(int rv) {

#002    DCHECK(rv != ERR_IO_PENDING);

#003    DCHECK(callback_);

#004 

#005    // since Run may result in Read being called, clear callback_ up front.

#006    CompletionCallback* c = callback_;

#007    callback_ = NULL;

#008    c->Run(rv);

#009  }

 

看到这里又是一个回调函数c->Run的通知,它是调用开始创建这个连接时设置的回调对象。如果是HTTP请求,那么这个请求回调函数是对象URLRequestHttpJob里的函数,也就是调用URLRequestHttpJob::OnReadCompleted函数,这个函数是当数据接收完成,或接收失败,或者接收还没有完成时都会调用。这个函数代码如下:

#001  void URLRequestHttpJob::OnReadCompleted(int result) {

#002    read_in_progress_ = false;

#003 

 

这里是接收数据完成。

#004    if (result == 0) {

#005      NotifyDone(URLRequestStatus());

#006    } else if (result < 0) {

 

这里是接收数据出错划。

#007      NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result));

#008    } else {

 

这里是接收数据还没有完成。

#009      // Clear the IO_PENDING status

#010      SetStatus(URLRequestStatus());

#011    }

#012 

#013    NotifyReadComplete(result);

#014  }

 

当上面读取数据完成时,就开始把接收到数据通过类ResourceDispatcherHost来发送出去,而类ResourceDispatcherHost发送数据的方式比较特别,它不是通过消息把整块数据用命名管道发送的,而是通过共享内存的方式让另一个进程来读取数据,这样达到速度快的特点,可见可多米处理处理考虑的都是速度,它的代码如下:

#001  bool OnReadCompleted(int request_id, int* bytes_read) {

#002      if (!*bytes_read)

#003        return true;

#004      DCHECK(read_buffer_.get());

#005 

#006      if (!rdh_->WillSendData(render_process_host_id_, request_id)) {

#007        // We should not send this data now, we have too many pending requests.

#008        return true;

#009      }

#010 

 

这里创建共享内存。

#011      SharedMemoryHandle handle;

#012      if (!read_buffer_->GiveToProcess(render_process_, &handle)) {

#013        // We wrongfully incremented the pending data count. Fake an ACK message

#014        // to fix this. We can't move this call above the WillSendData because

#015        // it's killing our read_buffer_, and we don't want that when we pause

#016        // the request.

#017        rdh_->OnDataReceivedACK(render_process_host_id_, request_id);

#018        return false;

#019      }

#020 

 

把共享内存通过管道消息发送给渲染进程。

#021      receiver_->Send(new ViewMsg_Resource_DataReceived(

#022          routing_id_, request_id, handle, *bytes_read));

#023 

#024      return true;

#025    }

#026 

 

共享内存是使用Windows API函数CreateFileMapping来创建内存共享文件实现的,具体实现方法请参考类SharedMemory的实现。这里既然把消息通过管道发送出去了,那么在另一个线程里肯定就处理的这个消息,下一次再来分析那部份代码。




from: http://blog.csdn.net/caimouse/article/details/3230179

;