Home | History | Annotate | Download | only in url_loader
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // This example shows how to use the URLLoader in streaming mode (reading to
      6 // memory as data comes over the network). This example uses PostMessage between
      7 // the plugin and the url_loader.html page in this directory to start the load
      8 // and to communicate the result.
      9 //
     10 // The other mode is to stream to a file instead. See stream_to_file.cc
     11 
     12 #include "ppapi/cpp/instance.h"
     13 #include "ppapi/cpp/module.h"
     14 #include "ppapi/cpp/url_loader.h"
     15 #include "ppapi/cpp/url_request_info.h"
     16 #include "ppapi/cpp/url_response_info.h"
     17 #include "ppapi/utility/completion_callback_factory.h"
     18 
     19 // When compiling natively on Windows, PostMessage can be #define-d to
     20 // something else.
     21 #ifdef PostMessage
     22 #undef PostMessage
     23 #endif
     24 
     25 // Buffer size for reading network data.
     26 const int kBufSize = 1024;
     27 
     28 class MyInstance : public pp::Instance {
     29  public:
     30   explicit MyInstance(PP_Instance instance)
     31       : pp::Instance(instance) {
     32     factory_.Initialize(this);
     33   }
     34   virtual ~MyInstance() {
     35     // Make sure to explicitly close the loader. If somebody else is holding a
     36     // reference to the URLLoader object when this class goes out of scope (so
     37     // the URLLoader outlives "this"), and you have an outstanding read
     38     // request, the URLLoader will write into invalid memory.
     39     loader_.Close();
     40   }
     41 
     42   // Handler for the page sending us messages.
     43   virtual void HandleMessage(const pp::Var& message_data);
     44 
     45  private:
     46   // Called to initiate the request.
     47   void StartRequest(const std::string& url);
     48 
     49   // Callback for the URLLoader to tell us it finished opening the connection.
     50   void OnOpenComplete(int32_t result);
     51 
     52   // Starts streaming data.
     53   void ReadMore();
     54 
     55   // Callback for the URLLoader to tell us when it finished a read.
     56   void OnReadComplete(int32_t result);
     57 
     58   // Forwards the given string to the page.
     59   void ReportResponse(const std::string& data);
     60 
     61   // Generates completion callbacks scoped to this class.
     62   pp::CompletionCallbackFactory<MyInstance> factory_;
     63 
     64   pp::URLLoader loader_;
     65   pp::URLResponseInfo response_;
     66 
     67   // The buffer used for the current read request. This is filled and then
     68   // copied into content_ to build up the entire document.
     69   char buf_[kBufSize];
     70 
     71   // All the content loaded so far.
     72   std::string content_;
     73 };
     74 
     75 void MyInstance::HandleMessage(const pp::Var& message_data) {
     76   if (message_data.is_string() && message_data.AsString() == "go")
     77     StartRequest("./fetched_content.html");
     78 }
     79 
     80 void MyInstance::StartRequest(const std::string& url) {
     81   content_.clear();
     82 
     83   pp::URLRequestInfo request(this);
     84   request.SetURL(url);
     85   request.SetMethod("GET");
     86 
     87   loader_ = pp::URLLoader(this);
     88   loader_.Open(request,
     89                factory_.NewCallback(&MyInstance::OnOpenComplete));
     90 }
     91 
     92 void MyInstance::OnOpenComplete(int32_t result) {
     93   if (result != PP_OK) {
     94     ReportResponse("URL could not be requested");
     95     return;
     96   }
     97 
     98   response_ = loader_.GetResponseInfo();
     99 
    100   // Here you would process the headers. A real program would want to at least
    101   // check the HTTP code and potentially cancel the request.
    102 
    103   // Start streaming.
    104   ReadMore();
    105 }
    106 
    107 void MyInstance::ReadMore() {
    108   // Note that you specifically want an "optional" callback here. This will
    109   // allow Read() to return synchronously, ignoring your completion callback,
    110   // if data is available. For fast connections and large files, reading as
    111   // fast as we can will make a large performance difference. However, in the
    112   // case of a synchronous return, we need to be sure to run the callback we
    113   // created since the loader won't do anything with it.
    114   pp::CompletionCallback cc =
    115       factory_.NewOptionalCallback(&MyInstance::OnReadComplete);
    116   int32_t result = PP_OK;
    117   do {
    118     result = loader_.ReadResponseBody(buf_, kBufSize, cc);
    119     // Handle streaming data directly. Note that we *don't* want to call
    120     // OnReadComplete here, since in the case of result > 0 it will schedule
    121     // another call to this function. If the network is very fast, we could
    122     // end up with a deeply recursive stack.
    123     if (result > 0)
    124       content_.append(buf_, result);
    125   } while (result > 0);
    126 
    127   if (result != PP_OK_COMPLETIONPENDING) {
    128     // Either we reached the end of the stream (result == PP_OK) or there was
    129     // an error. We want OnReadComplete to get called no matter what to handle
    130     // that case, whether the error is synchronous or asynchronous. If the
    131     // result code *is* COMPLETIONPENDING, our callback will be called
    132     // asynchronously.
    133     cc.Run(result);
    134   }
    135 }
    136 
    137 void MyInstance::OnReadComplete(int32_t result) {
    138   if (result == PP_OK) {
    139     // Streaming the file is complete.
    140     ReportResponse(content_);
    141   } else if (result > 0) {
    142     // The URLLoader just filled "result" number of bytes into our buffer.
    143     // Save them and perform another read.
    144     content_.append(buf_, result);
    145     ReadMore();
    146   } else {
    147     // A read error occurred.
    148     ReportResponse("A read error occurred");
    149   }
    150 }
    151 
    152 void MyInstance::ReportResponse(const std::string& data) {
    153   PostMessage(pp::Var(data));
    154 }
    155 
    156 // This object is the global object representing this plugin library as long
    157 // as it is loaded.
    158 class MyModule : public pp::Module {
    159  public:
    160   MyModule() : pp::Module() {}
    161   virtual ~MyModule() {}
    162 
    163   // Override CreateInstance to create your customized Instance object.
    164   virtual pp::Instance* CreateInstance(PP_Instance instance) {
    165     return new MyInstance(instance);
    166   }
    167 };
    168 
    169 namespace pp {
    170 
    171 // Factory function for your specialization of the Module object.
    172 Module* CreateModule() {
    173   return new MyModule();
    174 }
    175 
    176 }  // namespace pp
    177