url-loading.rst revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1.. _devguide-coding-url-loading: 2 3########### 4URL Loading 5########### 6 7.. contents:: 8 :local: 9 :backlinks: none 10 :depth: 2 11 12Introduction 13============ 14 15This section describes how to use the `URLLoader API 16</native-client/pepper_stable/cpp/classpp_1_1_u_r_l_loader>`_ to load resources 17such as images and sound files from a server into your application. 18 19The example discussed in this section is included in the SDK in the directory 20``examples/api/url_loader``. 21 22Reference information 23===================== 24 25For reference information related to loading data from URLs, see the 26following documentation: 27 28* `url_loader.h </native-client/pepper_stable/cpp/url__loader_8h>`_ - Contains 29 ``URLLoader`` class for loading data from URLs 30* `url_request_info.h 31 </native-client/pepper_stable/cpp/url__request__info_8h>`_ - Contains 32 ``URLRequest`` class for creating and manipulating URL requests 33* `url_response_info.h 34 </native-client/pepper_stable/cpp/url__response__info_8h>`_ - Contains 35 ``URLResponse`` class for examaning URL responses 36 37Background 38========== 39 40When a user launches your Native Client web application, Chrome downloads and 41caches your application's HTML file, manifest file (.nmf), and Native Client 42module (.pexe or .nexe). If your application needs additional assets, such as 43images and sound files, it must explicitly load those assets. You can use the 44Pepper APIs described in this section to load assets from a URL into your 45application. 46 47After you've loaded assets into your application, Chrome will cache those 48assets. To avoid being at the whim of the Chrome cache, however, you may want 49to use the `Pepper FileIO API 50</native-client/pepper_stable/cpp/classpp_1_1_file_i_o>`_ to write those assets 51to a persistent, sandboxed location on the user's file system. 52 53The ``url_loader`` example 54========================== 55 56The SDK includes an example called ``url_loader`` demonstrating downloading 57files from a server. This example has these primary files: 58 59* ``index.html`` - The HTML code that launches the Native Client module. 60* ``example.js`` - The JavaScript file for index.html. It has code that sends 61 a PostMessage request to the Native Client module when the "Get URL" button 62 is clicked. 63* ``url_loader_success.html`` - An HTML file on the server whose contents are 64 being retrieved using the ``URLLoader`` API. 65* ``url_loader.cc`` - The code that sets up and provides and entry point into 66 the Native client module. 67* ``url_loader_handler.cc`` - The code that retrieves the contents of the 68 url_loader_success.html file and returns the results (this is where the 69 bulk of the work is done). 70 71The remainder of this document covers the code in the ``url_loader.cc`` and 72``url_loader_handler.cc`` files. 73 74URL loading overview 75-------------------- 76 77Like many Pepper APIs, the ``URLLoader`` API includes a set of methods that 78execute asynchronously and that invoke callback functions in your Native Client 79module. The high-level flow for the ``url_loader`` example is described below. 80Note that methods in the namespace ``pp::URLLoader`` are part of the Pepper 81``URLLoader`` API, while the rest of the functions are part of the code in the 82Native Client module (specifically in the file ``url_loader_handler.cc``). The 83following image shows the flow of the ``url_loader_handler`` code: 84 85.. image:: /images/pepper-urlloader-api.png 86 87Following are the high-level steps involved in URL loading. 88 89#. The Native Client module calls ``pp::URLLoader::Open`` to begin opening the 90 URL. 91#. When ``Open`` completes, it invokes a callback function in the Native Client 92 module (in this case, ``OnOpen``). 93#. The Native Client module calls the Pepper function 94 ``URLLoader::ReadResponseBody`` to begin reading the response body with the 95 data. ``ReadResponseBody`` is passed an optional callback function in the 96 Native Client module (in this case, On ``Read``). The callback function is 97 an optional callback because ``ReadResponseBody`` may read data and return 98 synchronously if data is available (this improves performance for large 99 files and fast connections). 100 101The remainder of this document demonstrates how the previous steps are 102implemented in the ``url_loader`` example. 103 104``url_loader`` deep dive 105======================== 106 107Setting up the request 108---------------------- 109 110``HandleMessage`` in ``url_loader.cc`` creates a ``URLLoaderHandler`` instance 111and passes it the URL of the asset to be retrieved. Then ``HandleMessage`` 112calls ``Start`` to start retrieving the asset from the server: 113 114.. naclcode:: 115 116 void URLLoaderInstance::HandleMessage(const pp::Var& var_message) { 117 if (!var_message.is_string()) { 118 return; 119 } 120 std::string message = var_message.AsString(); 121 if (message.find(kLoadUrlMethodId) == 0) { 122 // The argument to getUrl is everything after the first ':'. 123 size_t sep_pos = message.find_first_of(kMessageArgumentSeparator); 124 if (sep_pos != std::string::npos) { 125 std::string url = message.substr(sep_pos + 1); 126 printf("URLLoaderInstance::HandleMessage('%s', '%s')\n", 127 message.c_str(), 128 url.c_str()); 129 fflush(stdout); 130 URLLoaderHandler* handler = URLLoaderHandler::Create(this, url); 131 if (handler != NULL) { 132 // Starts asynchronous download. When download is finished or when an 133 // error occurs, |handler| posts the results back to the browser 134 // vis PostMessage and self-destroys. 135 handler->Start(); 136 } 137 } 138 } 139 } 140 141Notice that the constructor for ``URLLoaderHandler`` in 142``url_loader_handler.cc`` sets up the parameters of the URL request (using 143``SetURL,`` ``SetMethod``, and ``SetRecordDownloadProgress``): 144 145 146.. naclcode:: 147 148 URLLoaderHandler::URLLoaderHandler(pp::Instance* instance, 149 const std::string& url) 150 : instance_(instance), 151 url_(url), 152 url_request_(instance), 153 url_loader_(instance), 154 buffer_(new char[READ_BUFFER_SIZE]), 155 cc_factory_(this) { 156 url_request_.SetURL(url); 157 url_request_.SetMethod("GET"); 158 url_request_.SetRecordDownloadProgress(true); 159 } 160 161Downloading the data 162-------------------- 163 164``Start`` in ``url_loader_handler.cc`` creates a callback (``cc``) using a 165``CompletionCallbackFactory``. The callback is passed to ``Open`` to be called 166upon its completion. ``Open`` begins loading the ``URLRequestInfo``. 167 168.. naclcode:: 169 170 void URLLoaderHandler::Start() { 171 pp::CompletionCallback cc = 172 cc_factory_.NewCallback(&URLLoaderHandler::OnOpen); 173 url_loader_.Open(url_request_, cc); 174 } 175 176``OnOpen`` ensures that the Open call was successful and, if so, calls 177``GetDownloadProgress`` to determine the amount of data to be downloaded so it 178can allocate memory for the response body. 179 180Note that the amount of data to be downloaded may be unknown, in which case 181``GetDownloadProgress`` sets ``total_bytes_to_be_received`` to -1. It is not a 182problem if ``total_bytes_to_be_received`` is set to -1 or if 183``GetDownloadProgress`` fails; in these scenarios memory for the read buffer 184can't be allocated in advance and must be allocated as data is received. 185 186Finally, ``OnOpen`` calls ``ReadBody.`` 187 188.. naclcode:: 189 190 void URLLoaderHandler::OnOpen(int32_t result) { 191 if (result != PP_OK) { 192 ReportResultAndDie(url_, "pp::URLLoader::Open() failed", false); 193 return; 194 } 195 int64_t bytes_received = 0; 196 int64_t total_bytes_to_be_received = 0; 197 if (url_loader_.GetDownloadProgress(&bytes_received, 198 &total_bytes_to_be_received)) { 199 if (total_bytes_to_be_received > 0) { 200 url_response_body_.reserve(total_bytes_to_be_received); 201 } 202 } 203 url_request_.SetRecordDownloadProgress(false); 204 ReadBody(); 205 } 206 207``ReadBody`` creates another ``CompletionCallback`` (a ``NewOptionalCallback``) 208and passes it to ``ReadResponseBody,`` which reads the response body, and 209``AppendDataBytes,`` which appends the resulting data to the previously read 210data. 211 212.. naclcode:: 213 214 void URLLoaderHandler::ReadBody() { 215 pp::CompletionCallback cc = 216 cc_factory_.NewOptionalCallback(&URLLoaderHandler::OnRead); 217 int32_t result = PP_OK; 218 do { 219 result = url_loader_.ReadResponseBody(buffer_, READ_BUFFER_SIZE, cc); 220 if (result > 0) { 221 AppendDataBytes(buffer_, result); 222 } 223 } while (result > 0); 224 225 if (result != PP_OK_COMPLETIONPENDING) { 226 cc.Run(result); 227 } 228 } 229 230 void URLLoaderHandler::AppendDataBytes(const char* buffer, int32_t num_bytes) { 231 if (num_bytes <= 0) 232 return; 233 num_bytes = std::min(READ_BUFFER_SIZE, num_bytes); 234 url_response_body_.insert( 235 url_response_body_.end(), buffer, buffer + num_bytes); 236 } 237 238Eventually either all the bytes have been read for the entire file (resulting 239in ``PP_OK`` or 0), all the bytes have been read for what has been 240downloaded, but more is to be downloaded (``PP_OK_COMPLETIONPENDING`` or -1), 241or there is an error (less than -1). ``OnRead`` is called in the event of an 242error or ``PP_OK``. 243 244Displaying a result 245------------------- 246 247OnRead calls ``ReportResultAndDie`` when either an error or ``PP_OK`` is 248returned to indicate streaming of file is complete. ``ReportResultAndDie`` then 249calls ``ReportResult,`` which calls ``PostMessage`` to send the result back to 250the HTML page. 251