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