[go: nahoru, domu]

blob: 4d035f87e87ec4ab6991f53399a8217d840107c7 [file] [log] [blame]
{{+bindTo:partials.standard_nacl_article}}
<b><font color="#cc0000">
NOTE:
Deprecation of the technologies described here has been announced
for platforms other than ChromeOS.<br/>
Please visit our
<a href="/native-client/migration">migration guide</a>
for details.
</font></b>
<hr/><section id="url-loading">
<h1 id="url-loading">URL Loading</h1>
<div class="contents local" id="contents" style="display: none">
<ul class="small-gap">
<li><a class="reference internal" href="#introduction" id="id1">Introduction</a></li>
<li><a class="reference internal" href="#reference-information" id="id2">Reference information</a></li>
<li><a class="reference internal" href="#background" id="id3">Background</a></li>
<li><p class="first"><a class="reference internal" href="#the-url-loader-example" id="id4">The <code>url_loader</code> example</a></p>
<ul class="small-gap">
<li><a class="reference internal" href="#url-loading-overview" id="id5">URL loading overview</a></li>
</ul>
</li>
<li><p class="first"><a class="reference internal" href="#url-loader-deep-dive" id="id6"><code>url_loader</code> deep dive</a></p>
<ul class="small-gap">
<li><a class="reference internal" href="#setting-up-the-request" id="id7">Setting up the request</a></li>
<li><a class="reference internal" href="#downloading-the-data" id="id8">Downloading the data</a></li>
<li><a class="reference internal" href="#displaying-a-result" id="id9">Displaying a result</a></li>
</ul>
</li>
</ul>
</div><h2 id="introduction">Introduction</h2>
<p>This section 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
such as images and sound files from a server into your application.</p>
<p>The example discussed in this section is included in the SDK in the directory
<code>examples/api/url_loader</code>.</p>
<h2 id="reference-information">Reference information</h2>
<p>For reference information related to loading data from URLs, see the
following documentation:</p>
<ul class="small-gap">
<li><a class="reference external" href="/native-client/pepper_stable/cpp/url__loader_8h">url_loader.h</a> - Contains
<code>URLLoader</code> class for loading data from URLs</li>
<li><a class="reference external" href="/native-client/pepper_stable/cpp/url__request__info_8h">url_request_info.h</a> - Contains
<code>URLRequest</code> class for creating and manipulating URL requests</li>
<li><a class="reference external" href="/native-client/pepper_stable/cpp/url__response__info_8h">url_response_info.h</a> - Contains
<code>URLResponse</code> class for examaning URL responses</li>
</ul>
<h2 id="background">Background</h2>
<p>When a user launches your Native Client web application, Chrome downloads and
caches your application&#8217;s HTML file, manifest file (.nmf), and Native Client
module (.pexe or .nexe). If your application needs additional assets, such as
images and sound files, it must explicitly load those assets. You can use the
Pepper APIs described in this section to load assets from a URL into your
application.</p>
<p>After you&#8217;ve loaded assets into your application, Chrome will cache those
assets. To avoid being at the whim of the Chrome cache, however, you may want
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
to a persistent, sandboxed location on the user&#8217;s file system.</p>
<h2 id="the-url-loader-example">The <code>url_loader</code> example</h2>
<p>The SDK includes an example called <code>url_loader</code> demonstrating downloading
files from a server. This example has these primary files:</p>
<ul class="small-gap">
<li><code>index.html</code> - The HTML code that launches the Native Client module.</li>
<li><code>example.js</code> - The JavaScript file for index.html. It has code that sends
a PostMessage request to the Native Client module when the &#8220;Get URL&#8221; button
is clicked.</li>
<li><code>url_loader_success.html</code> - An HTML file on the server whose contents are
being retrieved using the <code>URLLoader</code> API.</li>
<li><code>url_loader.cc</code> - The code that sets up and provides and entry point into
the Native client module.</li>
<li><code>url_loader_handler.cc</code> - The code that retrieves the contents of the
url_loader_success.html file and returns the results (this is where the
bulk of the work is done).</li>
</ul>
<p>The remainder of this document covers the code in the <code>url_loader.cc</code> and
<code>url_loader_handler.cc</code> files.</p>
<h3 id="url-loading-overview">URL loading overview</h3>
<p>Like many Pepper APIs, the <code>URLLoader</code> API includes a set of methods that
execute asynchronously and that invoke callback functions in your Native Client
module. The high-level flow for the <code>url_loader</code> example is described below.
Note that methods in the namespace <code>pp::URLLoader</code> are part of the Pepper
<code>URLLoader</code> API, while the rest of the functions are part of the code in the
Native Client module (specifically in the file <code>url_loader_handler.cc</code>). The
following image shows the flow of the <code>url_loader_handler</code> code:</p>
<img alt="/native-client/images/pepper-urlloader-api.png" src="/native-client/images/pepper-urlloader-api.png" />
<p>Following are the high-level steps involved in URL loading.</p>
<ol class="arabic simple">
<li>The Native Client module calls <code>pp::URLLoader::Open</code> to begin opening the
URL.</li>
<li>When <code>Open</code> completes, it invokes a callback function in the Native Client
module (in this case, <code>OnOpen</code>).</li>
<li>The Native Client module calls the Pepper function
<code>URLLoader::ReadResponseBody</code> to begin reading the response body with the
data. <code>ReadResponseBody</code> is passed an optional callback function in the
Native Client module (in this case, On <code>Read</code>). The callback function is
an optional callback because <code>ReadResponseBody</code> may read data and return
synchronously if data is available (this improves performance for large
files and fast connections).</li>
</ol>
<p>The remainder of this document demonstrates how the previous steps are
implemented in the <code>url_loader</code> example.</p>
<h2 id="url-loader-deep-dive"><code>url_loader</code> deep dive</h2>
<h3 id="setting-up-the-request">Setting up the request</h3>
<p><code>HandleMessage</code> in <code>url_loader.cc</code> creates a <code>URLLoaderHandler</code> instance
and passes it the URL of the asset to be retrieved. Then <code>HandleMessage</code>
calls <code>Start</code> to start retrieving the asset from the server:</p>
<pre class="prettyprint">
void URLLoaderInstance::HandleMessage(const pp::Var&amp; var_message) {
if (!var_message.is_string()) {
return;
}
std::string message = var_message.AsString();
if (message.find(kLoadUrlMethodId) == 0) {
// The argument to getUrl is everything after the first ':'.
size_t sep_pos = message.find_first_of(kMessageArgumentSeparator);
if (sep_pos != std::string::npos) {
std::string url = message.substr(sep_pos + 1);
printf(&quot;URLLoaderInstance::HandleMessage('%s', '%s')\n&quot;,
message.c_str(),
url.c_str());
fflush(stdout);
URLLoaderHandler* handler = URLLoaderHandler::Create(this, url);
if (handler != NULL) {
// Starts asynchronous download. When download is finished or when an
// error occurs, |handler| posts the results back to the browser
// vis PostMessage and self-destroys.
handler-&gt;Start();
}
}
}
}
</pre>
<p>Notice that the constructor for <code>URLLoaderHandler</code> in
<code>url_loader_handler.cc</code> sets up the parameters of the URL request (using
<code>SetURL,</code> <code>SetMethod</code>, and <code>SetRecordDownloadProgress</code>):</p>
<pre class="prettyprint">
URLLoaderHandler::URLLoaderHandler(pp::Instance* instance,
const std::string&amp; url)
: instance_(instance),
url_(url),
url_request_(instance),
url_loader_(instance),
buffer_(new char[READ_BUFFER_SIZE]),
cc_factory_(this) {
url_request_.SetURL(url);
url_request_.SetMethod(&quot;GET&quot;);
url_request_.SetRecordDownloadProgress(true);
}
</pre>
<h3 id="downloading-the-data">Downloading the data</h3>
<p><code>Start</code> in <code>url_loader_handler.cc</code> creates a callback (<code>cc</code>) using a
<code>CompletionCallbackFactory</code>. The callback is passed to <code>Open</code> to be called
upon its completion. <code>Open</code> begins loading the <code>URLRequestInfo</code>.</p>
<pre class="prettyprint">
void URLLoaderHandler::Start() {
pp::CompletionCallback cc =
cc_factory_.NewCallback(&amp;URLLoaderHandler::OnOpen);
url_loader_.Open(url_request_, cc);
}
</pre>
<p><code>OnOpen</code> ensures that the Open call was successful and, if so, calls
<code>GetDownloadProgress</code> to determine the amount of data to be downloaded so it
can allocate memory for the response body.</p>
<p>Note that the amount of data to be downloaded may be unknown, in which case
<code>GetDownloadProgress</code> sets <code>total_bytes_to_be_received</code> to -1. It is not a
problem if <code>total_bytes_to_be_received</code> is set to -1 or if
<code>GetDownloadProgress</code> fails; in these scenarios memory for the read buffer
can&#8217;t be allocated in advance and must be allocated as data is received.</p>
<p>Finally, <code>OnOpen</code> calls <code>ReadBody.</code></p>
<pre class="prettyprint">
void URLLoaderHandler::OnOpen(int32_t result) {
if (result != PP_OK) {
ReportResultAndDie(url_, &quot;pp::URLLoader::Open() failed&quot;, false);
return;
}
int64_t bytes_received = 0;
int64_t total_bytes_to_be_received = 0;
if (url_loader_.GetDownloadProgress(&amp;bytes_received,
&amp;total_bytes_to_be_received)) {
if (total_bytes_to_be_received &gt; 0) {
url_response_body_.reserve(total_bytes_to_be_received);
}
}
url_request_.SetRecordDownloadProgress(false);
ReadBody();
}
</pre>
<p><code>ReadBody</code> creates another <code>CompletionCallback</code> (a <code>NewOptionalCallback</code>)
and passes it to <code>ReadResponseBody,</code> which reads the response body, and
<code>AppendDataBytes,</code> which appends the resulting data to the previously read
data.</p>
<pre class="prettyprint">
void URLLoaderHandler::ReadBody() {
pp::CompletionCallback cc =
cc_factory_.NewOptionalCallback(&amp;URLLoaderHandler::OnRead);
int32_t result = PP_OK;
do {
result = url_loader_.ReadResponseBody(buffer_, READ_BUFFER_SIZE, cc);
if (result &gt; 0) {
AppendDataBytes(buffer_, result);
}
} while (result &gt; 0);
if (result != PP_OK_COMPLETIONPENDING) {
cc.Run(result);
}
}
void URLLoaderHandler::AppendDataBytes(const char* buffer, int32_t num_bytes) {
if (num_bytes &lt;= 0)
return;
num_bytes = std::min(READ_BUFFER_SIZE, num_bytes);
url_response_body_.insert(
url_response_body_.end(), buffer, buffer + num_bytes);
}
</pre>
<p>Eventually either all the bytes have been read for the entire file (resulting
in <code>PP_OK</code> or 0), all the bytes have been read for what has been
downloaded, but more is to be downloaded (<code>PP_OK_COMPLETIONPENDING</code> or -1),
or there is an error (less than -1). <code>OnRead</code> is called in the event of an
error or <code>PP_OK</code>.</p>
<h3 id="displaying-a-result">Displaying a result</h3>
<p>OnRead calls <code>ReportResultAndDie</code> when either an error or <code>PP_OK</code> is
returned to indicate streaming of file is complete. <code>ReportResultAndDie</code> then
calls <code>ReportResult,</code> which calls <code>PostMessage</code> to send the result back to
the HTML page.</p>
</section>
{{/partials.standard_nacl_article}}