Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 /* Copyright (c) 2012 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 | 5 |
| 6 #include "nacl_io/mount_http.h" | 6 #include "nacl_io/mount_http.h" |
| 7 | |
| 7 #include <assert.h> | 8 #include <assert.h> |
| 8 #include <ctype.h> | 9 #include <ctype.h> |
| 9 #include <errno.h> | 10 #include <errno.h> |
| 10 #include <fcntl.h> | 11 #include <fcntl.h> |
| 11 #include <ppapi/c/pp_errors.h> | |
| 12 #include <stdio.h> | 12 #include <stdio.h> |
| 13 #include <string.h> | 13 #include <string.h> |
| 14 #include <sys/stat.h> | 14 #include <sys/stat.h> |
| 15 #include <sys/types.h> | 15 #include <sys/types.h> |
| 16 | |
| 16 #include <vector> | 17 #include <vector> |
| 18 | |
| 19 #include <ppapi/c/pp_errors.h> | |
| 20 | |
| 17 #include "nacl_io/mount_node_dir.h" | 21 #include "nacl_io/mount_node_dir.h" |
| 22 #include "nacl_io/mount_node_http.h" | |
| 18 #include "nacl_io/osinttypes.h" | 23 #include "nacl_io/osinttypes.h" |
| 19 #include "utils/auto_lock.h" | 24 #include "utils/auto_lock.h" |
| 20 | 25 |
| 21 #if defined(WIN32) | |
| 22 #define snprintf _snprintf | |
| 23 #endif | |
| 24 | |
| 25 | |
| 26 namespace { | 26 namespace { |
| 27 | 27 |
| 28 typedef std::vector<char *> StringList_t; | 28 typedef std::vector<char*> StringList_t; |
| 29 size_t SplitString(char *str, const char *delim, StringList_t* list) { | 29 size_t SplitString(char* str, const char* delim, StringList_t* list) { |
| 30 char *item = strtok(str, delim); | 30 char* item = strtok(str, delim); |
| 31 | 31 |
| 32 list->clear(); | 32 list->clear(); |
| 33 while (item) { | 33 while (item) { |
| 34 list->push_back(item); | 34 list->push_back(item); |
| 35 item = strtok(NULL, delim); | 35 item = strtok(NULL, delim); |
| 36 } | 36 } |
| 37 | 37 |
| 38 return list->size(); | 38 return list->size(); |
| 39 } | 39 } |
| 40 | 40 |
| 41 | 41 } // namespace |
| 42 // If we're attempting to read a partial request, but the server returns a full | |
| 43 // request, we need to read all of the data up to the start of our partial | |
| 44 // request into a dummy buffer. This is the maximum size of that buffer. | |
| 45 const size_t MAX_READ_BUFFER_SIZE = 64 * 1024; | |
| 46 const int32_t STATUSCODE_OK = 200; | |
| 47 const int32_t STATUSCODE_PARTIAL_CONTENT = 206; | |
| 48 | 42 |
| 49 std::string NormalizeHeaderKey(const std::string& s) { | 43 std::string NormalizeHeaderKey(const std::string& s) { |
| 50 // Capitalize the first letter and any letter following a hyphen: | 44 // Capitalize the first letter and any letter following a hyphen: |
| 51 // e.g. ACCEPT-ENCODING -> Accept-Encoding | 45 // e.g. ACCEPT-ENCODING -> Accept-Encoding |
| 52 std::string result; | 46 std::string result; |
| 53 bool upper = true; | 47 bool upper = true; |
| 54 for (size_t i = 0; i < s.length(); ++i) { | 48 for (size_t i = 0; i < s.length(); ++i) { |
| 55 char c = s[i]; | 49 char c = s[i]; |
| 56 result += upper ? toupper(c) : tolower(c); | 50 result += upper ? toupper(c) : tolower(c); |
| 57 upper = c == '-'; | 51 upper = c == '-'; |
| 58 } | 52 } |
| 59 | 53 |
| 60 return result; | 54 return result; |
| 61 } | 55 } |
| 62 | 56 |
| 63 StringMap_t ParseHeaders(const char* headers, int32_t headers_length) { | 57 Error MountHttp::Open(const Path& path, int mode, MountNode** out_node) { |
| 64 enum State { | 58 *out_node = NULL; |
| 65 FINDING_KEY, | |
| 66 SKIPPING_WHITESPACE, | |
| 67 FINDING_VALUE, | |
| 68 }; | |
| 69 | 59 |
| 70 StringMap_t result; | |
| 71 std::string key; | |
| 72 std::string value; | |
| 73 | |
| 74 State state = FINDING_KEY; | |
| 75 const char* start = headers; | |
| 76 for (int i = 0; i < headers_length; ++i) { | |
| 77 switch (state) { | |
| 78 case FINDING_KEY: | |
| 79 if (headers[i] == ':') { | |
| 80 // Found key. | |
| 81 key.assign(start, &headers[i] - start); | |
| 82 key = NormalizeHeaderKey(key); | |
| 83 state = SKIPPING_WHITESPACE; | |
| 84 } | |
| 85 break; | |
| 86 | |
| 87 case SKIPPING_WHITESPACE: | |
| 88 if (headers[i] == ' ') { | |
| 89 // Found whitespace, keep going... | |
| 90 break; | |
| 91 } | |
| 92 | |
| 93 // Found a non-whitespace, mark this as the start of the value. | |
| 94 start = &headers[i]; | |
| 95 state = FINDING_VALUE; | |
| 96 // Fallthrough to start processing value without incrementing i. | |
| 97 | |
| 98 case FINDING_VALUE: | |
| 99 if (headers[i] == '\n') { | |
| 100 // Found value. | |
| 101 value.assign(start, &headers[i] - start); | |
| 102 result[key] = value; | |
| 103 start = &headers[i + 1]; | |
| 104 state = FINDING_KEY; | |
| 105 } | |
| 106 break; | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 return result; | |
| 111 } | |
| 112 | |
| 113 bool ParseContentLength(const StringMap_t& headers, size_t* content_length) { | |
| 114 StringMap_t::const_iterator iter = headers.find("Content-Length"); | |
| 115 if (iter == headers.end()) | |
| 116 return false; | |
| 117 | |
| 118 *content_length = strtoul(iter->second.c_str(), NULL, 10); | |
| 119 return true; | |
| 120 } | |
| 121 | |
| 122 bool ParseContentRange(const StringMap_t& headers, size_t* read_start, | |
| 123 size_t* read_end, size_t* entity_length) { | |
| 124 StringMap_t::const_iterator iter = headers.find("Content-Range"); | |
| 125 if (iter == headers.end()) | |
| 126 return false; | |
| 127 | |
| 128 // The key should look like "bytes ##-##/##" or "bytes ##-##/*". The last | |
| 129 // value is the entity length, which can potentially be * (i.e. unknown). | |
| 130 int read_start_int; | |
| 131 int read_end_int; | |
| 132 int entity_length_int; | |
| 133 int result = sscanf(iter->second.c_str(), "bytes %"SCNuS"-%"SCNuS"/%"SCNuS, | |
| 134 &read_start_int, &read_end_int, &entity_length_int); | |
| 135 | |
| 136 // The Content-Range header specifies an inclusive range: e.g. the first ten | |
| 137 // bytes is "bytes 0-9/*". Convert it to a half-open range by incrementing | |
| 138 // read_end. | |
| 139 if (result == 2) { | |
| 140 *read_start = read_start_int; | |
| 141 *read_end = read_end_int + 1; | |
| 142 *entity_length = 0; | |
| 143 return true; | |
| 144 } else if (result == 3) { | |
| 145 *read_start = read_start_int; | |
| 146 *read_end = read_end_int + 1; | |
| 147 *entity_length = entity_length_int; | |
| 148 return true; | |
| 149 } | |
| 150 | |
| 151 return false; | |
| 152 } | |
| 153 | |
| 154 } // namespace | |
| 155 | |
| 156 | |
| 157 class MountNodeHttp : public MountNode { | |
| 158 public: | |
| 159 virtual int FSync(); | |
| 160 virtual int GetDents(size_t offs, struct dirent* pdir, size_t count); | |
| 161 virtual int GetStat(struct stat* stat); | |
| 162 virtual int Read(size_t offs, void* buf, size_t count); | |
| 163 virtual int FTruncate(off_t size); | |
| 164 virtual int Write(size_t offs, const void* buf, size_t count); | |
| 165 virtual size_t GetSize(); | |
| 166 | |
| 167 void SetCachedSize(off_t size); | |
| 168 | |
| 169 protected: | |
| 170 MountNodeHttp(Mount* mount, const std::string& url, bool cache_content); | |
| 171 | |
| 172 private: | |
| 173 bool OpenUrl(const char* method, | |
| 174 StringMap_t* request_headers, | |
| 175 PP_Resource* out_loader, | |
| 176 PP_Resource* out_request, | |
| 177 PP_Resource* out_response, | |
| 178 int32_t* out_statuscode, | |
| 179 StringMap_t* out_response_headers); | |
| 180 | |
| 181 int DownloadToCache(); | |
| 182 int ReadPartialFromCache(size_t offs, void* buf, size_t count); | |
| 183 int DownloadPartial(size_t offs, void* buf, size_t count); | |
| 184 int DownloadToBuffer(PP_Resource loader, void* buf, size_t count); | |
| 185 | |
| 186 std::string url_; | |
| 187 std::vector<char> buffer_; | |
| 188 | |
| 189 bool cache_content_; | |
| 190 bool has_cached_size_; | |
| 191 std::vector<char> cached_data_; | |
| 192 | |
| 193 friend class MountHttp; | |
| 194 }; | |
| 195 | |
| 196 void MountNodeHttp::SetCachedSize(off_t size) { | |
| 197 has_cached_size_ = true; | |
| 198 stat_.st_size = size; | |
| 199 } | |
| 200 | |
| 201 int MountNodeHttp::FSync() { | |
| 202 errno = ENOSYS; | |
| 203 return -1; | |
| 204 } | |
| 205 | |
| 206 int MountNodeHttp::GetDents(size_t offs, struct dirent* pdir, size_t count) { | |
| 207 errno = ENOSYS; | |
| 208 return -1; | |
| 209 } | |
| 210 | |
| 211 int MountNodeHttp::GetStat(struct stat* stat) { | |
| 212 AutoLock lock(&lock_); | |
| 213 | |
| 214 // Assume we need to 'HEAD' if we do not know the size, otherwise, assume | |
| 215 // that the information is constant. We can add a timeout if needed. | |
| 216 MountHttp* mount = static_cast<MountHttp*>(mount_); | |
| 217 if (stat_.st_size == 0 || !mount->cache_stat_) { | |
| 218 StringMap_t headers; | |
| 219 PP_Resource loader; | |
| 220 PP_Resource request; | |
| 221 PP_Resource response; | |
| 222 int32_t statuscode; | |
| 223 StringMap_t response_headers; | |
| 224 if (!OpenUrl("HEAD", &headers, &loader, &request, &response, &statuscode, | |
| 225 &response_headers)) { | |
| 226 // errno is already set by OpenUrl. | |
| 227 return -1; | |
| 228 } | |
| 229 | |
| 230 ScopedResource scoped_loader(mount_->ppapi(), loader); | |
| 231 ScopedResource scoped_request(mount_->ppapi(), request); | |
| 232 ScopedResource scoped_response(mount_->ppapi(), response); | |
| 233 | |
| 234 | |
| 235 size_t entity_length; | |
| 236 if (ParseContentLength(response_headers, &entity_length)) { | |
| 237 SetCachedSize(static_cast<off_t>(entity_length)); | |
| 238 } else if (cache_content_ && !has_cached_size_) { | |
| 239 DownloadToCache(); | |
| 240 } else { | |
| 241 // Don't use SetCachedSize here -- it is actually unknown. | |
| 242 stat_.st_size = 0; | |
| 243 } | |
| 244 | |
| 245 stat_.st_atime = 0; // TODO(binji): Use "Last-Modified". | |
| 246 stat_.st_mtime = 0; | |
| 247 stat_.st_ctime = 0; | |
| 248 } | |
| 249 | |
| 250 // Fill the stat structure if provided | |
| 251 if (stat) { | |
| 252 memcpy(stat, &stat_, sizeof(stat_)); | |
| 253 } | |
| 254 return 0; | |
| 255 } | |
| 256 | |
| 257 int MountNodeHttp::Read(size_t offs, void* buf, size_t count) { | |
| 258 AutoLock lock(&lock_); | |
| 259 if (cache_content_) { | |
| 260 if (cached_data_.empty()) { | |
| 261 if (DownloadToCache() < 0) | |
| 262 return -1; | |
| 263 } | |
| 264 | |
| 265 return ReadPartialFromCache(offs, buf, count); | |
| 266 } | |
| 267 | |
| 268 return DownloadPartial(offs, buf, count); | |
| 269 } | |
| 270 | |
| 271 int MountNodeHttp::FTruncate(off_t size) { | |
| 272 errno = ENOSYS; | |
| 273 return -1; | |
| 274 } | |
| 275 | |
| 276 int MountNodeHttp::Write(size_t offs, const void* buf, size_t count) { | |
| 277 // TODO(binji): support POST? | |
| 278 errno = ENOSYS; | |
| 279 return -1; | |
| 280 } | |
| 281 | |
| 282 size_t MountNodeHttp::GetSize() { | |
| 283 // TODO(binji): This value should be cached properly; i.e. obey the caching | |
| 284 // headers returned by the server. | |
| 285 AutoLock lock(&lock_); | |
| 286 if (!has_cached_size_) { | |
| 287 // Even if DownloadToCache fails, the best result we can return is what | |
| 288 // was written to stat_.st_size. | |
| 289 if (cache_content_) | |
| 290 DownloadToCache(); | |
| 291 } | |
| 292 | |
| 293 return stat_.st_size; | |
| 294 } | |
| 295 | |
| 296 MountNodeHttp::MountNodeHttp(Mount* mount, const std::string& url, | |
| 297 bool cache_content) | |
| 298 : MountNode(mount), | |
| 299 url_(url), | |
| 300 cache_content_(cache_content), | |
| 301 has_cached_size_(false) { | |
| 302 } | |
| 303 | |
| 304 bool MountNodeHttp::OpenUrl(const char* method, | |
| 305 StringMap_t* request_headers, | |
| 306 PP_Resource* out_loader, | |
| 307 PP_Resource* out_request, | |
| 308 PP_Resource* out_response, | |
| 309 int32_t* out_statuscode, | |
| 310 StringMap_t* out_response_headers) { | |
| 311 // Assume lock_ is already held. | |
| 312 | |
| 313 PepperInterface* ppapi = mount_->ppapi(); | |
| 314 | |
| 315 MountHttp* mount_http = static_cast<MountHttp*>(mount_); | |
| 316 ScopedResource request(ppapi, | |
| 317 mount_http->MakeUrlRequestInfo(url_, method, | |
| 318 request_headers)); | |
| 319 if (!request.pp_resource()) { | |
| 320 errno = EINVAL; | |
| 321 return false; | |
| 322 } | |
| 323 | |
| 324 URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); | |
| 325 URLResponseInfoInterface* response_interface = | |
| 326 ppapi->GetURLResponseInfoInterface(); | |
| 327 VarInterface* var_interface = ppapi->GetVarInterface(); | |
| 328 | |
| 329 ScopedResource loader(ppapi, loader_interface->Create(ppapi->GetInstance())); | |
| 330 if (!loader.pp_resource()) { | |
| 331 errno = EINVAL; | |
| 332 return false; | |
| 333 } | |
| 334 | |
| 335 int32_t result = loader_interface->Open( | |
| 336 loader.pp_resource(), request.pp_resource(), PP_BlockUntilComplete()); | |
| 337 if (result != PP_OK) { | |
| 338 errno = PPErrorToErrno(result); | |
| 339 return false; | |
| 340 } | |
| 341 | |
| 342 ScopedResource response( | |
| 343 ppapi, | |
| 344 loader_interface->GetResponseInfo(loader.pp_resource())); | |
| 345 if (!response.pp_resource()) { | |
| 346 errno = EINVAL; | |
| 347 return false; | |
| 348 } | |
| 349 | |
| 350 // Get response statuscode. | |
| 351 PP_Var statuscode = response_interface->GetProperty( | |
| 352 response.pp_resource(), | |
| 353 PP_URLRESPONSEPROPERTY_STATUSCODE); | |
| 354 | |
| 355 if (statuscode.type != PP_VARTYPE_INT32) { | |
| 356 errno = EINVAL; | |
| 357 return false; | |
| 358 } | |
| 359 | |
| 360 *out_statuscode = statuscode.value.as_int; | |
| 361 | |
| 362 // Only accept OK or Partial Content. | |
| 363 if (*out_statuscode != STATUSCODE_OK && | |
| 364 *out_statuscode != STATUSCODE_PARTIAL_CONTENT) { | |
| 365 errno = EINVAL; | |
| 366 return false; | |
| 367 } | |
| 368 | |
| 369 // Get response headers. | |
| 370 PP_Var response_headers_var = response_interface->GetProperty( | |
| 371 response.pp_resource(), | |
| 372 PP_URLRESPONSEPROPERTY_HEADERS); | |
| 373 | |
| 374 uint32_t response_headers_length; | |
| 375 const char* response_headers_str = var_interface->VarToUtf8( | |
| 376 response_headers_var, | |
| 377 &response_headers_length); | |
| 378 | |
| 379 *out_loader = loader.Release(); | |
| 380 *out_request = request.Release(); | |
| 381 *out_response = response.Release(); | |
| 382 *out_response_headers = ParseHeaders(response_headers_str, | |
| 383 response_headers_length); | |
| 384 | |
| 385 return true; | |
| 386 } | |
| 387 | |
| 388 int MountNodeHttp::DownloadToCache() { | |
| 389 StringMap_t headers; | |
| 390 PP_Resource loader; | |
| 391 PP_Resource request; | |
| 392 PP_Resource response; | |
| 393 int32_t statuscode; | |
| 394 StringMap_t response_headers; | |
| 395 if (!OpenUrl("GET", &headers, &loader, &request, &response, &statuscode, | |
| 396 &response_headers)) { | |
| 397 // errno is already set by OpenUrl. | |
| 398 return -1; | |
| 399 } | |
| 400 | |
| 401 PepperInterface* ppapi = mount_->ppapi(); | |
| 402 ScopedResource scoped_loader(ppapi, loader); | |
| 403 ScopedResource scoped_request(ppapi, request); | |
| 404 ScopedResource scoped_response(ppapi, response); | |
| 405 | |
| 406 size_t content_length = 0; | |
| 407 if (ParseContentLength(response_headers, &content_length)) { | |
| 408 cached_data_.resize(content_length); | |
| 409 int real_size = DownloadToBuffer(loader, cached_data_.data(), | |
| 410 content_length); | |
| 411 if (real_size < 0) | |
| 412 return -1; | |
| 413 | |
| 414 SetCachedSize(real_size); | |
| 415 cached_data_.resize(real_size); | |
| 416 return real_size; | |
| 417 } | |
| 418 | |
| 419 // We don't know how big the file is. Read in chunks. | |
| 420 cached_data_.resize(MAX_READ_BUFFER_SIZE); | |
| 421 size_t total_bytes_read = 0; | |
| 422 size_t bytes_to_read = MAX_READ_BUFFER_SIZE; | |
| 423 while (true) { | |
| 424 char* buf = cached_data_.data() + total_bytes_read; | |
| 425 int bytes_read = DownloadToBuffer(loader, buf, bytes_to_read); | |
| 426 if (bytes_read < 0) | |
| 427 return -1; | |
| 428 | |
| 429 total_bytes_read += bytes_read; | |
| 430 | |
| 431 if (bytes_read < bytes_to_read) { | |
| 432 SetCachedSize(total_bytes_read); | |
| 433 cached_data_.resize(total_bytes_read); | |
| 434 return total_bytes_read; | |
| 435 } | |
| 436 | |
| 437 cached_data_.resize(total_bytes_read + bytes_to_read); | |
| 438 } | |
| 439 } | |
| 440 | |
| 441 int MountNodeHttp::ReadPartialFromCache(size_t offs, void* buf, size_t count) { | |
| 442 if (offs > cached_data_.size()) { | |
| 443 errno = EINVAL; | |
| 444 return -1; | |
| 445 } | |
| 446 | |
| 447 count = std::min(count, cached_data_.size() - offs); | |
| 448 memcpy(buf, &cached_data_.data()[offs], count); | |
| 449 return count; | |
| 450 } | |
| 451 | |
| 452 int MountNodeHttp::DownloadPartial(size_t offs, void* buf, size_t count) { | |
| 453 StringMap_t headers; | |
| 454 | |
| 455 char buffer[100]; | |
| 456 // Range request is inclusive: 0-99 returns 100 bytes. | |
| 457 snprintf(&buffer[0], sizeof(buffer), "bytes=%"PRIuS"-%"PRIuS, | |
| 458 offs, offs + count - 1); | |
| 459 headers["Range"] = buffer; | |
| 460 | |
| 461 PP_Resource loader; | |
| 462 PP_Resource request; | |
| 463 PP_Resource response; | |
| 464 int32_t statuscode; | |
| 465 StringMap_t response_headers; | |
| 466 if (!OpenUrl("GET", &headers, &loader, &request, &response, &statuscode, | |
| 467 &response_headers)) { | |
| 468 // errno is already set by OpenUrl. | |
| 469 return -1; | |
| 470 } | |
| 471 | |
| 472 PepperInterface* ppapi = mount_->ppapi(); | |
| 473 ScopedResource scoped_loader(ppapi, loader); | |
| 474 ScopedResource scoped_request(ppapi, request); | |
| 475 ScopedResource scoped_response(ppapi, response); | |
| 476 | |
| 477 size_t read_start = 0; | |
| 478 if (statuscode == STATUSCODE_OK) { | |
| 479 // No partial result, read everything starting from the part we care about. | |
| 480 size_t content_length; | |
| 481 if (ParseContentLength(response_headers, &content_length)) { | |
| 482 if (offs >= content_length) { | |
| 483 errno = EINVAL; | |
| 484 return 0; | |
| 485 } | |
| 486 | |
| 487 // Clamp count, if trying to read past the end of the file. | |
| 488 if (offs + count > content_length) { | |
| 489 count = content_length - offs; | |
| 490 } | |
| 491 } | |
| 492 } else if (statuscode == STATUSCODE_PARTIAL_CONTENT) { | |
| 493 // Determine from the headers where we are reading. | |
| 494 size_t read_end; | |
| 495 size_t entity_length; | |
| 496 if (ParseContentRange(response_headers, &read_start, &read_end, | |
| 497 &entity_length)) { | |
| 498 if (read_start > offs || read_start > read_end) { | |
| 499 // If this error occurs, the server is returning bogus values. | |
| 500 errno = EINVAL; | |
| 501 return -1; | |
| 502 } | |
| 503 | |
| 504 // Clamp count, if trying to read past the end of the file. | |
| 505 count = std::min(read_end - read_start, count); | |
| 506 } else { | |
| 507 // Partial Content without Content-Range. Assume that the server gave us | |
| 508 // exactly what we asked for. This can happen even when the server | |
| 509 // returns 200 -- the cache may return 206 in this case, but not modify | |
| 510 // the headers. | |
| 511 read_start = offs; | |
| 512 } | |
| 513 } | |
| 514 | |
| 515 if (read_start < offs) { | |
| 516 // We aren't yet at the location where we want to start reading. Read into | |
| 517 // our dummy buffer until then. | |
| 518 size_t bytes_to_read = offs - read_start; | |
| 519 if (buffer_.size() < bytes_to_read) | |
| 520 buffer_.resize(std::min(bytes_to_read, MAX_READ_BUFFER_SIZE)); | |
| 521 | |
| 522 while (bytes_to_read > 0) { | |
| 523 int32_t bytes_read = DownloadToBuffer(loader, buffer_.data(), | |
| 524 buffer_.size()); | |
| 525 if (bytes_read < 0) | |
| 526 return -1; | |
| 527 | |
| 528 bytes_to_read -= bytes_read; | |
| 529 } | |
| 530 } | |
| 531 | |
| 532 return DownloadToBuffer(loader, buf, count); | |
| 533 } | |
| 534 | |
| 535 int MountNodeHttp::DownloadToBuffer(PP_Resource loader, void* buf, | |
| 536 size_t count) { | |
| 537 PepperInterface* ppapi = mount_->ppapi(); | |
| 538 URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); | |
| 539 | |
| 540 char* out_buffer = static_cast<char*>(buf); | |
| 541 size_t bytes_to_read = count; | |
| 542 while (bytes_to_read > 0) { | |
| 543 int32_t bytes_read = loader_interface->ReadResponseBody( | |
| 544 loader, out_buffer, bytes_to_read, PP_BlockUntilComplete()); | |
| 545 | |
| 546 if (bytes_read == 0) { | |
| 547 // This is not an error -- it may just be that we were trying to read | |
| 548 // more data than exists. | |
| 549 return count - bytes_to_read; | |
| 550 } | |
| 551 | |
| 552 if (bytes_read < 0) { | |
| 553 errno = PPErrorToErrno(bytes_read); | |
| 554 return -1; | |
| 555 } | |
| 556 | |
| 557 assert(bytes_read <= bytes_to_read); | |
| 558 bytes_to_read -= bytes_read; | |
| 559 out_buffer += bytes_read; | |
| 560 } | |
| 561 | |
| 562 return count; | |
| 563 } | |
| 564 | |
| 565 MountNode *MountHttp::Open(const Path& path, int mode) { | |
| 566 assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/'); | 60 assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/'); |
| 567 | 61 |
| 568 NodeMap_t::iterator iter = node_cache_.find(path.Join()); | 62 NodeMap_t::iterator iter = node_cache_.find(path.Join()); |
| 569 if (iter != node_cache_.end()) { | 63 if (iter != node_cache_.end()) { |
| 570 return iter->second; | 64 *out_node = iter->second; |
| 65 return 0; | |
| 571 } | 66 } |
| 572 | 67 |
| 573 // If we can't find the node in the cache, create it | 68 // If we can't find the node in the cache, create it |
| 574 std::string url = url_root_ + (path.IsAbsolute() ? | 69 std::string url = url_root_ + (path.IsAbsolute() ? path.Range(1, path.Size()) |
| 575 path.Range(1, path.Size()) : | 70 : path.Join()); |
| 576 path.Join()); | |
| 577 | 71 |
| 578 MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); | 72 MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); |
| 579 if (!node->Init(mode) || (0 != node->GetStat(NULL))) { | 73 Error error = node->Init(mode); |
| 74 if (error) { | |
| 580 node->Release(); | 75 node->Release(); |
| 581 return NULL; | 76 return error; |
| 582 } | 77 } |
| 583 | 78 |
| 584 MountNodeDir* parent = FindOrCreateDir(path.Parent()); | 79 error = node->GetStat(NULL); |
| 80 if (error) { | |
| 81 node->Release(); | |
| 82 return error; | |
| 83 } | |
| 84 | |
| 85 MountNodeDir* parent; | |
| 86 error = FindOrCreateDir(path.Parent(), &parent); | |
| 87 if (error) { | |
| 88 node->Release(); | |
| 89 return error; | |
| 90 } | |
| 91 | |
| 92 error = parent->AddChild(path.Basename(), node); | |
| 93 if (error) { | |
| 94 node->Release(); | |
| 95 return error; | |
| 96 } | |
| 97 | |
| 585 node_cache_[path.Join()] = node; | 98 node_cache_[path.Join()] = node; |
| 586 parent->AddChild(path.Basename(), node); | 99 |
| 587 return node; | 100 *out_node = node; |
| 101 return 0; | |
| 588 } | 102 } |
| 589 | 103 |
| 590 int MountHttp::Unlink(const Path& path) { | 104 Error MountHttp::Unlink(const Path& path) { return ENOSYS; } |
| 591 errno = ENOSYS; | |
| 592 return -1; | |
| 593 } | |
| 594 | 105 |
| 595 int MountHttp::Mkdir(const Path& path, int permissions) { | 106 Error MountHttp::Mkdir(const Path& path, int permissions) { return ENOSYS; } |
| 596 errno = ENOSYS; | |
| 597 return -1; | |
| 598 } | |
| 599 | 107 |
| 600 int MountHttp::Rmdir(const Path& path) { | 108 Error MountHttp::Rmdir(const Path& path) { return ENOSYS; } |
| 601 errno = ENOSYS; | |
| 602 return -1; | |
| 603 } | |
| 604 | 109 |
| 605 int MountHttp::Remove(const Path& path) { | 110 Error MountHttp::Remove(const Path& path) { return ENOSYS; } |
| 606 errno = ENOSYS; | |
| 607 return -1; | |
| 608 } | |
| 609 | 111 |
| 610 PP_Resource MountHttp::MakeUrlRequestInfo( | 112 PP_Resource MountHttp::MakeUrlRequestInfo(const std::string& url, |
| 611 const std::string& url, | 113 const char* method, |
| 612 const char* method, | 114 StringMap_t* additional_headers) { |
| 613 StringMap_t* additional_headers) { | |
| 614 URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface(); | 115 URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface(); |
| 615 VarInterface* var_interface = ppapi_->GetVarInterface(); | 116 VarInterface* var_interface = ppapi_->GetVarInterface(); |
| 616 | 117 |
| 617 PP_Resource request_info = interface->Create(ppapi_->GetInstance()); | 118 PP_Resource request_info = interface->Create(ppapi_->GetInstance()); |
| 618 if (!request_info) | 119 if (!request_info) |
| 619 return 0; | 120 return 0; |
| 620 | 121 |
| 621 interface->SetProperty( | 122 interface->SetProperty(request_info, |
| 622 request_info, PP_URLREQUESTPROPERTY_URL, | 123 PP_URLREQUESTPROPERTY_URL, |
| 623 var_interface->VarFromUtf8(url.c_str(), url.length())); | 124 var_interface->VarFromUtf8(url.c_str(), url.length())); |
| 624 interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_METHOD, | 125 interface->SetProperty(request_info, |
| 126 PP_URLREQUESTPROPERTY_METHOD, | |
| 625 var_interface->VarFromUtf8(method, strlen(method))); | 127 var_interface->VarFromUtf8(method, strlen(method))); |
| 626 interface->SetProperty(request_info, | 128 interface->SetProperty(request_info, |
| 627 PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS, | 129 PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS, |
| 628 PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE)); | 130 PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE)); |
| 629 interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS, | 131 interface->SetProperty(request_info, |
| 132 PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS, | |
| 630 PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE)); | 133 PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE)); |
| 631 | 134 |
| 632 // Merge the mount headers with the request headers. If the field is already | 135 // Merge the mount headers with the request headers. If the field is already |
| 633 // set it |additional_headers|, don't use the one from headers_. | 136 // set it |additional_headers|, don't use the one from headers_. |
| 634 for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end(); | 137 for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end(); |
| 635 ++iter) { | 138 ++iter) { |
| 636 const std::string& key = NormalizeHeaderKey(iter->first); | 139 const std::string& key = NormalizeHeaderKey(iter->first); |
| 637 if (additional_headers->find(key) == additional_headers->end()) { | 140 if (additional_headers->find(key) == additional_headers->end()) { |
| 638 additional_headers->insert(std::make_pair(key, iter->second)); | 141 additional_headers->insert(std::make_pair(key, iter->second)); |
| 639 } | 142 } |
| 640 } | 143 } |
| 641 | 144 |
| 642 // Join the headers into one string. | 145 // Join the headers into one string. |
| 643 std::string headers; | 146 std::string headers; |
| 644 for (StringMap_t::iterator iter = additional_headers->begin(); | 147 for (StringMap_t::iterator iter = additional_headers->begin(); |
| 645 iter != additional_headers->end(); ++iter) { | 148 iter != additional_headers->end(); |
| 149 ++iter) { | |
| 646 headers += iter->first + ": " + iter->second + '\n'; | 150 headers += iter->first + ": " + iter->second + '\n'; |
| 647 } | 151 } |
| 648 | 152 |
| 649 interface->SetProperty( | 153 interface->SetProperty( |
| 650 request_info, PP_URLREQUESTPROPERTY_HEADERS, | 154 request_info, |
| 155 PP_URLREQUESTPROPERTY_HEADERS, | |
| 651 var_interface->VarFromUtf8(headers.c_str(), headers.length())); | 156 var_interface->VarFromUtf8(headers.c_str(), headers.length())); |
| 652 | 157 |
| 653 return request_info; | 158 return request_info; |
| 654 } | 159 } |
| 655 | 160 |
| 656 MountHttp::MountHttp() | 161 MountHttp::MountHttp() |
| 657 : allow_cors_(false), | 162 : allow_cors_(false), |
| 658 allow_credentials_(false), | 163 allow_credentials_(false), |
| 659 cache_stat_(true), | 164 cache_stat_(true), |
| 660 cache_content_(true) { | 165 cache_content_(true) {} |
| 661 } | |
| 662 | 166 |
| 663 bool MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { | 167 Error MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { |
| 664 if (!Mount::Init(dev, args, ppapi)) | 168 Error error = Mount::Init(dev, args, ppapi); |
| 665 return false; | 169 if (error) |
|
noelallen1
2013/06/07 21:48:43
style: Single line or {}
binji
2013/06/07 23:23:11
After discussion, we'll leave without {}
| |
| 170 return error; | |
| 666 | 171 |
| 667 // Parse mount args. | 172 // Parse mount args. |
| 668 for (StringMap_t::iterator iter = args.begin(); iter != args.end(); ++iter) { | 173 for (StringMap_t::iterator iter = args.begin(); iter != args.end(); ++iter) { |
| 669 if (iter->first == "SOURCE") { | 174 if (iter->first == "SOURCE") { |
| 670 url_root_ = iter->second; | 175 url_root_ = iter->second; |
| 671 | 176 |
| 672 // Make sure url_root_ ends with a slash. | 177 // Make sure url_root_ ends with a slash. |
| 673 if (!url_root_.empty() && url_root_[url_root_.length() - 1] != '/') { | 178 if (!url_root_.empty() && url_root_[url_root_.length() - 1] != '/') { |
| 674 url_root_ += '/'; | 179 url_root_ += '/'; |
| 675 } | 180 } |
| 676 } else if (iter->first == "manifest") { | 181 } else if (iter->first == "manifest") { |
| 677 char *text = LoadManifest(iter->second); | 182 char* text; |
| 678 if (text != NULL) { | 183 error = LoadManifest(iter->second, &text); |
| 679 ParseManifest(text); | 184 if (error) |
| 185 return error; | |
| 186 | |
| 187 error = ParseManifest(text); | |
| 188 if (error) { | |
| 680 delete[] text; | 189 delete[] text; |
| 190 return error; | |
| 681 } | 191 } |
| 192 | |
| 193 delete[] text; | |
| 682 } else if (iter->first == "allow_cross_origin_requests") { | 194 } else if (iter->first == "allow_cross_origin_requests") { |
| 683 allow_cors_ = iter->second == "true"; | 195 allow_cors_ = iter->second == "true"; |
| 684 } else if (iter->first == "allow_credentials") { | 196 } else if (iter->first == "allow_credentials") { |
| 685 allow_credentials_ = iter->second == "true"; | 197 allow_credentials_ = iter->second == "true"; |
| 686 } else if (iter->first == "cache_stat") { | 198 } else if (iter->first == "cache_stat") { |
| 687 cache_stat_ = iter->second == "true"; | 199 cache_stat_ = iter->second == "true"; |
| 688 } else if (iter->first == "cache_content") { | 200 } else if (iter->first == "cache_content") { |
| 689 cache_content_ = iter->second == "true"; | 201 cache_content_ = iter->second == "true"; |
| 690 } else { | 202 } else { |
| 691 // Assume it is a header to pass to an HTTP request. | 203 // Assume it is a header to pass to an HTTP request. |
| 692 headers_[NormalizeHeaderKey(iter->first)] = iter->second; | 204 headers_[NormalizeHeaderKey(iter->first)] = iter->second; |
| 693 } | 205 } |
| 694 } | 206 } |
| 695 | 207 |
| 696 return true; | 208 return 0; |
| 697 } | 209 } |
| 698 | 210 |
| 699 void MountHttp::Destroy() { | 211 void MountHttp::Destroy() {} |
| 700 } | |
| 701 | 212 |
| 702 MountNodeDir* MountHttp::FindOrCreateDir(const Path& path) { | 213 Error MountHttp::FindOrCreateDir(const Path& path, MountNodeDir** out_node) { |
| 214 *out_node = NULL; | |
| 215 | |
| 703 std::string strpath = path.Join(); | 216 std::string strpath = path.Join(); |
| 704 NodeMap_t::iterator iter = node_cache_.find(strpath); | 217 NodeMap_t::iterator iter = node_cache_.find(strpath); |
| 705 if (iter != node_cache_.end()) { | 218 if (iter != node_cache_.end()) { |
| 706 return static_cast<MountNodeDir*>(iter->second); | 219 *out_node = static_cast<MountNodeDir*>(iter->second); |
| 220 return 0; | |
| 707 } | 221 } |
| 708 | 222 |
| 709 // If the node does not exist, create it, and add it to the node cache | 223 // If the node does not exist, create it. |
| 710 MountNodeDir* node = new MountNodeDir(this); | 224 MountNodeDir* node = new MountNodeDir(this); |
| 711 node->Init(S_IREAD); | 225 Error error = node->Init(S_IREAD); |
| 712 node_cache_[strpath] = node; | 226 if (error) { |
| 227 node->Release(); | |
| 228 return error; | |
| 229 } | |
| 713 | 230 |
| 714 // If not the root node, find the parent node and add it to the parent | 231 // If not the root node, find the parent node and add it to the parent |
| 715 if (!path.Top()) { | 232 if (!path.Top()) { |
| 716 MountNodeDir* parent = FindOrCreateDir(path.Parent()); | 233 MountNodeDir* parent; |
| 717 parent->AddChild(path.Basename(), node); | 234 error = FindOrCreateDir(path.Parent(), &parent); |
| 235 if (error) { | |
| 236 node->Release(); | |
| 237 return error; | |
| 238 } | |
| 239 | |
| 240 error = parent->AddChild(path.Basename(), node); | |
| 241 if (error) { | |
| 242 node->Release(); | |
| 243 return error; | |
| 244 } | |
| 718 } | 245 } |
| 719 | 246 |
| 720 return node; | 247 // Add it to the node cache. |
| 248 node_cache_[strpath] = node; | |
| 249 | |
| 250 *out_node = node; | |
| 251 return 0; | |
| 721 } | 252 } |
| 722 | 253 |
| 723 bool MountHttp::ParseManifest(char *text) { | 254 Error MountHttp::ParseManifest(char* text) { |
| 724 StringList_t lines; | 255 StringList_t lines; |
| 725 SplitString(text, "\n", &lines); | 256 SplitString(text, "\n", &lines); |
| 726 | 257 |
| 727 for (size_t i = 0; i < lines.size(); i++) { | 258 for (size_t i = 0; i < lines.size(); i++) { |
| 728 StringList_t words; | 259 StringList_t words; |
| 729 SplitString(lines[i], " ", &words); | 260 SplitString(lines[i], " ", &words); |
| 730 | 261 |
| 731 if (words.size() == 3) { | 262 if (words.size() == 3) { |
| 732 char* modestr = words[0]; | 263 char* modestr = words[0]; |
| 733 char* lenstr = words[1]; | 264 char* lenstr = words[1]; |
| 734 char* name = words[2]; | 265 char* name = words[2]; |
| 735 | 266 |
| 736 assert(modestr && strlen(modestr) == 4); | 267 assert(modestr && strlen(modestr) == 4); |
| 737 assert(name && name[0] == '/'); | 268 assert(name && name[0] == '/'); |
| 738 assert(lenstr); | 269 assert(lenstr); |
| 739 | 270 |
| 740 // Only support regular and streams for now | 271 // Only support regular and streams for now |
| 741 // Ignore EXEC bit | 272 // Ignore EXEC bit |
| 742 int mode = S_IFREG; | 273 int mode = S_IFREG; |
| 743 switch (modestr[0]) { | 274 switch (modestr[0]) { |
| 744 case '-': mode = S_IFREG; break; | 275 case '-': |
| 745 case 'c': mode = S_IFCHR; break; | 276 mode = S_IFREG; |
| 277 break; | |
| 278 case 'c': | |
| 279 mode = S_IFCHR; | |
| 280 break; | |
| 746 default: | 281 default: |
| 747 fprintf(stderr, "Unable to parse type %s for %s.\n", modestr, name); | 282 fprintf(stderr, "Unable to parse type %s for %s.\n", modestr, name); |
| 748 return false; | 283 return EINVAL; |
| 749 } | 284 } |
| 750 | 285 |
| 751 switch (modestr[1]) { | 286 switch (modestr[1]) { |
| 752 case '-': break; | 287 case '-': |
| 753 case 'r': mode |= S_IREAD; break; | 288 break; |
| 289 case 'r': | |
| 290 mode |= S_IREAD; | |
| 291 break; | |
| 754 default: | 292 default: |
| 755 fprintf(stderr, "Unable to parse read %s for %s.\n", modestr, name); | 293 fprintf(stderr, "Unable to parse read %s for %s.\n", modestr, name); |
| 756 return false; | 294 return EINVAL; |
| 757 } | 295 } |
| 758 | 296 |
| 759 switch (modestr[2]) { | 297 switch (modestr[2]) { |
| 760 case '-': break; | 298 case '-': |
| 761 case 'w': mode |= S_IWRITE; break; | 299 break; |
| 300 case 'w': | |
| 301 mode |= S_IWRITE; | |
| 302 break; | |
| 762 default: | 303 default: |
| 763 fprintf(stderr, "Unable to parse write %s for %s.\n", modestr, name); | 304 fprintf(stderr, "Unable to parse write %s for %s.\n", modestr, name); |
| 764 return false; | 305 return EINVAL; |
| 765 } | 306 } |
| 766 | 307 |
| 767 Path path(name); | 308 Path path(name); |
| 768 std::string url = url_root_ + (path.IsAbsolute() ? | 309 std::string url = |
| 769 path.Range(1, path.Size()) : | 310 url_root_ + |
| 770 path.Join()); | 311 (path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join()); |
| 771 | 312 |
| 772 MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); | 313 MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); |
| 773 node->Init(mode); | 314 Error error = node->Init(mode); |
| 315 if (error) { | |
| 316 node->Release(); | |
| 317 return error; | |
| 318 } | |
| 319 | |
| 774 node->SetCachedSize(atoi(lenstr)); | 320 node->SetCachedSize(atoi(lenstr)); |
| 775 | 321 |
| 776 MountNodeDir* dir_node = FindOrCreateDir(path.Parent()); | 322 MountNodeDir* dir_node; |
| 777 dir_node->AddChild(path.Basename(), node); | 323 error = FindOrCreateDir(path.Parent(), &dir_node); |
| 324 if (error) { | |
| 325 node->Release(); | |
| 326 return error; | |
| 327 } | |
| 328 | |
| 329 error = dir_node->AddChild(path.Basename(), node); | |
| 330 if (error) { | |
| 331 node->Release(); | |
| 332 return error; | |
| 333 } | |
| 778 | 334 |
| 779 std::string pname = path.Join(); | 335 std::string pname = path.Join(); |
| 780 node_cache_[pname] = node; | 336 node_cache_[pname] = node; |
| 781 } | 337 } |
| 782 } | 338 } |
| 783 | 339 |
| 784 return true; | 340 return 0; |
| 785 } | 341 } |
| 786 | 342 |
| 787 char *MountHttp::LoadManifest(const std::string& manifest_name) { | 343 Error MountHttp::LoadManifest(const std::string& manifest_name, |
| 344 char** out_manifest) { | |
| 788 Path manifest_path(manifest_name); | 345 Path manifest_path(manifest_name); |
| 789 MountNode* manifest_node = Open(manifest_path, O_RDONLY); | 346 MountNode* manifest_node = NULL; |
| 347 *out_manifest = NULL; | |
| 790 | 348 |
| 791 if (manifest_node) { | 349 int error = Open(manifest_path, O_RDONLY, &manifest_node); |
| 792 char *text = new char[manifest_node->GetSize() + 1]; | 350 if (error) |
| 793 off_t len = manifest_node->Read(0, text, manifest_node->GetSize()); | 351 return error; |
| 352 | |
| 353 size_t size; | |
| 354 error = manifest_node->GetSize(&size); | |
| 355 if (error) { | |
| 794 manifest_node->Release(); | 356 manifest_node->Release(); |
| 795 | 357 return error; |
| 796 text[len] = 0; | |
| 797 return text; | |
| 798 } | 358 } |
| 799 | 359 |
| 800 fprintf(stderr, "Could not open manifest: %s\n", manifest_name.c_str()); | 360 char* text = new char[size + 1]; |
| 801 return NULL; | 361 int len; |
| 362 error = manifest_node->Read(0, text, size, &len); | |
| 363 if (error) { | |
| 364 manifest_node->Release(); | |
| 365 return error; | |
| 366 } | |
| 367 | |
| 368 manifest_node->Release(); | |
| 369 text[len] = 0; | |
| 370 | |
| 371 *out_manifest = text; | |
| 372 return 0; | |
| 802 } | 373 } |
| OLD | NEW |