• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
MyInstance(PP_Instance instance)30   explicit MyInstance(PP_Instance instance)
31       : pp::Instance(instance) {
32     factory_.Initialize(this);
33   }
~MyInstance()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 
HandleMessage(const pp::Var & message_data)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 
StartRequest(const std::string & url)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 
OnOpenComplete(int32_t result)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 
ReadMore()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 
OnReadComplete(int32_t result)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 
ReportResponse(const std::string & data)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:
MyModule()160   MyModule() : pp::Module() {}
~MyModule()161   virtual ~MyModule() {}
162 
163   // Override CreateInstance to create your customized Instance object.
CreateInstance(PP_Instance instance)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.
CreateModule()172 Module* CreateModule() {
173   return new MyModule();
174 }
175 
176 }  // namespace pp
177