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