| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "nacl_io/httpfs/http_fs_node.h" | 5 #include "nacl_io/httpfs/http_fs_node.h" |
| 6 | 6 |
| 7 #include <assert.h> | 7 #include <assert.h> |
| 8 #include <errno.h> | 8 #include <errno.h> |
| 9 #include <stdio.h> | 9 #include <stdio.h> |
| 10 #include <string.h> | 10 #include <string.h> |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 start = &headers[i + 1]; | 76 start = &headers[i + 1]; |
| 77 state = FINDING_KEY; | 77 state = FINDING_KEY; |
| 78 } | 78 } |
| 79 break; | 79 break; |
| 80 } | 80 } |
| 81 } | 81 } |
| 82 | 82 |
| 83 return result; | 83 return result; |
| 84 } | 84 } |
| 85 | 85 |
| 86 bool ParseContentLength(const StringMap_t& headers, size_t* content_length) { | 86 bool ParseContentLength(const StringMap_t& headers, off_t* content_length) { |
| 87 StringMap_t::const_iterator iter = headers.find("Content-Length"); | 87 StringMap_t::const_iterator iter = headers.find("Content-Length"); |
| 88 if (iter == headers.end()) | 88 if (iter == headers.end()) |
| 89 return false; | 89 return false; |
| 90 | 90 |
| 91 *content_length = strtoul(iter->second.c_str(), NULL, 10); | 91 *content_length = strtoull(iter->second.c_str(), NULL, 10); |
| 92 return true; | 92 return true; |
| 93 } | 93 } |
| 94 | 94 |
| 95 bool ParseContentRange(const StringMap_t& headers, | 95 bool ParseContentRange(const StringMap_t& headers, |
| 96 size_t* read_start, | 96 off_t* read_start, |
| 97 size_t* read_end, | 97 off_t* read_end, |
| 98 size_t* entity_length) { | 98 off_t* entity_length) { |
| 99 StringMap_t::const_iterator iter = headers.find("Content-Range"); | 99 StringMap_t::const_iterator iter = headers.find("Content-Range"); |
| 100 if (iter == headers.end()) | 100 if (iter == headers.end()) |
| 101 return false; | 101 return false; |
| 102 | 102 |
| 103 // The key should look like "bytes ##-##/##" or "bytes ##-##/*". The last | 103 // The key should look like "bytes ##-##/##" or "bytes ##-##/*". The last |
| 104 // value is the entity length, which can potentially be * (i.e. unknown). | 104 // value is the entity length, which can potentially be * (i.e. unknown). |
| 105 size_t read_start_int; | 105 off_t read_start_int; |
| 106 size_t read_end_int; | 106 off_t read_end_int; |
| 107 size_t entity_length_int; | 107 off_t entity_length_int; |
| 108 int result = sscanf(iter->second.c_str(), | 108 int result = sscanf(iter->second.c_str(), |
| 109 "bytes %" SCNuS "-%" SCNuS "/%" SCNuS, | 109 "bytes %" SCNi64 "-%" SCNi64 "/%" SCNi64, |
| 110 &read_start_int, | 110 &read_start_int, |
| 111 &read_end_int, | 111 &read_end_int, |
| 112 &entity_length_int); | 112 &entity_length_int); |
| 113 | 113 |
| 114 // The Content-Range header specifies an inclusive range: e.g. the first ten | 114 // The Content-Range header specifies an inclusive range: e.g. the first ten |
| 115 // bytes is "bytes 0-9/*". Convert it to a half-open range by incrementing | 115 // bytes is "bytes 0-9/*". Convert it to a half-open range by incrementing |
| 116 // read_end. | 116 // read_end. |
| 117 if (result == 2) { | 117 if (result == 2) { |
| 118 *read_start = read_start_int; | 118 *read_start = read_start_int; |
| 119 *read_end = read_end_int + 1; | 119 *read_end = read_end_int + 1; |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 191 | 191 |
| 192 Error HttpFsNode::Write(const HandleAttr& attr, | 192 Error HttpFsNode::Write(const HandleAttr& attr, |
| 193 const void* buf, | 193 const void* buf, |
| 194 size_t count, | 194 size_t count, |
| 195 int* out_bytes) { | 195 int* out_bytes) { |
| 196 // TODO(binji): support POST? | 196 // TODO(binji): support POST? |
| 197 *out_bytes = 0; | 197 *out_bytes = 0; |
| 198 return EACCES; | 198 return EACCES; |
| 199 } | 199 } |
| 200 | 200 |
| 201 Error HttpFsNode::GetSize(size_t* out_size) { | 201 Error HttpFsNode::GetSize(off_t* out_size) { |
| 202 *out_size = 0; | 202 *out_size = 0; |
| 203 | 203 |
| 204 // TODO(binji): This value should be cached properly; i.e. obey the caching | 204 // TODO(binji): This value should be cached properly; i.e. obey the caching |
| 205 // headers returned by the server. | 205 // headers returned by the server. |
| 206 AUTO_LOCK(node_lock_); | 206 AUTO_LOCK(node_lock_); |
| 207 struct stat statbuf; | 207 struct stat statbuf; |
| 208 Error error = GetStat_Locked(&statbuf); | 208 Error error = GetStat_Locked(&statbuf); |
| 209 if (error) | 209 if (error) |
| 210 return error; | 210 return error; |
| 211 | 211 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 237 Error error = OpenUrl("HEAD", | 237 Error error = OpenUrl("HEAD", |
| 238 &headers, | 238 &headers, |
| 239 &loader, | 239 &loader, |
| 240 &request, | 240 &request, |
| 241 &response, | 241 &response, |
| 242 &statuscode, | 242 &statuscode, |
| 243 &response_headers); | 243 &response_headers); |
| 244 if (error) | 244 if (error) |
| 245 return error; | 245 return error; |
| 246 | 246 |
| 247 size_t entity_length; | 247 off_t entity_length; |
| 248 if (ParseContentLength(response_headers, &entity_length)) { | 248 if (ParseContentLength(response_headers, &entity_length)) { |
| 249 SetCachedSize(static_cast<off_t>(entity_length)); | 249 SetCachedSize(static_cast<off_t>(entity_length)); |
| 250 } else if (cache_content_) { | 250 } else if (cache_content_) { |
| 251 // The server didn't give a content length; download the data to memory | 251 // The server didn't give a content length; download the data to memory |
| 252 // via DownloadToCache, which will also set stat_.st_size; | 252 // via DownloadToCache, which will also set stat_.st_size; |
| 253 error = DownloadToCache(); | 253 error = DownloadToCache(); |
| 254 if (error) | 254 if (error) |
| 255 return error; | 255 return error; |
| 256 } else { | 256 } else { |
| 257 // The user doesn't want to cache content, but we didn't get a | 257 // The user doesn't want to cache content, but we didn't get a |
| 258 // "Content-Length" header. Read the entire entity, and throw it away. | 258 // "Content-Length" header. Read the entire entity, and throw it away. |
| 259 // Don't use DownloadToCache, as that will still allocate enough memory | 259 // Don't use DownloadToCache, as that will still allocate enough memory |
| 260 // for the entire entity. | 260 // for the entire entity. |
| 261 int bytes_read; | 261 off_t bytes_read; |
| 262 error = DownloadToTemp(&bytes_read); | 262 error = DownloadToTemp(&bytes_read); |
| 263 if (error) | 263 if (error) |
| 264 return error; | 264 return error; |
| 265 | 265 |
| 266 SetCachedSize(bytes_read); | 266 SetCachedSize(bytes_read); |
| 267 } | 267 } |
| 268 | 268 |
| 269 stat_.st_atime = 0; // TODO(binji): Use "Last-Modified". | 269 stat_.st_atime = 0; // TODO(binji): Use "Last-Modified". |
| 270 stat_.st_mtime = 0; | 270 stat_.st_mtime = 0; |
| 271 stat_.st_ctime = 0; | 271 stat_.st_ctime = 0; |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 360 Error error = OpenUrl("GET", | 360 Error error = OpenUrl("GET", |
| 361 &headers, | 361 &headers, |
| 362 &loader, | 362 &loader, |
| 363 &request, | 363 &request, |
| 364 &response, | 364 &response, |
| 365 &statuscode, | 365 &statuscode, |
| 366 &response_headers); | 366 &response_headers); |
| 367 if (error) | 367 if (error) |
| 368 return error; | 368 return error; |
| 369 | 369 |
| 370 size_t content_length = 0; | 370 off_t content_length = 0; |
| 371 if (ParseContentLength(response_headers, &content_length)) { | 371 if (ParseContentLength(response_headers, &content_length)) { |
| 372 cached_data_.resize(content_length); | 372 cached_data_.resize(content_length); |
| 373 int real_size; | 373 int real_size; |
| 374 error = ReadResponseToBuffer( | 374 error = ReadResponseToBuffer( |
| 375 loader, cached_data_.data(), content_length, &real_size); | 375 loader, cached_data_.data(), content_length, &real_size); |
| 376 if (error) | 376 if (error) |
| 377 return error; | 377 return error; |
| 378 | 378 |
| 379 SetCachedSize(real_size); | 379 SetCachedSize(real_size); |
| 380 cached_data_.resize(real_size); | 380 cached_data_.resize(real_size); |
| 381 return 0; | 381 return 0; |
| 382 } | 382 } |
| 383 | 383 |
| 384 int bytes_read; | 384 int bytes_read; |
| 385 error = ReadEntireResponseToCache(loader, &bytes_read); | 385 error = ReadEntireResponseToCache(loader, &bytes_read); |
| 386 if (error) | 386 if (error) |
| 387 return error; | 387 return error; |
| 388 | 388 |
| 389 SetCachedSize(bytes_read); | 389 SetCachedSize(bytes_read); |
| 390 return 0; | 390 return 0; |
| 391 } | 391 } |
| 392 | 392 |
| 393 Error HttpFsNode::ReadPartialFromCache(const HandleAttr& attr, | 393 Error HttpFsNode::ReadPartialFromCache(const HandleAttr& attr, |
| 394 void* buf, | 394 void* buf, |
| 395 int count, | 395 int count, |
| 396 int* out_bytes) { | 396 int* out_bytes) { |
| 397 *out_bytes = 0; | 397 *out_bytes = 0; |
| 398 size_t size = cached_data_.size(); | 398 off_t size = cached_data_.size(); |
| 399 | 399 |
| 400 if (attr.offs + count > size) | 400 if (attr.offs + count > size) |
| 401 count = size - attr.offs; | 401 count = size - attr.offs; |
| 402 | 402 |
| 403 if (count <= 0) | 403 if (count <= 0) |
| 404 return 0; | 404 return 0; |
| 405 | 405 |
| 406 memcpy(buf, &cached_data_.data()[attr.offs], count); | 406 memcpy(buf, &cached_data_.data()[attr.offs], count); |
| 407 *out_bytes = count; | 407 *out_bytes = count; |
| 408 return 0; | 408 return 0; |
| 409 } | 409 } |
| 410 | 410 |
| 411 Error HttpFsNode::DownloadPartial(const HandleAttr& attr, | 411 Error HttpFsNode::DownloadPartial(const HandleAttr& attr, |
| 412 void* buf, | 412 void* buf, |
| 413 size_t count, | 413 off_t count, |
| 414 int* out_bytes) { | 414 int* out_bytes) { |
| 415 *out_bytes = 0; | 415 *out_bytes = 0; |
| 416 | 416 |
| 417 StringMap_t headers; | 417 StringMap_t headers; |
| 418 | 418 |
| 419 char buffer[100]; | 419 char buffer[100]; |
| 420 // Range request is inclusive: 0-99 returns 100 bytes. | 420 // Range request is inclusive: 0-99 returns 100 bytes. |
| 421 snprintf(&buffer[0], | 421 snprintf(&buffer[0], |
| 422 sizeof(buffer), | 422 sizeof(buffer), |
| 423 "bytes=%" PRIuS "-%" PRIuS, | 423 "bytes=%" PRIi64 "-%" PRIi64, |
| 424 attr.offs, | 424 attr.offs, |
| 425 attr.offs + count - 1); | 425 attr.offs + count - 1); |
| 426 headers["Range"] = buffer; | 426 headers["Range"] = buffer; |
| 427 | 427 |
| 428 ScopedResource loader(filesystem_->ppapi()); | 428 ScopedResource loader(filesystem_->ppapi()); |
| 429 ScopedResource request(filesystem_->ppapi()); | 429 ScopedResource request(filesystem_->ppapi()); |
| 430 ScopedResource response(filesystem_->ppapi()); | 430 ScopedResource response(filesystem_->ppapi()); |
| 431 int32_t statuscode; | 431 int32_t statuscode; |
| 432 StringMap_t response_headers; | 432 StringMap_t response_headers; |
| 433 Error error = OpenUrl("GET", | 433 Error error = OpenUrl("GET", |
| 434 &headers, | 434 &headers, |
| 435 &loader, | 435 &loader, |
| 436 &request, | 436 &request, |
| 437 &response, | 437 &response, |
| 438 &statuscode, | 438 &statuscode, |
| 439 &response_headers); | 439 &response_headers); |
| 440 if (error) { | 440 if (error) { |
| 441 if (statuscode == STATUSCODE_REQUESTED_RANGE_NOT_SATISFIABLE) { | 441 if (statuscode == STATUSCODE_REQUESTED_RANGE_NOT_SATISFIABLE) { |
| 442 // We're likely trying to read past the end. Return 0 bytes. | 442 // We're likely trying to read past the end. Return 0 bytes. |
| 443 *out_bytes = 0; | 443 *out_bytes = 0; |
| 444 return 0; | 444 return 0; |
| 445 } | 445 } |
| 446 | 446 |
| 447 return error; | 447 return error; |
| 448 } | 448 } |
| 449 | 449 |
| 450 size_t read_start = 0; | 450 off_t read_start = 0; |
| 451 if (statuscode == STATUSCODE_OK) { | 451 if (statuscode == STATUSCODE_OK) { |
| 452 // No partial result, read everything starting from the part we care about. | 452 // No partial result, read everything starting from the part we care about. |
| 453 size_t content_length; | 453 off_t content_length; |
| 454 if (ParseContentLength(response_headers, &content_length)) { | 454 if (ParseContentLength(response_headers, &content_length)) { |
| 455 if (attr.offs >= content_length) | 455 if (attr.offs >= content_length) |
| 456 return EINVAL; | 456 return EINVAL; |
| 457 | 457 |
| 458 // Clamp count, if trying to read past the end of the file. | 458 // Clamp count, if trying to read past the end of the file. |
| 459 if (attr.offs + count > content_length) { | 459 if (attr.offs + count > content_length) { |
| 460 count = content_length - attr.offs; | 460 count = content_length - attr.offs; |
| 461 } | 461 } |
| 462 } | 462 } |
| 463 } else if (statuscode == STATUSCODE_PARTIAL_CONTENT) { | 463 } else if (statuscode == STATUSCODE_PARTIAL_CONTENT) { |
| 464 // Determine from the headers where we are reading. | 464 // Determine from the headers where we are reading. |
| 465 size_t read_end; | 465 off_t read_end; |
| 466 size_t entity_length; | 466 off_t entity_length; |
| 467 if (ParseContentRange( | 467 if (ParseContentRange( |
| 468 response_headers, &read_start, &read_end, &entity_length)) { | 468 response_headers, &read_start, &read_end, &entity_length)) { |
| 469 if (read_start > attr.offs || read_start > read_end) { | 469 if (read_start > attr.offs || read_start > read_end) { |
| 470 // If this error occurs, the server is returning bogus values. | 470 // If this error occurs, the server is returning bogus values. |
| 471 return EINVAL; | 471 return EINVAL; |
| 472 } | 472 } |
| 473 | 473 |
| 474 // Clamp count, if trying to read past the end of the file. | 474 // Clamp count, if trying to read past the end of the file. |
| 475 count = std::min(read_end - read_start, count); | 475 count = std::min(read_end - read_start, count); |
| 476 } else { | 476 } else { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 494 // Tried to read past the end of the entity. | 494 // Tried to read past the end of the entity. |
| 495 if (bytes_read < bytes_to_read) { | 495 if (bytes_read < bytes_to_read) { |
| 496 *out_bytes = 0; | 496 *out_bytes = 0; |
| 497 return 0; | 497 return 0; |
| 498 } | 498 } |
| 499 } | 499 } |
| 500 | 500 |
| 501 return ReadResponseToBuffer(loader, buf, count, out_bytes); | 501 return ReadResponseToBuffer(loader, buf, count, out_bytes); |
| 502 } | 502 } |
| 503 | 503 |
| 504 Error HttpFsNode::DownloadToTemp(int* out_bytes) { | 504 Error HttpFsNode::DownloadToTemp(off_t* out_bytes) { |
| 505 StringMap_t headers; | 505 StringMap_t headers; |
| 506 ScopedResource loader(filesystem_->ppapi()); | 506 ScopedResource loader(filesystem_->ppapi()); |
| 507 ScopedResource request(filesystem_->ppapi()); | 507 ScopedResource request(filesystem_->ppapi()); |
| 508 ScopedResource response(filesystem_->ppapi()); | 508 ScopedResource response(filesystem_->ppapi()); |
| 509 int32_t statuscode; | 509 int32_t statuscode; |
| 510 StringMap_t response_headers; | 510 StringMap_t response_headers; |
| 511 Error error = OpenUrl("GET", | 511 Error error = OpenUrl("GET", |
| 512 &headers, | 512 &headers, |
| 513 &loader, | 513 &loader, |
| 514 &request, | 514 &request, |
| 515 &response, | 515 &response, |
| 516 &statuscode, | 516 &statuscode, |
| 517 &response_headers); | 517 &response_headers); |
| 518 if (error) | 518 if (error) |
| 519 return error; | 519 return error; |
| 520 | 520 |
| 521 size_t content_length = 0; | 521 off_t content_length = 0; |
| 522 if (ParseContentLength(response_headers, &content_length)) { | 522 if (ParseContentLength(response_headers, &content_length)) { |
| 523 *out_bytes = content_length; | 523 *out_bytes = content_length; |
| 524 return 0; | 524 return 0; |
| 525 } | 525 } |
| 526 | 526 |
| 527 return ReadEntireResponseToTemp(loader, out_bytes); | 527 return ReadEntireResponseToTemp(loader, out_bytes); |
| 528 } | 528 } |
| 529 | 529 |
| 530 Error HttpFsNode::ReadEntireResponseToTemp(const ScopedResource& loader, | 530 Error HttpFsNode::ReadEntireResponseToTemp(const ScopedResource& loader, |
| 531 int* out_bytes) { | 531 off_t* out_bytes) { |
| 532 *out_bytes = 0; | 532 *out_bytes = 0; |
| 533 | 533 |
| 534 const int kBytesToRead = MAX_READ_BUFFER_SIZE; | 534 const int kBytesToRead = MAX_READ_BUFFER_SIZE; |
| 535 buffer_.resize(kBytesToRead); | 535 buffer_.resize(kBytesToRead); |
| 536 | 536 |
| 537 while (true) { | 537 while (true) { |
| 538 int bytes_read; | 538 int bytes_read; |
| 539 Error error = | 539 Error error = |
| 540 ReadResponseToBuffer(loader, buffer_.data(), kBytesToRead, &bytes_read); | 540 ReadResponseToBuffer(loader, buffer_.data(), kBytesToRead, &bytes_read); |
| 541 if (error) | 541 if (error) |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 633 assert(bytes_read <= bytes_to_read); | 633 assert(bytes_read <= bytes_to_read); |
| 634 bytes_to_read -= bytes_read; | 634 bytes_to_read -= bytes_read; |
| 635 out_buffer += bytes_read; | 635 out_buffer += bytes_read; |
| 636 } | 636 } |
| 637 | 637 |
| 638 *out_bytes = count; | 638 *out_bytes = count; |
| 639 return 0; | 639 return 0; |
| 640 } | 640 } |
| 641 | 641 |
| 642 } // namespace nacl_io | 642 } // namespace nacl_io |
| OLD | NEW |