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