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