| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <stdio.h> | |
| 6 #include <stdlib.h> | |
| 7 #include "ppapi/c/pp_errors.h" | |
| 8 #include "ppapi/c/ppb_instance.h" | |
| 9 #include "ppapi/cpp/module.h" | |
| 10 #include "ppapi/cpp/var.h" | |
| 11 | |
| 12 #include "geturl_handler.h" | |
| 13 | |
| 14 #ifdef WIN32 | |
| 15 #undef min | |
| 16 #undef max | |
| 17 #undef PostMessage | |
| 18 | |
| 19 // Allow 'this' in initializer list | |
| 20 #pragma warning(disable : 4355) | |
| 21 #endif | |
| 22 | |
| 23 GetURLHandler* GetURLHandler::Create(pp::Instance* instance, | |
| 24 const std::string& url) { | |
| 25 return new GetURLHandler(instance, url); | |
| 26 } | |
| 27 | |
| 28 GetURLHandler::GetURLHandler(pp::Instance* instance, const std::string& url) | |
| 29 : instance_(instance), | |
| 30 url_(url), | |
| 31 url_request_(instance), | |
| 32 url_loader_(instance), | |
| 33 buffer_(new char[READ_BUFFER_SIZE]), | |
| 34 cc_factory_(this) { | |
| 35 url_request_.SetURL(url); | |
| 36 url_request_.SetMethod("GET"); | |
| 37 url_request_.SetRecordDownloadProgress(true); | |
| 38 } | |
| 39 | |
| 40 GetURLHandler::~GetURLHandler() { | |
| 41 delete[] buffer_; | |
| 42 buffer_ = NULL; | |
| 43 } | |
| 44 | |
| 45 void GetURLHandler::Start() { | |
| 46 pp::CompletionCallback cc = cc_factory_.NewCallback(&GetURLHandler::OnOpen); | |
| 47 url_loader_.Open(url_request_, cc); | |
| 48 } | |
| 49 | |
| 50 void GetURLHandler::OnOpen(int32_t result) { | |
| 51 if (result != PP_OK) { | |
| 52 ReportResultAndDie(url_, "pp::URLLoader::Open() failed", false); | |
| 53 return; | |
| 54 } | |
| 55 // Here you would process the headers. A real program would want to at least | |
| 56 // check the HTTP code and potentially cancel the request. | |
| 57 // pp::URLResponseInfo response = loader_.GetResponseInfo(); | |
| 58 | |
| 59 // Try to figure out how many bytes of data are going to be downloaded in | |
| 60 // order to allocate memory for the response body in advance (this will | |
| 61 // reduce heap traffic and also the amount of memory allocated). | |
| 62 // It is not a problem if this fails, it just means that the | |
| 63 // url_response_body_.insert() call in GetURLHandler::AppendDataBytes() | |
| 64 // will allocate the memory later on. | |
| 65 int64_t bytes_received = 0; | |
| 66 int64_t total_bytes_to_be_received = 0; | |
| 67 if (url_loader_.GetDownloadProgress(&bytes_received, | |
| 68 &total_bytes_to_be_received)) { | |
| 69 if (total_bytes_to_be_received > 0) { | |
| 70 url_response_body_.reserve(total_bytes_to_be_received); | |
| 71 } | |
| 72 } | |
| 73 // We will not use the download progress anymore, so just disable it. | |
| 74 url_request_.SetRecordDownloadProgress(false); | |
| 75 | |
| 76 // Start streaming. | |
| 77 ReadBody(); | |
| 78 } | |
| 79 | |
| 80 void GetURLHandler::AppendDataBytes(const char* buffer, int32_t num_bytes) { | |
| 81 if (num_bytes <= 0) | |
| 82 return; | |
| 83 // Make sure we don't get a buffer overrun. | |
| 84 num_bytes = std::min(READ_BUFFER_SIZE, num_bytes); | |
| 85 // Note that we do *not* try to minimally increase the amount of allocated | |
| 86 // memory here by calling url_response_body_.reserve(). Doing so causes a | |
| 87 // lot of string reallocations that kills performance for large files. | |
| 88 url_response_body_.insert( | |
| 89 url_response_body_.end(), buffer, buffer + num_bytes); | |
| 90 } | |
| 91 | |
| 92 void GetURLHandler::OnRead(int32_t result) { | |
| 93 if (result == PP_OK) { | |
| 94 // Streaming the file is complete, delete the read buffer since it is | |
| 95 // no longer needed. | |
| 96 delete[] buffer_; | |
| 97 buffer_ = NULL; | |
| 98 ReportResultAndDie(url_, url_response_body_, true); | |
| 99 } else if (result > 0) { | |
| 100 // The URLLoader just filled "result" number of bytes into our buffer. | |
| 101 // Save them and perform another read. | |
| 102 AppendDataBytes(buffer_, result); | |
| 103 ReadBody(); | |
| 104 } else { | |
| 105 // A read error occurred. | |
| 106 ReportResultAndDie( | |
| 107 url_, "pp::URLLoader::ReadResponseBody() result<0", false); | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 void GetURLHandler::ReadBody() { | |
| 112 // Note that you specifically want an "optional" callback here. This will | |
| 113 // allow ReadBody() to return synchronously, ignoring your completion | |
| 114 // callback, if data is available. For fast connections and large files, | |
| 115 // reading as fast as we can will make a large performance difference | |
| 116 // However, in the case of a synchronous return, we need to be sure to run | |
| 117 // the callback we created since the loader won't do anything with it. | |
| 118 pp::CompletionCallback cc = | |
| 119 cc_factory_.NewOptionalCallback(&GetURLHandler::OnRead); | |
| 120 int32_t result = PP_OK; | |
| 121 do { | |
| 122 result = url_loader_.ReadResponseBody(buffer_, READ_BUFFER_SIZE, cc); | |
| 123 // Handle streaming data directly. Note that we *don't* want to call | |
| 124 // OnRead here, since in the case of result > 0 it will schedule | |
| 125 // another call to this function. If the network is very fast, we could | |
| 126 // end up with a deeply recursive stack. | |
| 127 if (result > 0) { | |
| 128 AppendDataBytes(buffer_, result); | |
| 129 } | |
| 130 } while (result > 0); | |
| 131 | |
| 132 if (result != PP_OK_COMPLETIONPENDING) { | |
| 133 // Either we reached the end of the stream (result == PP_OK) or there was | |
| 134 // an error. We want OnRead to get called no matter what to handle | |
| 135 // that case, whether the error is synchronous or asynchronous. If the | |
| 136 // result code *is* COMPLETIONPENDING, our callback will be called | |
| 137 // asynchronously. | |
| 138 cc.Run(result); | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 void GetURLHandler::ReportResultAndDie(const std::string& fname, | |
| 143 const std::string& text, | |
| 144 bool success) { | |
| 145 ReportResult(fname, text, success); | |
| 146 delete this; | |
| 147 } | |
| 148 | |
| 149 void GetURLHandler::ReportResult(const std::string& fname, | |
| 150 const std::string& text, | |
| 151 bool success) { | |
| 152 if (success) | |
| 153 printf("GetURLHandler::ReportResult(Ok).\n"); | |
| 154 else | |
| 155 printf("GetURLHandler::ReportResult(Err). %s\n", text.c_str()); | |
| 156 fflush(stdout); | |
| 157 if (instance_) { | |
| 158 pp::Var var_result(fname + "\n" + text); | |
| 159 instance_->PostMessage(var_result); | |
| 160 } | |
| 161 } | |
| OLD | NEW |