asar_url_loader.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. // Copyright (c) 2019 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/browser/net/asar/asar_url_loader.h"
  5. #include <algorithm>
  6. #include <memory>
  7. #include <string>
  8. #include <string_view>
  9. #include <utility>
  10. #include <vector>
  11. #include "base/task/thread_pool.h"
  12. #include "content/public/browser/file_url_loader.h"
  13. #include "mojo/public/cpp/bindings/receiver.h"
  14. #include "mojo/public/cpp/bindings/remote.h"
  15. #include "mojo/public/cpp/system/data_pipe_producer.h"
  16. #include "mojo/public/cpp/system/file_data_source.h"
  17. #include "net/base/filename_util.h"
  18. #include "net/base/mime_sniffer.h"
  19. #include "net/base/mime_util.h"
  20. #include "net/http/http_byte_range.h"
  21. #include "net/http/http_util.h"
  22. #include "services/network/public/mojom/url_response_head.mojom.h"
  23. #include "shell/browser/net/asar/asar_file_validator.h"
  24. #include "shell/common/asar/archive.h"
  25. #include "shell/common/asar/asar_util.h"
  26. namespace asar {
  27. namespace {
  28. net::Error ConvertMojoResultToNetError(MojoResult result) {
  29. switch (result) {
  30. case MOJO_RESULT_OK:
  31. return net::OK;
  32. case MOJO_RESULT_NOT_FOUND:
  33. return net::ERR_FILE_NOT_FOUND;
  34. case MOJO_RESULT_PERMISSION_DENIED:
  35. return net::ERR_ACCESS_DENIED;
  36. case MOJO_RESULT_RESOURCE_EXHAUSTED:
  37. return net::ERR_INSUFFICIENT_RESOURCES;
  38. case MOJO_RESULT_ABORTED:
  39. return net::ERR_ABORTED;
  40. default:
  41. return net::ERR_FAILED;
  42. }
  43. }
  44. constexpr size_t kDefaultFileUrlPipeSize = 65536;
  45. // Because this makes things simpler.
  46. static_assert(kDefaultFileUrlPipeSize >= net::kMaxBytesToSniff,
  47. "Default file data pipe size must be at least as large as a MIME-"
  48. "type sniffing buffer.");
  49. // Modified from the |FileURLLoader| in |file_url_loader_factory.cc|, to serve
  50. // asar files instead of normal files.
  51. class AsarURLLoader : public network::mojom::URLLoader {
  52. public:
  53. static void CreateAndStart(
  54. const network::ResourceRequest& request,
  55. mojo::PendingReceiver<network::mojom::URLLoader> loader,
  56. mojo::PendingRemote<network::mojom::URLLoaderClient> client,
  57. scoped_refptr<net::HttpResponseHeaders> extra_response_headers) {
  58. // Owns itself. Will live as long as its URLLoader and URLLoaderClientPtr
  59. // bindings are alive - essentially until either the client gives up or all
  60. // file data has been sent to it.
  61. auto* asar_url_loader = new AsarURLLoader;
  62. asar_url_loader->Start(request, std::move(loader), std::move(client),
  63. std::move(extra_response_headers));
  64. }
  65. // network::mojom::URLLoader:
  66. void FollowRedirect(
  67. const std::vector<std::string>& removed_headers,
  68. const net::HttpRequestHeaders& modified_headers,
  69. const net::HttpRequestHeaders& modified_cors_exempt_headers,
  70. const std::optional<GURL>& new_url) override {}
  71. void SetPriority(net::RequestPriority priority,
  72. int32_t intra_priority_value) override {}
  73. // disable copy
  74. AsarURLLoader(const AsarURLLoader&) = delete;
  75. AsarURLLoader& operator=(const AsarURLLoader&) = delete;
  76. private:
  77. AsarURLLoader() = default;
  78. ~AsarURLLoader() override = default;
  79. void Start(const network::ResourceRequest& request,
  80. mojo::PendingReceiver<network::mojom::URLLoader> loader,
  81. mojo::PendingRemote<network::mojom::URLLoaderClient> client,
  82. scoped_refptr<net::HttpResponseHeaders> extra_response_headers) {
  83. auto head = network::mojom::URLResponseHead::New();
  84. head->request_start = base::TimeTicks::Now();
  85. head->response_start = base::TimeTicks::Now();
  86. head->headers = extra_response_headers;
  87. base::FilePath path;
  88. if (!net::FileURLToFilePath(request.url, &path)) {
  89. mojo::Remote<network::mojom::URLLoaderClient> client_remote(
  90. std::move(client));
  91. client_remote->OnComplete(
  92. network::URLLoaderCompletionStatus(net::ERR_FAILED));
  93. MaybeDeleteSelf();
  94. return;
  95. }
  96. // Determine whether it is an asar file.
  97. base::FilePath asar_path, relative_path;
  98. if (!GetAsarArchivePath(path, &asar_path, &relative_path)) {
  99. content::CreateFileURLLoaderBypassingSecurityChecks(
  100. request, std::move(loader), std::move(client), nullptr, false,
  101. extra_response_headers);
  102. MaybeDeleteSelf();
  103. return;
  104. }
  105. client_.Bind(std::move(client));
  106. receiver_.Bind(std::move(loader));
  107. receiver_.set_disconnect_handler(base::BindOnce(
  108. &AsarURLLoader::OnConnectionError, base::Unretained(this)));
  109. // Parse asar archive.
  110. std::shared_ptr<Archive> archive = GetOrCreateAsarArchive(asar_path);
  111. Archive::FileInfo info;
  112. if (!archive || !archive->GetFileInfo(relative_path, &info)) {
  113. OnClientComplete(net::ERR_FILE_NOT_FOUND);
  114. return;
  115. }
  116. bool is_verifying_file = info.integrity.has_value();
  117. // For unpacked path, read like normal file.
  118. base::FilePath real_path;
  119. if (info.unpacked) {
  120. archive->CopyFileOut(relative_path, &real_path);
  121. info.offset = 0;
  122. }
  123. mojo::ScopedDataPipeProducerHandle producer_handle;
  124. mojo::ScopedDataPipeConsumerHandle consumer_handle;
  125. if (mojo::CreateDataPipe(kDefaultFileUrlPipeSize, producer_handle,
  126. consumer_handle) != MOJO_RESULT_OK) {
  127. OnClientComplete(net::ERR_FAILED);
  128. return;
  129. }
  130. // Note that while the |Archive| already opens a |base::File|, we still need
  131. // to create a new |base::File| here, as it might be accessed by multiple
  132. // requests at the same time.
  133. base::File file(info.unpacked ? real_path : archive->path(),
  134. base::File::FLAG_OPEN | base::File::FLAG_READ);
  135. auto file_data_source =
  136. std::make_unique<mojo::FileDataSource>(file.Duplicate());
  137. std::unique_ptr<mojo::DataPipeProducer::DataSource> readable_data_source;
  138. mojo::FileDataSource* file_data_source_raw = file_data_source.get();
  139. AsarFileValidator* file_validator_raw = nullptr;
  140. uint32_t block_size = 0;
  141. if (info.integrity.has_value()) {
  142. block_size = info.integrity.value().block_size;
  143. auto asar_validator = std::make_unique<AsarFileValidator>(
  144. std::move(info.integrity.value()), std::move(file));
  145. file_validator_raw = asar_validator.get();
  146. readable_data_source = std::make_unique<mojo::FilteredDataSource>(
  147. std::move(file_data_source), std::move(asar_validator));
  148. } else {
  149. readable_data_source = std::move(file_data_source);
  150. }
  151. std::vector<char> initial_read_buffer(
  152. std::min(static_cast<uint32_t>(net::kMaxBytesToSniff), info.size));
  153. auto read_result = readable_data_source.get()->Read(
  154. info.offset, base::span<char>(initial_read_buffer));
  155. if (read_result.result != MOJO_RESULT_OK) {
  156. OnClientComplete(ConvertMojoResultToNetError(read_result.result));
  157. return;
  158. }
  159. auto range_header =
  160. request.headers.GetHeader(net::HttpRequestHeaders::kRange);
  161. net::HttpByteRange byte_range;
  162. if (range_header) {
  163. // Handle a simple Range header for a single range.
  164. std::vector<net::HttpByteRange> ranges;
  165. bool fail = false;
  166. if (net::HttpUtil::ParseRangeHeader(range_header.value(), &ranges) &&
  167. ranges.size() == 1) {
  168. byte_range = ranges[0];
  169. if (!byte_range.ComputeBounds(info.size))
  170. fail = true;
  171. } else {
  172. fail = true;
  173. }
  174. if (fail) {
  175. OnClientComplete(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE);
  176. return;
  177. }
  178. }
  179. uint64_t first_byte_to_send = 0U;
  180. uint64_t total_bytes_dropped_from_head = initial_read_buffer.size();
  181. uint64_t total_bytes_to_send = info.size;
  182. if (byte_range.IsValid()) {
  183. first_byte_to_send = byte_range.first_byte_position();
  184. total_bytes_to_send =
  185. byte_range.last_byte_position() - first_byte_to_send + 1;
  186. }
  187. total_bytes_written_ = total_bytes_to_send;
  188. head->content_length = base::saturated_cast<int64_t>(total_bytes_to_send);
  189. if (first_byte_to_send < read_result.bytes_read) {
  190. // Write any data we read for MIME sniffing, constraining by range where
  191. // applicable. This will always fit in the pipe (see assertion near
  192. // |kDefaultFileUrlPipeSize| definition).
  193. const size_t write_size = std::min(
  194. (read_result.bytes_read - first_byte_to_send), total_bytes_to_send);
  195. base::span<const uint8_t> bytes =
  196. base::as_byte_span(initial_read_buffer)
  197. .subspan(static_cast<size_t>(first_byte_to_send), write_size);
  198. size_t bytes_written = 0;
  199. MojoResult result = producer_handle->WriteData(
  200. bytes, MOJO_WRITE_DATA_FLAG_NONE, bytes_written);
  201. if (result != MOJO_RESULT_OK || write_size != bytes_written) {
  202. OnFileWritten(result);
  203. return;
  204. }
  205. // Discount the bytes we just sent from the total range.
  206. first_byte_to_send = read_result.bytes_read;
  207. total_bytes_to_send -= write_size;
  208. } else if (is_verifying_file &&
  209. first_byte_to_send >= static_cast<uint64_t>(block_size)) {
  210. // If validation is active and the range of bytes the request wants starts
  211. // beyond the first block we need to read the next 4MB-1KB to validate
  212. // that block. Then we can skip ahead to the target block in the SetRange
  213. // call below If we hit this case it is assumed that none of the data read
  214. // will be needed by the producer
  215. uint64_t bytes_to_drop = block_size - net::kMaxBytesToSniff;
  216. total_bytes_dropped_from_head += bytes_to_drop;
  217. std::vector<char> abandoned_buffer(bytes_to_drop);
  218. auto abandon_read_result =
  219. readable_data_source.get()->Read(info.offset + net::kMaxBytesToSniff,
  220. base::span<char>(abandoned_buffer));
  221. if (abandon_read_result.result != MOJO_RESULT_OK) {
  222. OnClientComplete(
  223. ConvertMojoResultToNetError(abandon_read_result.result));
  224. return;
  225. }
  226. }
  227. if (!net::GetMimeTypeFromFile(path, &head->mime_type)) {
  228. std::string new_type;
  229. net::SniffMimeType(
  230. std::string_view(initial_read_buffer.data(), read_result.bytes_read),
  231. request.url, head->mime_type,
  232. net::ForceSniffFileUrlsForHtml::kDisabled, &new_type);
  233. head->mime_type.assign(new_type);
  234. head->did_mime_sniff = true;
  235. }
  236. if (head->headers) {
  237. head->headers->AddHeader(net::HttpRequestHeaders::kContentType,
  238. head->mime_type);
  239. }
  240. client_->OnReceiveResponse(std::move(head), std::move(consumer_handle),
  241. std::nullopt);
  242. if (total_bytes_to_send == 0) {
  243. // There's definitely no more data, so we're already done.
  244. // We provide the range data to the file validator so that
  245. // it can validate the tiny amount of data we did send
  246. if (file_validator_raw)
  247. file_validator_raw->SetRange(info.offset + first_byte_to_send,
  248. total_bytes_dropped_from_head,
  249. info.offset + info.size);
  250. OnFileWritten(MOJO_RESULT_OK);
  251. return;
  252. }
  253. if (is_verifying_file) {
  254. int start_block = first_byte_to_send / block_size;
  255. // If we're starting from the first block, we might not be starting from
  256. // where we sniffed. We might be a few KB into a file so we need to read
  257. // the data in the middle so it gets hashed.
  258. //
  259. // If we're starting from a later block we might be starting half-way
  260. // through the block regardless of what was sniffed. We need to read the
  261. // data from the start of our initial block up to the start of our actual
  262. // read point so it gets hashed.
  263. uint64_t bytes_to_drop =
  264. start_block == 0 ? first_byte_to_send - net::kMaxBytesToSniff
  265. : first_byte_to_send - (start_block * block_size);
  266. if (file_validator_raw)
  267. file_validator_raw->SetCurrentBlock(start_block);
  268. if (bytes_to_drop > 0) {
  269. uint64_t dropped_bytes_offset =
  270. info.offset + (start_block * block_size);
  271. if (start_block == 0)
  272. dropped_bytes_offset += net::kMaxBytesToSniff;
  273. total_bytes_dropped_from_head += bytes_to_drop;
  274. std::vector<char> abandoned_buffer(bytes_to_drop);
  275. auto abandon_read_result = readable_data_source.get()->Read(
  276. dropped_bytes_offset, base::span<char>(abandoned_buffer));
  277. if (abandon_read_result.result != MOJO_RESULT_OK) {
  278. OnClientComplete(
  279. ConvertMojoResultToNetError(abandon_read_result.result));
  280. return;
  281. }
  282. }
  283. }
  284. // In case of a range request, seek to the appropriate position before
  285. // sending the remaining bytes asynchronously. Under normal conditions
  286. // (i.e., no range request) this Seek is effectively a no-op.
  287. //
  288. // Note that in Electron we also need to add file offset.
  289. file_data_source_raw->SetRange(
  290. first_byte_to_send + info.offset,
  291. first_byte_to_send + info.offset + total_bytes_to_send);
  292. if (file_validator_raw)
  293. file_validator_raw->SetRange(info.offset + first_byte_to_send,
  294. total_bytes_dropped_from_head,
  295. info.offset + info.size);
  296. data_producer_ =
  297. std::make_unique<mojo::DataPipeProducer>(std::move(producer_handle));
  298. data_producer_->Write(
  299. std::move(readable_data_source),
  300. base::BindOnce(&AsarURLLoader::OnFileWritten, base::Unretained(this)));
  301. }
  302. void OnConnectionError() {
  303. receiver_.reset();
  304. MaybeDeleteSelf();
  305. }
  306. void OnClientComplete(net::Error net_error) {
  307. client_->OnComplete(network::URLLoaderCompletionStatus(net_error));
  308. client_.reset();
  309. MaybeDeleteSelf();
  310. }
  311. void MaybeDeleteSelf() {
  312. if (!receiver_.is_bound() && !client_.is_bound())
  313. delete this;
  314. }
  315. void OnFileWritten(MojoResult result) {
  316. // All the data has been written now. Close the data pipe. The consumer will
  317. // be notified that there will be no more data to read from now.
  318. data_producer_.reset();
  319. if (result == MOJO_RESULT_OK) {
  320. network::URLLoaderCompletionStatus status(net::OK);
  321. status.encoded_data_length = total_bytes_written_;
  322. status.encoded_body_length = total_bytes_written_;
  323. status.decoded_body_length = total_bytes_written_;
  324. client_->OnComplete(status);
  325. } else {
  326. client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED));
  327. }
  328. client_.reset();
  329. MaybeDeleteSelf();
  330. }
  331. std::unique_ptr<mojo::DataPipeProducer> data_producer_;
  332. mojo::Receiver<network::mojom::URLLoader> receiver_{this};
  333. mojo::Remote<network::mojom::URLLoaderClient> client_;
  334. // In case of successful loads, this holds the total number of bytes written
  335. // to the response (this may be smaller than the total size of the file when
  336. // a byte range was requested).
  337. // It is used to set some of the URLLoaderCompletionStatus data passed back
  338. // to the URLLoaderClients (eg SimpleURLLoader).
  339. size_t total_bytes_written_ = 0;
  340. };
  341. } // namespace
  342. void CreateAsarURLLoader(
  343. const network::ResourceRequest& request,
  344. mojo::PendingReceiver<network::mojom::URLLoader> loader,
  345. mojo::PendingRemote<network::mojom::URLLoaderClient> client,
  346. scoped_refptr<net::HttpResponseHeaders> extra_response_headers) {
  347. auto task_runner = base::ThreadPool::CreateSequencedTaskRunner(
  348. {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
  349. base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
  350. task_runner->PostTask(
  351. FROM_HERE,
  352. base::BindOnce(&AsarURLLoader::CreateAndStart, request, std::move(loader),
  353. std::move(client), std::move(extra_response_headers)));
  354. }
  355. } // namespace asar