消息的流通过程,是一个不同类相互交流的过程,如果不了解这个过程,根本就不知道这些类是怎么样相互协作的。由于上一次说到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