Chromium 网络栈之创建 URLRequest

发布于 2021-11-03


URLRequest代表一个通过 URL 请求资源的对象。它支持多种协议,比如 http(s)、ftp、data等等。

以请求 https://www.qq.com/ 这个 URL 为例,具体代码如下:

class URLRequestTask : public net::URLRequest::Delegate {
 public:
  URLRequestTask() {}
  ~URLRequestTask() override {}

  void SetURLRequest(std::unique_ptr<net::URLRequest> url_request) {
    url_request_ = std::move(url_request);
  }

  static void PrintRequestHeaders(net::HttpRawRequestHeaders headers) {
    printf("PrintRequestHeaders\n");
    for (auto i : headers.headers()) {
      printf("%s:%s\n", i.first.c_str(), i.second.c_str());
    }
    printf("\n");
  }

  static void PrintResponseHeaders(
      scoped_refptr<const net::HttpResponseHeaders> headers) {
    printf("PrintResponseHeaders\n");
    size_t iter = 0;
    std::string name;
    std::string value;
    while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
      printf("%s:%s\n", name.c_str(), value.c_str());
    }
    printf("\n");
  }

  bool Start() {
    if (url_request_) {
      url_request_->SetRequestHeadersCallback(
          base::BindRepeating(&URLRequestTask::PrintRequestHeaders));

      url_request_->SetResponseHeadersCallback(
          base::BindRepeating(&URLRequestTask::PrintResponseHeaders));

      url_request_->Start();
      return true;
    }
    return false;
  }

  void OnResponseStarted(net::URLRequest* request, int net_error) override {
    buf_ = base::MakeRefCounted<net::IOBuffer>(max_buf_size_);

    scoped_refptr<base::SingleThreadTaskRunner> network_task_runner =
        g_network_thread->task_runner();
    network_task_runner->PostTask(
        FROM_HERE,
        base::BindOnce(&URLRequestTask::ReadData, base::Unretained(this)));
  }

  void OnReadCompleted(net::URLRequest* request, int bytes_read) override {
    scoped_refptr<base::SingleThreadTaskRunner> network_task_runner =
        g_network_thread->task_runner();
    network_task_runner->PostTask(
        FROM_HERE,
        base::BindOnce(&URLRequestTask::ReadData, base::Unretained(this)));
  }

 private:
  void ReadData() {
    if (url_request_) {
      memset(buf_->data(), 0, max_buf_size_);
      int bytes_read = url_request_->Read(buf_.get(), max_buf_size_ - 1);

      printf("** ReadData:%d **\n", bytes_read);

      if (bytes_read == net::OK) {
        scoped_refptr<base::SingleThreadTaskRunner> network_task_runner =
            g_network_thread->task_runner();
        network_task_runner->DeleteSoon(FROM_HERE, this);
        return;
      }

      if (bytes_read == net::ERR_IO_PENDING) {
        return;
      }

      buf_->data()[bytes_read] = 0;
      printf("%s", buf_->data());
      printf("\n");

      OnReadCompleted(url_request_.get(), bytes_read);
    }
  }

  std::unique_ptr<net::URLRequest> url_request_;
  scoped_refptr<net::IOBuffer> buf_;
  const int max_buf_size_ = 1024;
};



  URLRequestTask* task = new URLRequestTask;
  std::unique_ptr<net::URLRequest> url_request =
      g_url_request_context->CreateRequest(GURL("https://www.qq.com/"),
                                           net::DEFAULT_PRIORITY, task);
  task->SetURLRequest(std::move(url_request));
  task->Start();

发起网络请求

URLRequestContext创建完成之后,就可以调用它的CreateRequest接口创建返回一个URLRequest对象。

我们创建一个类URLRequestTask,继承自net::URLRequest::Delegate,用来处理URLRequest过程中的事件和管理URLRequest对象的生命周期。

在我们实现的URLRequestTask中,给URLRequest设置RequestHeadersCallbackResponseHeadersCallback,把URLRequest的 Request Headers 和 Response Headers 打印出来,运行程序输出的效果如下:

PrintRequestHeaders
Host:www.qq.com
Connection:keep-alive
User-Agent:
Accept-Encoding:gzip, deflate

PrintResponseHeaders
Date:Wed, 03 Nov 2021 13:09:51 GMT
Content-Type:text/html; charset=GB2312
Transfer-Encoding:chunked
Connection:keep-alive
Server:squid/3.5.24
Vary:Accept-Encoding
Expires:Wed, 03 Nov 2021 13:10:51 GMT
Cache-Control:max-age=60
Vary:Accept-Encoding
Content-Encoding:gzip
Vary:Accept-Encoding
X-Cache:HIT from shenzhen.qq.com
X-Frame-Options:SAMEORIGIN
Content-Security-Policy:frame-ancestors https://*.qq.com
X-Content-Type-Options:nosniff

读取数据

我们重写OnResponseStarted函数,在这个时机开始读取URLRequest从服务器返回的结果。

我们开辟一个IOBuffer缓冲区,最大读取大小是1024字节,从URLRequestRead接口读数据,如果返回值大于0,就是读取到的数据大小。

如果返回值是net::OK,数字是0,表示所有数据都读取完成了。

如果返回值是net::ERR_IO_PENDING,数字是-1,表示还没有未读的数据,需要进一步从服务器那里去获取。当有新的数据时候,会自动调用我们重写的OnReadCompleted接口,继续进行读取数据。

我们用限制网络的带宽的软件,模拟一下这种情况:

** ReadData:-1 **
** ReadData:226 **
** ReadData:-1 **
** ReadData:1023 **
** ReadData:1023 **
** ReadData:1023 **
** ReadData:468 **
** ReadData:-1 **
** ReadData:180 **
** ReadData:0 **