blob: b3231a2670d1dcc147b9af423d35753ddaed1e5a [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2019 The Chromium Authors
Sorin Jianu95fb33d2019-02-21 16:59:332// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Roger Tawac0a1b9c2021-09-23 23:34:045#include "components/winhttp/network_fetcher.h"
Sorin Jianu95fb33d2019-02-21 16:59:336
Sorin Jianu92a26c92022-10-19 17:07:207#include <cstdint>
Sorin Jianubf44a912019-03-02 01:35:018#include <limits>
S. Ganeshe02679752024-06-05 21:41:449#include <optional>
Jan Wilken Dörriead587c32021-03-11 14:09:2710#include <string>
Helmut Januschka1dce9dc2024-06-11 13:05:3511#include <string_view>
Sorin Jianubf44a912019-03-02 01:35:0112#include <utility>
Sorin Jianu92a26c92022-10-19 17:07:2013#include <vector>
Sorin Jianubf44a912019-03-02 01:35:0114
Sorin Jianu92a26c92022-10-19 17:07:2015#include "base/check_op.h"
Tom Sepez9ccb7912024-09-23 16:52:1516#include "base/containers/span.h"
Sorin Jianubf44a912019-03-02 01:35:0117#include "base/files/file.h"
18#include "base/files/file_util.h"
Sorin Jianu92a26c92022-10-19 17:07:2019#include "base/functional/bind.h"
20#include "base/functional/callback.h"
Sorin Jianubf44a912019-03-02 01:35:0121#include "base/logging.h"
Maciek Kumorek1d1e5c22020-07-25 22:49:3922#include "base/memory/scoped_refptr.h"
Sorin Jianubf44a912019-03-02 01:35:0123#include "base/numerics/safe_math.h"
24#include "base/strings/strcat.h"
Peter Kasting36f86c62023-09-22 16:39:4125#include "base/strings/string_number_conversions_win.h"
Sorin Jianubf44a912019-03-02 01:35:0126#include "base/strings/stringprintf.h"
27#include "base/strings/sys_string_conversions.h"
Peter Kasting36f86c62023-09-22 16:39:4128#include "base/strings/utf_string_conversions.h"
Sorin Jianu6092b8c12023-02-01 17:40:0529#include "base/task/sequenced_task_runner.h"
Gabriel Charette1a24fba82020-02-28 16:54:3730#include "base/task/task_traits.h"
31#include "base/task/thread_pool.h"
Maciek Kumorek1d1e5c22020-07-25 22:49:3932#include "base/win/windows_version.h"
Roger Tawac0a1b9c2021-09-23 23:34:0433#include "components/winhttp/net_util.h"
34#include "components/winhttp/proxy_info.h"
35#include "components/winhttp/scoped_hinternet.h"
36#include "components/winhttp/scoped_winttp_proxy_info.h"
Sorin Jianubf44a912019-03-02 01:35:0137#include "url/url_constants.h"
Sorin Jianu95fb33d2019-02-21 16:59:3338
Roger Tawac0a1b9c2021-09-23 23:34:0439namespace winhttp {
Sorin Jianubf44a912019-03-02 01:35:0140namespace {
41
Alison Galeb8be9522024-04-16 00:00:3142// TODO(crbug.com/40163568) - implement a way to express priority for
Sorin Jianubc1c270a2022-10-19 23:26:2743// foreground/background network fetches.
Sorin Jianu660bfa82020-05-31 15:28:5644constexpr base::TaskTraits kTaskTraits = {
Sorin Jianubc1c270a2022-10-19 23:26:2745 base::MayBlock(), base::TaskPriority::USER_VISIBLE,
Sorin Jianu660bfa82020-05-31 15:28:5646 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
47
Sorin Jianubf44a912019-03-02 01:35:0148void CrackUrl(const GURL& url,
49 bool* is_https,
50 std::string* host,
51 int* port,
52 std::string* path_for_request) {
Sorin Jianu40b6bd02024-01-11 00:25:0453 if (is_https) {
Sorin Jianubf44a912019-03-02 01:35:0154 *is_https = url.SchemeIs(url::kHttpsScheme);
Sorin Jianu40b6bd02024-01-11 00:25:0455 }
56 if (host) {
Sorin Jianubf44a912019-03-02 01:35:0157 *host = url.host();
Sorin Jianu40b6bd02024-01-11 00:25:0458 }
59 if (port) {
Sorin Jianubf44a912019-03-02 01:35:0160 *port = url.EffectiveIntPort();
Sorin Jianu40b6bd02024-01-11 00:25:0461 }
62 if (path_for_request) {
Sorin Jianubf44a912019-03-02 01:35:0163 *path_for_request = url.PathForRequest();
Sorin Jianu40b6bd02024-01-11 00:25:0464 }
Sorin Jianubf44a912019-03-02 01:35:0165}
66
67} // namespace
68
Roger Tawac0a1b9c2021-09-23 23:34:0469NetworkFetcher::NetworkFetcher(
Sorin Jianu622be9c2024-02-15 00:17:4270 scoped_refptr<SharedHInternet> session_handle,
Maciek Kumorek1d1e5c22020-07-25 22:49:3971 scoped_refptr<ProxyConfiguration> proxy_configuration)
Sorin Jianu6092b8c12023-02-01 17:40:0572 : main_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
Maciek Kumorek1d1e5c22020-07-25 22:49:3973 session_handle_(session_handle),
74 proxy_configuration_(proxy_configuration) {}
Sorin Jianubf44a912019-03-02 01:35:0175
Roger Tawac0a1b9c2021-09-23 23:34:0476NetworkFetcher::~NetworkFetcher() {
Sorin Jianu28a32f42023-09-22 19:43:3277 DVLOG(3) << __func__;
Sorin Jianu660bfa82020-05-31 15:28:5678}
Sorin Jianubf44a912019-03-02 01:35:0179
Sorin Jianu92a26c92022-10-19 17:07:2080void NetworkFetcher::HandleClosing() {
81 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianu28a32f42023-09-22 19:43:3282 // `write_data_callback_` maintains an outstanding reference to this object
83 // and the reference must be released to avoid leaking the object.
84 write_data_callback_.Reset();
Sorin Jianu92a26c92022-10-19 17:07:2085 self_ = nullptr;
86}
87
Roger Tawac0a1b9c2021-09-23 23:34:0488void NetworkFetcher::Close() {
Sorin Jianu92a26c92022-10-19 17:07:2089 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:0190 request_handle_.reset();
91}
92
Roger Tawac0a1b9c2021-09-23 23:34:0493void NetworkFetcher::CompleteFetch() {
Sorin Jianu660bfa82020-05-31 15:28:5694 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
95 if (!file_.IsValid()) {
Sebastien Lalancettee26adaec2022-01-05 15:32:4696 std::move(fetch_complete_callback_).Run(response_code_);
Sorin Jianu660bfa82020-05-31 15:28:5697 return;
98 }
99 base::ThreadPool::PostTaskAndReply(
100 FROM_HERE, kTaskTraits,
101 base::BindOnce([](base::File& file) { file.Close(); }, std::ref(file_)),
Roger Tawac0a1b9c2021-09-23 23:34:04102 base::BindOnce(&NetworkFetcher::CompleteFetch, this));
Sorin Jianu660bfa82020-05-31 15:28:56103}
104
Roger Tawac0a1b9c2021-09-23 23:34:04105HRESULT NetworkFetcher::QueryHeaderString(const std::wstring& name,
106 std::wstring* value) const {
Sorin Jianu92a26c92022-10-19 17:07:20107 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Joshua Pawlickibb0a3f5c2023-09-20 01:08:36108 if (!request_handle_.is_valid()) {
109 return HRESULT_FROM_WIN32(ERROR_CANCELLED);
110 }
Roger Tawac0a1b9c2021-09-23 23:34:04111 return QueryHeadersString(request_handle_.get(), WINHTTP_QUERY_CUSTOM,
112 name.c_str(), value);
113}
114
115HRESULT NetworkFetcher::QueryHeaderInt(const std::wstring& name,
116 int* value) const {
Sorin Jianu92a26c92022-10-19 17:07:20117 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Joshua Pawlickibb0a3f5c2023-09-20 01:08:36118 if (!request_handle_.is_valid()) {
119 return HRESULT_FROM_WIN32(ERROR_CANCELLED);
120 }
Roger Tawac0a1b9c2021-09-23 23:34:04121 return QueryHeadersInt(request_handle_.get(), WINHTTP_QUERY_CUSTOM,
122 name.c_str(), value);
123}
124
125std::string NetworkFetcher::GetResponseBody() const {
Sorin Jianued722422020-05-29 19:17:14126 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:01127 return post_response_body_;
128}
129
Roger Tawac0a1b9c2021-09-23 23:34:04130HRESULT NetworkFetcher::GetNetError() const {
Sorin Jianued722422020-05-29 19:17:14131 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:01132 return net_error_;
133}
134
Roger Tawac0a1b9c2021-09-23 23:34:04135base::FilePath NetworkFetcher::GetFilePath() const {
Sorin Jianued722422020-05-29 19:17:14136 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:01137 return file_path_;
138}
139
Roger Tawac0a1b9c2021-09-23 23:34:04140int64_t NetworkFetcher::GetContentSize() const {
Sorin Jianued722422020-05-29 19:17:14141 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:01142 return content_size_;
143}
Sorin Jianu95fb33d2019-02-21 16:59:33144
Roger Tawac0a1b9c2021-09-23 23:34:04145void NetworkFetcher::PostRequest(
Sorin Jianu95fb33d2019-02-21 16:59:33146 const GURL& url,
147 const std::string& post_data,
Sorin Jianuc2e91392020-06-18 01:05:30148 const std::string& content_type,
Sorin Jianu95fb33d2019-02-21 16:59:33149 const base::flat_map<std::string, std::string>& post_additional_headers,
Sorin Jianubf44a912019-03-02 01:35:01150 FetchStartedCallback fetch_started_callback,
Sorin Jianu520cfc02019-03-04 23:49:29151 FetchProgressCallback fetch_progress_callback,
Sorin Jianubf44a912019-03-02 01:35:01152 FetchCompleteCallback fetch_complete_callback) {
Sorin Jianued722422020-05-29 19:17:14153 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:01154
155 url_ = url;
156 fetch_started_callback_ = std::move(fetch_started_callback);
Sorin Jianu520cfc02019-03-04 23:49:29157 fetch_progress_callback_ = std::move(fetch_progress_callback);
Sorin Jianubf44a912019-03-02 01:35:01158 fetch_complete_callback_ = std::move(fetch_complete_callback);
159
Maciek Kumorek1d1e5c22020-07-25 22:49:39160 CrackUrl(url_, &is_https_, &host_, &port_, &path_for_request_);
Sorin Jianubf44a912019-03-02 01:35:01161
162 verb_ = L"POST";
Maciek Kumorek50ead5c5c2020-07-08 15:10:14163 content_type_ = content_type;
Sorin Jianued722422020-05-29 19:17:14164 write_data_callback_ =
Roger Tawac0a1b9c2021-09-23 23:34:04165 base::BindRepeating(&NetworkFetcher::WriteDataToMemory, this);
Sorin Jianubf44a912019-03-02 01:35:01166
167 net_error_ = BeginFetch(post_data, post_additional_headers);
Maciek Kumorek1d1e5c22020-07-25 22:49:39168
Sorin Jianu40b6bd02024-01-11 00:25:04169 if (FAILED(net_error_)) {
Sorin Jianu660bfa82020-05-31 15:28:56170 CompleteFetch();
Sorin Jianu40b6bd02024-01-11 00:25:04171 }
Sorin Jianubf44a912019-03-02 01:35:01172}
Sorin Jianu95fb33d2019-02-21 16:59:33173
Joshua Pawlickibb0a3f5c2023-09-20 01:08:36174base::OnceClosure NetworkFetcher::DownloadToFile(
Sorin Jianu95fb33d2019-02-21 16:59:33175 const GURL& url,
176 const base::FilePath& file_path,
Sorin Jianubf44a912019-03-02 01:35:01177 FetchStartedCallback fetch_started_callback,
Sorin Jianu520cfc02019-03-04 23:49:29178 FetchProgressCallback fetch_progress_callback,
Sorin Jianubf44a912019-03-02 01:35:01179 FetchCompleteCallback fetch_complete_callback) {
Sorin Jianued722422020-05-29 19:17:14180 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianu95fb33d2019-02-21 16:59:33181
Sorin Jianubf44a912019-03-02 01:35:01182 url_ = url;
183 file_path_ = file_path;
184 fetch_started_callback_ = std::move(fetch_started_callback);
Sorin Jianu520cfc02019-03-04 23:49:29185 fetch_progress_callback_ = std::move(fetch_progress_callback);
Sorin Jianubf44a912019-03-02 01:35:01186 fetch_complete_callback_ = std::move(fetch_complete_callback);
Sorin Jianu95fb33d2019-02-21 16:59:33187
Sorin Jianubf44a912019-03-02 01:35:01188 CrackUrl(url, &is_https_, &host_, &port_, &path_for_request_);
189
190 verb_ = L"GET";
Sorin Jianued722422020-05-29 19:17:14191 write_data_callback_ =
Roger Tawac0a1b9c2021-09-23 23:34:04192 base::BindRepeating(&NetworkFetcher::WriteDataToFile, this);
Sorin Jianubf44a912019-03-02 01:35:01193
194 net_error_ = BeginFetch({}, {});
Maciek Kumorek1d1e5c22020-07-25 22:49:39195
Sorin Jianu40b6bd02024-01-11 00:25:04196 if (FAILED(net_error_)) {
Sorin Jianu660bfa82020-05-31 15:28:56197 CompleteFetch();
Sorin Jianu40b6bd02024-01-11 00:25:04198 }
Joshua Pawlickibb0a3f5c2023-09-20 01:08:36199
200 return base::BindOnce(&NetworkFetcher::Close, this);
Sorin Jianubf44a912019-03-02 01:35:01201}
202
Roger Tawac0a1b9c2021-09-23 23:34:04203HRESULT NetworkFetcher::BeginFetch(
Sorin Jianubf44a912019-03-02 01:35:01204 const std::string& data,
S. Ganeshe02679752024-06-05 21:41:44205 const base::flat_map<std::string, std::string>& additional_headers) {
Sorin Jianued722422020-05-29 19:17:14206 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Maciek Kumorek1d1e5c22020-07-25 22:49:39207
Sorin Jianu40b6bd02024-01-11 00:25:04208 if (!url_.SchemeIsHTTPOrHTTPS()) {
Sorin Jianuc03eade2021-10-18 22:57:32209 return E_INVALIDARG;
Sorin Jianu40b6bd02024-01-11 00:25:04210 }
Sorin Jianuc03eade2021-10-18 22:57:32211
Sorin Jianubf44a912019-03-02 01:35:01212 connect_handle_ = Connect();
Sorin Jianu40b6bd02024-01-11 00:25:04213 if (!connect_handle_.get()) {
Sorin Jianubf44a912019-03-02 01:35:01214 return HRESULTFromLastError();
Sorin Jianu40b6bd02024-01-11 00:25:04215 }
Sorin Jianubf44a912019-03-02 01:35:01216
S. Ganeshe02679752024-06-05 21:41:44217 base::ThreadPool::PostTaskAndReplyWithResult(
218 FROM_HERE, kTaskTraits,
219 base::BindOnce(&NetworkFetcher::GetProxyForUrl, this),
220 base::BindOnce(&NetworkFetcher::ContinueFetch, this, data,
221 additional_headers));
Sorin Jianubf44a912019-03-02 01:35:01222 return S_OK;
223}
224
S. Ganeshe02679752024-06-05 21:41:44225std::optional<ScopedWinHttpProxyInfo> NetworkFetcher::GetProxyForUrl() {
226 return proxy_configuration_->GetProxyForUrl(session_handle_->handle(), url_);
227}
228
229void NetworkFetcher::ContinueFetch(
230 const std::string& data,
231 base::flat_map<std::string, std::string> additional_headers,
232 std::optional<ScopedWinHttpProxyInfo> winhttp_proxy_info) {
233 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
234
235 net_error_ = [&] {
236 request_handle_ = OpenRequest();
237 if (!request_handle_.get()) {
238 return HRESULTFromLastError();
239 }
240
Sorin Jianu9085ad7f2024-10-16 21:57:32241 SetProxyForRequest(request_handle_.get(), std::move(winhttp_proxy_info));
S. Ganeshe02679752024-06-05 21:41:44242
243 const auto winhttp_callback = ::WinHttpSetStatusCallback(
244 request_handle_.get(), &NetworkFetcher::WinHttpStatusCallback,
245 WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS, 0);
246 if (winhttp_callback == WINHTTP_INVALID_STATUS_CALLBACK) {
247 return HRESULTFromLastError();
248 }
249
250 auto hr = SetOption(request_handle_.get(), WINHTTP_OPTION_CONTEXT_VALUE,
251 context());
252 if (FAILED(hr)) {
253 return hr;
254 }
255
256 // The reference is released when the request handle is closed.
257 self_ = this;
258
259 // Disables both saving and sending cookies.
260 hr = SetOption(request_handle_.get(), WINHTTP_OPTION_DISABLE_FEATURE,
261 WINHTTP_DISABLE_COOKIES);
262 if (FAILED(hr)) {
263 return hr;
264 }
265
266 if (!content_type_.empty()) {
267 additional_headers.insert({"Content-Type", content_type_});
268 }
269
Sorin Jianu15ab6972024-09-18 23:04:03270 for (const auto& [name, value] : additional_headers) {
271 const std::wstring raw_header =
272 base::SysUTF8ToWide(base::StrCat({name, ": ", value, "\r\n"}));
S. Ganeshe02679752024-06-05 21:41:44273 if (!::WinHttpAddRequestHeaders(
274 request_handle_.get(), raw_header.c_str(), raw_header.size(),
275 WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
276 PLOG(ERROR) << "Failed to set the request header: " << raw_header;
277 }
278 }
279
280 hr = SendRequest(data);
281 if (FAILED(hr)) {
282 return hr;
283 }
284
285 return S_OK;
286 }();
287
288 if (FAILED(net_error_)) {
289 CompleteFetch();
290 }
291}
292
Roger Tawac0a1b9c2021-09-23 23:34:04293ScopedHInternet NetworkFetcher::Connect() {
Sorin Jianued722422020-05-29 19:17:14294 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Roger Tawac0a1b9c2021-09-23 23:34:04295 return ScopedHInternet(::WinHttpConnect(
Sorin Jianu622be9c2024-02-15 00:17:42296 session_handle_->handle(), base::SysUTF8ToWide(host_).c_str(), port_, 0));
Sorin Jianubf44a912019-03-02 01:35:01297}
298
Roger Tawac0a1b9c2021-09-23 23:34:04299ScopedHInternet NetworkFetcher::OpenRequest() {
Sorin Jianued722422020-05-29 19:17:14300 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:01301 uint32_t flags = WINHTTP_FLAG_REFRESH;
Sorin Jianu40b6bd02024-01-11 00:25:04302 if (is_https_) {
Sorin Jianubf44a912019-03-02 01:35:01303 flags |= WINHTTP_FLAG_SECURE;
Sorin Jianu40b6bd02024-01-11 00:25:04304 }
Roger Tawac0a1b9c2021-09-23 23:34:04305 return ScopedHInternet(::WinHttpOpenRequest(
Sorin Jianubf44a912019-03-02 01:35:01306 connect_handle_.get(), verb_.data(),
307 base::SysUTF8ToWide(path_for_request_).c_str(), nullptr,
308 WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, flags));
309}
310
Roger Tawac0a1b9c2021-09-23 23:34:04311HRESULT NetworkFetcher::SendRequest(const std::string& data) {
Sorin Jianued722422020-05-29 19:17:14312 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianu6cd7bc52019-11-05 16:36:45313
314 VLOG(2) << data;
315
Xiaoling Bao3d98e162022-11-12 03:32:18316 // Make a copy of the request data to ensure the buffer is available until
317 // the request is processed.
318 request_data_ = data;
319
320 const uint32_t bytes_to_send =
321 base::saturated_cast<uint32_t>(request_data_.size());
322 void* request_body = bytes_to_send ? const_cast<char*>(request_data_.c_str())
323 : WINHTTP_NO_REQUEST_DATA;
Sorin Jianubf44a912019-03-02 01:35:01324 if (!::WinHttpSendRequest(request_handle_.get(),
325 WINHTTP_NO_ADDITIONAL_HEADERS, 0, request_body,
326 bytes_to_send, bytes_to_send, context())) {
327 return HRESULTFromLastError();
328 }
329
330 return S_OK;
331}
332
Roger Tawac0a1b9c2021-09-23 23:34:04333void NetworkFetcher::SendRequestComplete() {
Sorin Jianued722422020-05-29 19:17:14334 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:01335 net_error_ = ReceiveResponse();
Sorin Jianu40b6bd02024-01-11 00:25:04336 if (FAILED(net_error_)) {
Sorin Jianu660bfa82020-05-31 15:28:56337 CompleteFetch();
Sorin Jianu40b6bd02024-01-11 00:25:04338 }
Sorin Jianubf44a912019-03-02 01:35:01339}
340
Roger Tawac0a1b9c2021-09-23 23:34:04341HRESULT NetworkFetcher::ReceiveResponse() {
Sorin Jianued722422020-05-29 19:17:14342 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Joshua Pawlickibb0a3f5c2023-09-20 01:08:36343 if (!request_handle_.is_valid()) {
344 return HRESULT_FROM_WIN32(ERROR_CANCELLED);
345 }
Sorin Jianu40b6bd02024-01-11 00:25:04346 if (!::WinHttpReceiveResponse(request_handle_.get(), nullptr)) {
Sorin Jianubf44a912019-03-02 01:35:01347 return HRESULTFromLastError();
Sorin Jianu40b6bd02024-01-11 00:25:04348 }
Sorin Jianubf44a912019-03-02 01:35:01349 return S_OK;
350}
351
Roger Tawac0a1b9c2021-09-23 23:34:04352void NetworkFetcher::HeadersAvailable() {
Sorin Jianued722422020-05-29 19:17:14353 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:01354
Joshua Pawlickibb0a3f5c2023-09-20 01:08:36355 if (!request_handle_.is_valid()) {
356 CompleteFetch();
357 return;
358 }
359
Sorin Jianu2db8067d2023-07-07 01:10:38360 std::wstring request_headers;
361 QueryHeadersString(
362 request_handle_.get(),
363 WINHTTP_QUERY_RAW_HEADERS_CRLF | WINHTTP_QUERY_FLAG_REQUEST_HEADERS,
364 WINHTTP_HEADER_NAME_BY_INDEX, &request_headers);
365 VLOG(3) << "request headers:" << std::endl << request_headers;
366 std::wstring response_headers;
Sorin Jianubf44a912019-03-02 01:35:01367 QueryHeadersString(request_handle_.get(), WINHTTP_QUERY_RAW_HEADERS_CRLF,
Sorin Jianu2db8067d2023-07-07 01:10:38368 WINHTTP_HEADER_NAME_BY_INDEX, &response_headers);
369 VLOG(3) << "response headers:" << std::endl << response_headers;
Sorin Jianubf44a912019-03-02 01:35:01370
Sorin Jianubf44a912019-03-02 01:35:01371 net_error_ = QueryHeadersInt(request_handle_.get(), WINHTTP_QUERY_STATUS_CODE,
Sebastien Lalancettee26adaec2022-01-05 15:32:46372 WINHTTP_HEADER_NAME_BY_INDEX, &response_code_);
Sorin Jianubf44a912019-03-02 01:35:01373 if (FAILED(net_error_)) {
Sorin Jianu660bfa82020-05-31 15:28:56374 CompleteFetch();
Sorin Jianubf44a912019-03-02 01:35:01375 return;
376 }
377
378 int content_length = 0;
379 net_error_ =
380 QueryHeadersInt(request_handle_.get(), WINHTTP_QUERY_CONTENT_LENGTH,
381 WINHTTP_HEADER_NAME_BY_INDEX, &content_length);
Sorin Jianu3f607e072023-03-26 12:16:19382 std::move(fetch_started_callback_)
383 .Run(response_code_, SUCCEEDED(net_error_) ? content_length : -1);
Sorin Jianubf44a912019-03-02 01:35:01384
Sorin Jianu617e7792020-05-30 00:53:53385 // Start reading the body of response.
386 net_error_ = ReadData();
Sorin Jianu40b6bd02024-01-11 00:25:04387 if (FAILED(net_error_)) {
Sorin Jianu660bfa82020-05-31 15:28:56388 CompleteFetch();
Sorin Jianu40b6bd02024-01-11 00:25:04389 }
Sorin Jianubf44a912019-03-02 01:35:01390}
391
Roger Tawac0a1b9c2021-09-23 23:34:04392HRESULT NetworkFetcher::ReadData() {
Sorin Jianued722422020-05-29 19:17:14393 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:01394
Joshua Pawlickibb0a3f5c2023-09-20 01:08:36395 if (!request_handle_.is_valid()) {
396 return HRESULT_FROM_WIN32(ERROR_CANCELLED);
397 }
398
Sorin Jianu617e7792020-05-30 00:53:53399 // Use a fixed buffer size, larger than the internal WinHTTP buffer size (8K),
400 // according to the documentation for WinHttpReadData.
Sorin Jianued722422020-05-29 19:17:14401 constexpr size_t kNumBytesToRead = 0x4000; // 16KiB.
402 read_buffer_.resize(kNumBytesToRead);
Sorin Jianubf44a912019-03-02 01:35:01403
404 if (!::WinHttpReadData(request_handle_.get(), &read_buffer_.front(),
405 read_buffer_.size(), nullptr)) {
406 return HRESULTFromLastError();
407 }
Sorin Jianu617e7792020-05-30 00:53:53408
Sorin Jianu660bfa82020-05-31 15:28:56409 DVLOG(3) << "reading data...";
Sorin Jianubf44a912019-03-02 01:35:01410 return S_OK;
411}
412
Roger Tawac0a1b9c2021-09-23 23:34:04413void NetworkFetcher::ReadDataComplete(size_t num_bytes_read) {
Sorin Jianued722422020-05-29 19:17:14414 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:01415 read_buffer_.resize(num_bytes_read);
Sorin Jianud1c26aa2024-02-13 20:23:41416 if (write_data_callback_) {
417 write_data_callback_.Run();
418 }
Sorin Jianubf44a912019-03-02 01:35:01419}
420
Sorin Jianu92a26c92022-10-19 17:07:20421void NetworkFetcher::RequestError(DWORD error) {
Sorin Jianued722422020-05-29 19:17:14422 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianu92a26c92022-10-19 17:07:20423 net_error_ = HRESULT_FROM_WIN32(error);
Sorin Jianu660bfa82020-05-31 15:28:56424 CompleteFetch();
Sorin Jianubf44a912019-03-02 01:35:01425}
426
Roger Tawac0a1b9c2021-09-23 23:34:04427void NetworkFetcher::WriteDataToFile() {
Sorin Jianued722422020-05-29 19:17:14428 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianu55c77daa2022-10-19 20:41:58429 VLOG(3) << __func__;
Gabriel Charette1a24fba82020-02-28 16:54:37430 base::ThreadPool::PostTaskAndReplyWithResult(
Sorin Jianubf44a912019-03-02 01:35:01431 FROM_HERE, kTaskTraits,
Roger Tawac0a1b9c2021-09-23 23:34:04432 base::BindOnce(&NetworkFetcher::WriteDataToFileBlocking, this),
433 base::BindOnce(&NetworkFetcher::WriteDataToFileComplete, this));
Sorin Jianubf44a912019-03-02 01:35:01434}
435
Sorin Jianued722422020-05-29 19:17:14436// Returns true if EOF is reached.
Roger Tawac0a1b9c2021-09-23 23:34:04437bool NetworkFetcher::WriteDataToFileBlocking() {
Sorin Jianu55c77daa2022-10-19 20:41:58438 VLOG(3) << __func__;
439
Sorin Jianubf44a912019-03-02 01:35:01440 if (read_buffer_.empty()) {
441 file_.Close();
442 net_error_ = S_OK;
443 return true;
444 }
445
446 if (!file_.IsValid()) {
Alexei Svitkine85f53a4c2021-11-30 20:00:13447 file_.Initialize(file_path_,
448 base::File::Flags::FLAG_CREATE_ALWAYS |
449 base::File::Flags::FLAG_WRITE |
450 base::File::Flags::FLAG_WIN_SEQUENTIAL_SCAN);
Sorin Jianubf44a912019-03-02 01:35:01451 if (!file_.IsValid()) {
Roger Tawac0a1b9c2021-09-23 23:34:04452 net_error_ = HRESULTFromLastError();
Sorin Jianubf44a912019-03-02 01:35:01453 return false;
454 }
455 }
456
Tom Sepez9ccb7912024-09-23 16:52:15457 if (!file_.WriteAtCurrentPosAndCheck(base::as_byte_span(read_buffer_))) {
Roger Tawac0a1b9c2021-09-23 23:34:04458 net_error_ = HRESULTFromLastError();
Sorin Jianubf44a912019-03-02 01:35:01459 file_.Close();
Lei Zhang3c167232020-07-08 00:33:53460 base::DeleteFile(file_path_);
Sorin Jianubf44a912019-03-02 01:35:01461 return false;
462 }
463
464 content_size_ += read_buffer_.size();
465 return false;
466}
467
Roger Tawac0a1b9c2021-09-23 23:34:04468void NetworkFetcher::WriteDataToFileComplete(bool is_eof) {
Sorin Jianued722422020-05-29 19:17:14469 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianu55c77daa2022-10-19 20:41:58470 VLOG(3) << __func__;
Sorin Jianued722422020-05-29 19:17:14471
472 fetch_progress_callback_.Run(base::saturated_cast<int64_t>(content_size_));
Sorin Jianubf44a912019-03-02 01:35:01473
474 if (is_eof || FAILED(net_error_)) {
Sorin Jianu660bfa82020-05-31 15:28:56475 CompleteFetch();
Sorin Jianubf44a912019-03-02 01:35:01476 return;
477 }
478
Sorin Jianu617e7792020-05-30 00:53:53479 net_error_ = ReadData();
Sorin Jianu40b6bd02024-01-11 00:25:04480 if (FAILED(net_error_)) {
Sorin Jianu660bfa82020-05-31 15:28:56481 CompleteFetch();
Sorin Jianu40b6bd02024-01-11 00:25:04482 }
Sorin Jianubf44a912019-03-02 01:35:01483}
484
Roger Tawac0a1b9c2021-09-23 23:34:04485void NetworkFetcher::WriteDataToMemory() {
Sorin Jianued722422020-05-29 19:17:14486 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Sorin Jianubf44a912019-03-02 01:35:01487
488 if (read_buffer_.empty()) {
Sorin Jianuf4f51699e2019-03-27 20:39:03489 VLOG(2) << post_response_body_;
Sorin Jianubf44a912019-03-02 01:35:01490 net_error_ = S_OK;
Sorin Jianu660bfa82020-05-31 15:28:56491 CompleteFetch();
Sorin Jianubf44a912019-03-02 01:35:01492 return;
493 }
494
495 post_response_body_.append(read_buffer_.begin(), read_buffer_.end());
496 content_size_ += read_buffer_.size();
Sorin Jianued722422020-05-29 19:17:14497 fetch_progress_callback_.Run(base::saturated_cast<int64_t>(content_size_));
Sorin Jianubf44a912019-03-02 01:35:01498
Sorin Jianu617e7792020-05-30 00:53:53499 net_error_ = ReadData();
Sorin Jianu40b6bd02024-01-11 00:25:04500 if (FAILED(net_error_)) {
Sorin Jianu660bfa82020-05-31 15:28:56501 CompleteFetch();
Sorin Jianu40b6bd02024-01-11 00:25:04502 }
Sorin Jianubf44a912019-03-02 01:35:01503}
504
Roger Tawac0a1b9c2021-09-23 23:34:04505void __stdcall NetworkFetcher::WinHttpStatusCallback(HINTERNET handle,
506 DWORD_PTR context,
507 DWORD status,
508 void* info,
509 DWORD info_len) {
Sorin Jianua89cb852024-03-23 02:50:57510 CHECK(handle);
511 CHECK(context);
Sorin Jianubf44a912019-03-02 01:35:01512
Helmut Januschka1dce9dc2024-06-11 13:05:35513 std::string_view status_string;
Peter Kasting5d609b8f2021-02-16 06:37:50514 std::wstring info_string;
Sorin Jianubf44a912019-03-02 01:35:01515 switch (status) {
516 case WINHTTP_CALLBACK_STATUS_HANDLE_CREATED:
517 status_string = "handle created";
518 break;
519 case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
520 status_string = "handle closing";
521 break;
522 case WINHTTP_CALLBACK_STATUS_RESOLVING_NAME:
523 status_string = "resolving";
Sorin Jianu841da322024-03-27 02:37:51524 CHECK(info);
525 info_string = static_cast<const wchar_t*>(info); // host.
Sorin Jianu882ecf812024-03-05 22:33:01526 VLOG(1) << "hostname: " << info_string;
Sorin Jianubf44a912019-03-02 01:35:01527 break;
528 case WINHTTP_CALLBACK_STATUS_NAME_RESOLVED:
529 status_string = "resolved";
530 break;
531 case WINHTTP_CALLBACK_STATUS_CONNECTING_TO_SERVER:
532 status_string = "connecting";
Sorin Jianu841da322024-03-27 02:37:51533 CHECK(info);
534 info_string = static_cast<const wchar_t*>(info); // IP.
Sorin Jianu882ecf812024-03-05 22:33:01535 VLOG(1) << "ip: " << info_string;
Sorin Jianubf44a912019-03-02 01:35:01536 break;
537 case WINHTTP_CALLBACK_STATUS_CONNECTED_TO_SERVER:
538 status_string = "connected";
539 break;
540 case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
541 status_string = "sending";
542 break;
543 case WINHTTP_CALLBACK_STATUS_REQUEST_SENT:
544 status_string = "sent";
545 break;
546 case WINHTTP_CALLBACK_STATUS_RECEIVING_RESPONSE:
547 status_string = "receiving response";
548 break;
549 case WINHTTP_CALLBACK_STATUS_RESPONSE_RECEIVED:
550 status_string = "response received";
551 break;
552 case WINHTTP_CALLBACK_STATUS_CLOSING_CONNECTION:
553 status_string = "connection closing";
554 break;
555 case WINHTTP_CALLBACK_STATUS_CONNECTION_CLOSED:
556 status_string = "connection closed";
557 break;
558 case WINHTTP_CALLBACK_STATUS_REDIRECT:
Sorin Jianu617e7792020-05-30 00:53:53559 // |info| may contain invalid URL data and not safe to reference always.
Sorin Jianubf44a912019-03-02 01:35:01560 status_string = "redirect";
Sorin Jianubf44a912019-03-02 01:35:01561 break;
562 case WINHTTP_CALLBACK_STATUS_DATA_AVAILABLE:
563 status_string = "data available";
Sorin Jianua89cb852024-03-23 02:50:57564 CHECK(info);
565 CHECK_EQ(info_len, sizeof(uint32_t));
Peter Kasting36f86c62023-09-22 16:39:41566 info_string = base::NumberToWString(*static_cast<uint32_t*>(info));
Sorin Jianubf44a912019-03-02 01:35:01567 break;
568 case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
569 status_string = "headers available";
570 break;
571 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
572 status_string = "read complete";
Peter Kasting36f86c62023-09-22 16:39:41573 info_string = base::NumberToWString(info_len);
Sorin Jianubf44a912019-03-02 01:35:01574 break;
575 case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
576 status_string = "send request complete";
577 break;
578 case WINHTTP_CALLBACK_STATUS_WRITE_COMPLETE:
579 status_string = "write complete";
580 break;
581 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
582 status_string = "request error";
583 break;
584 case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
585 status_string = "https failure";
Sorin Jianua89cb852024-03-23 02:50:57586 CHECK(info);
587 CHECK_EQ(info_len, sizeof(uint32_t));
Peter Kasting36f86c62023-09-22 16:39:41588 info_string = base::ASCIIToWide(
589 base::StringPrintf("%#x", *static_cast<uint32_t*>(info)));
Sorin Jianubf44a912019-03-02 01:35:01590 break;
591 default:
592 status_string = "unknown callback";
593 break;
594 }
595
596 std::string msg;
Sorin Jianu40b6bd02024-01-11 00:25:04597 if (!status_string.empty()) {
Sorin Jianubf44a912019-03-02 01:35:01598 base::StringAppendF(&msg, "status=%s", status_string.data());
Sorin Jianu40b6bd02024-01-11 00:25:04599 } else {
Sorin Jianu92a26c92022-10-19 17:07:20600 base::StringAppendF(&msg, "status=%#lx", status);
Sorin Jianu40b6bd02024-01-11 00:25:04601 }
Sorin Jianu92a26c92022-10-19 17:07:20602 if (!info_string.empty()) {
Sorin Jianubf44a912019-03-02 01:35:01603 base::StringAppendF(&msg, ", info=%s",
604 base::SysWideToUTF8(info_string).c_str());
Sorin Jianu92a26c92022-10-19 17:07:20605 }
Sorin Jianu40b6bd02024-01-11 00:25:04606 VLOG(3) << "WinHttp status callback:" << " handle=" << handle << ", " << msg;
Sorin Jianubf44a912019-03-02 01:35:01607
Sorin Jianu92a26c92022-10-19 17:07:20608 NetworkFetcher* network_fetcher = reinterpret_cast<NetworkFetcher*>(context);
609 base::OnceClosure callback;
Sorin Jianubf44a912019-03-02 01:35:01610 switch (status) {
Sorin Jianubf44a912019-03-02 01:35:01611 case WINHTTP_CALLBACK_STATUS_HANDLE_CLOSING:
Sorin Jianu92a26c92022-10-19 17:07:20612 callback =
613 base::BindOnce(&NetworkFetcher::HandleClosing, network_fetcher);
Sorin Jianubf44a912019-03-02 01:35:01614 break;
615 case WINHTTP_CALLBACK_STATUS_SENDREQUEST_COMPLETE:
Sorin Jianu92a26c92022-10-19 17:07:20616 callback =
617 base::BindOnce(&NetworkFetcher::SendRequestComplete, network_fetcher);
Sorin Jianubf44a912019-03-02 01:35:01618 break;
619 case WINHTTP_CALLBACK_STATUS_HEADERS_AVAILABLE:
Sorin Jianu92a26c92022-10-19 17:07:20620 callback =
621 base::BindOnce(&NetworkFetcher::HeadersAvailable, network_fetcher);
Sorin Jianubf44a912019-03-02 01:35:01622 break;
623 case WINHTTP_CALLBACK_STATUS_READ_COMPLETE:
Sorin Jianua89cb852024-03-23 02:50:57624 CHECK_EQ(info, &network_fetcher->read_buffer_.front());
Sorin Jianu92a26c92022-10-19 17:07:20625 callback = base::BindOnce(&NetworkFetcher::ReadDataComplete,
626 network_fetcher, size_t{info_len});
Sorin Jianubf44a912019-03-02 01:35:01627 break;
628 case WINHTTP_CALLBACK_STATUS_REQUEST_ERROR:
Sorin Jianua89cb852024-03-23 02:50:57629 CHECK(info);
Sorin Jianu92a26c92022-10-19 17:07:20630 callback = base::BindOnce(
631 &NetworkFetcher::RequestError, network_fetcher,
632 static_cast<const WINHTTP_ASYNC_RESULT*>(info)->dwError);
Sorin Jianubf44a912019-03-02 01:35:01633 break;
Sorin Jianubf44a912019-03-02 01:35:01634 }
Sorin Jianu92a26c92022-10-19 17:07:20635 if (callback) {
Sorin Jianu6092b8c12023-02-01 17:40:05636 network_fetcher->main_task_runner_->PostTask(FROM_HERE,
637 std::move(callback));
Sorin Jianu92a26c92022-10-19 17:07:20638 }
Sorin Jianu95fb33d2019-02-21 16:59:33639}
640
Roger Tawac0a1b9c2021-09-23 23:34:04641} // namespace winhttp