Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / doc / devguide / coding / url-loading.rst
blob9949467cfe0ab6a35773df6c0a38bcd26a6545d8
1 .. _devguide-coding-url-loading:
3 ###########
4 URL Loading
5 ###########
7 .. contents::
8   :local:
9   :backlinks: none
10   :depth: 2
12 Introduction
13 ============
15 This section describes how to use the `URLLoader API
16 </native-client/pepper_stable/cpp/classpp_1_1_u_r_l_loader>`_ to load resources
17 such as images and sound files from a server into your application.
19 The example discussed in this section is included in the SDK in the directory
20 ``examples/api/url_loader``.
22 Reference information
23 =====================
25 For reference information related to loading data from URLs, see the
26 following documentation:
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
37 Background
38 ==========
40 When a user launches your Native Client web application, Chrome downloads and
41 caches your application's HTML file, manifest file (.nmf), and Native Client
42 module (.pexe or .nexe). If your application needs additional assets, such as
43 images and sound files, it must explicitly load those assets. You can use the
44 Pepper APIs described in this section to load assets from a URL into your
45 application.
47 After you've loaded assets into your application, Chrome will cache those
48 assets. To avoid being at the whim of the Chrome cache, however, you may want
49 to use the `Pepper FileIO API
50 </native-client/pepper_stable/cpp/classpp_1_1_file_i_o>`_ to write those assets
51 to a persistent, sandboxed location on the user's file system.
53 The ``url_loader`` example
54 ==========================
56 The SDK includes an example called ``url_loader`` demonstrating downloading
57 files from a server. This example has these primary files:
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).
71 The remainder of this document covers the code in the ``url_loader.cc`` and
72 ``url_loader_handler.cc`` files.
74 URL loading overview
75 --------------------
77 Like many Pepper APIs, the ``URLLoader`` API includes a set of methods that
78 execute asynchronously and that invoke callback functions in your Native Client
79 module. The high-level flow for the ``url_loader`` example is described below.
80 Note 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
82 Native Client module (specifically in the file ``url_loader_handler.cc``). The
83 following image shows the flow of the ``url_loader_handler`` code:
85 .. image:: /images/pepper-urlloader-api.png
87 Following are the high-level steps involved in URL loading.
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).
101 The remainder of this document demonstrates how the previous steps are
102 implemented in the ``url_loader`` example.
104 ``url_loader`` deep dive
105 ========================
107 Setting up the request
108 ----------------------
110 ``HandleMessage`` in ``url_loader.cc`` creates a ``URLLoaderHandler`` instance
111 and passes it the URL of the asset to be retrieved. Then ``HandleMessage``
112 calls ``Start`` to start retrieving the asset from the server:
114 .. naclcode::
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   }
141 Notice 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``):
146 .. naclcode::
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   }
161 Downloading the data
162 --------------------
164 ``Start`` in ``url_loader_handler.cc`` creates a callback (``cc``) using a
165 ``CompletionCallbackFactory``. The callback is passed to ``Open`` to be called
166 upon its completion. ``Open`` begins loading the ``URLRequestInfo``.
168 .. naclcode::
170   void URLLoaderHandler::Start() {
171     pp::CompletionCallback cc =
172         cc_factory_.NewCallback(&URLLoaderHandler::OnOpen);
173     url_loader_.Open(url_request_, cc);
174   }
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
178 can allocate memory for the response body.
180 Note 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
182 problem if ``total_bytes_to_be_received`` is set to -1 or if
183 ``GetDownloadProgress`` fails; in these scenarios memory for the read buffer
184 can't be allocated in advance and must be allocated as data is received.
186 Finally, ``OnOpen`` calls ``ReadBody.``
188 .. naclcode::
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   }
207 ``ReadBody`` creates another ``CompletionCallback`` (a ``NewOptionalCallback``)
208 and passes it to ``ReadResponseBody,`` which reads the response body, and
209 ``AppendDataBytes,`` which appends the resulting data to the previously read
210 data.
212 .. naclcode::
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);
225     if (result != PP_OK_COMPLETIONPENDING) {
226       cc.Run(result);
227     }
228   }
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   }
238 Eventually either all the bytes have been read for the entire file (resulting
239 in ``PP_OK`` or 0), all the bytes have been read for what has been
240 downloaded, but more is to be downloaded (``PP_OK_COMPLETIONPENDING`` or -1),
241 or there is an error (less than -1). ``OnRead`` is called in the event of an
242 error or ``PP_OK``.
244 Displaying a result
245 -------------------
247 OnRead calls ``ReportResultAndDie`` when either an error or ``PP_OK`` is
248 returned to indicate streaming of file is complete. ``ReportResultAndDie`` then
249 calls ``ReportResult,`` which calls ``PostMessage`` to send the result back to
250 the HTML page.