| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This example shows how to use the URLLoader in streaming mode (reading to |
| // memory as data comes over the network). This example uses PostMessage between |
| // the plugin and the url_loader.html page in this directory to start the load |
| // and to communicate the result. |
| // |
| // The other mode is to stream to a file instead. See stream_to_file.cc |
| |
| #include <stdint.h> |
| |
| #include "ppapi/cpp/instance.h" |
| #include "ppapi/cpp/module.h" |
| #include "ppapi/cpp/url_loader.h" |
| #include "ppapi/cpp/url_request_info.h" |
| #include "ppapi/cpp/url_response_info.h" |
| #include "ppapi/utility/completion_callback_factory.h" |
| |
| // When compiling natively on Windows, PostMessage can be #define-d to |
| // something else. |
| #ifdef PostMessage |
| #undef PostMessage |
| #endif |
| |
| // Buffer size for reading network data. |
| const int kBufSize = 1024; |
| |
| class MyInstance : public pp::Instance { |
| public: |
| explicit MyInstance(PP_Instance instance) |
| : pp::Instance(instance) { |
| factory_.Initialize(this); |
| } |
| virtual ~MyInstance() { |
| // Make sure to explicitly close the loader. If somebody else is holding a |
| // reference to the URLLoader object when this class goes out of scope (so |
| // the URLLoader outlives "this"), and you have an outstanding read |
| // request, the URLLoader will write into invalid memory. |
| loader_.Close(); |
| } |
| |
| // Handler for the page sending us messages. |
| virtual void HandleMessage(const pp::Var& message_data); |
| |
| private: |
| // Called to initiate the request. |
| void StartRequest(const std::string& url); |
| |
| // Callback for the URLLoader to tell us it finished opening the connection. |
| void OnOpenComplete(int32_t result); |
| |
| // Starts streaming data. |
| void ReadMore(); |
| |
| // Callback for the URLLoader to tell us when it finished a read. |
| void OnReadComplete(int32_t result); |
| |
| // Forwards the given string to the page. |
| void ReportResponse(const std::string& data); |
| |
| // Generates completion callbacks scoped to this class. |
| pp::CompletionCallbackFactory<MyInstance> factory_; |
| |
| pp::URLLoader loader_; |
| pp::URLResponseInfo response_; |
| |
| // The buffer used for the current read request. This is filled and then |
| // copied into content_ to build up the entire document. |
| char buf_[kBufSize]; |
| |
| // All the content loaded so far. |
| std::string content_; |
| }; |
| |
| void MyInstance::HandleMessage(const pp::Var& message_data) { |
| if (message_data.is_string() && message_data.AsString() == "go") |
| StartRequest("./fetched_content.html"); |
| } |
| |
| void MyInstance::StartRequest(const std::string& url) { |
| content_.clear(); |
| |
| pp::URLRequestInfo request(this); |
| request.SetURL(url); |
| request.SetMethod("GET"); |
| |
| loader_ = pp::URLLoader(this); |
| loader_.Open(request, |
| factory_.NewCallback(&MyInstance::OnOpenComplete)); |
| } |
| |
| void MyInstance::OnOpenComplete(int32_t result) { |
| if (result != PP_OK) { |
| ReportResponse("URL could not be requested"); |
| return; |
| } |
| |
| response_ = loader_.GetResponseInfo(); |
| |
| // Here you would process the headers. A real program would want to at least |
| // check the HTTP code and potentially cancel the request. |
| |
| // Start streaming. |
| ReadMore(); |
| } |
| |
| void MyInstance::ReadMore() { |
| // Note that you specifically want an "optional" callback here. This will |
| // allow Read() to return synchronously, ignoring your completion callback, |
| // if data is available. For fast connections and large files, reading as |
| // fast as we can will make a large performance difference. However, in the |
| // case of a synchronous return, we need to be sure to run the callback we |
| // created since the loader won't do anything with it. |
| pp::CompletionCallback cc = |
| factory_.NewOptionalCallback(&MyInstance::OnReadComplete); |
| int32_t result = PP_OK; |
| do { |
| result = loader_.ReadResponseBody(buf_, kBufSize, cc); |
| // Handle streaming data directly. Note that we *don't* want to call |
| // OnReadComplete here, since in the case of result > 0 it will schedule |
| // another call to this function. If the network is very fast, we could |
| // end up with a deeply recursive stack. |
| if (result > 0) |
| content_.append(buf_, result); |
| } while (result > 0); |
| |
| if (result != PP_OK_COMPLETIONPENDING) { |
| // Either we reached the end of the stream (result == PP_OK) or there was |
| // an error. We want OnReadComplete to get called no matter what to handle |
| // that case, whether the error is synchronous or asynchronous. If the |
| // result code *is* COMPLETIONPENDING, our callback will be called |
| // asynchronously. |
| cc.Run(result); |
| } |
| } |
| |
| void MyInstance::OnReadComplete(int32_t result) { |
| if (result == PP_OK) { |
| // Streaming the file is complete. |
| ReportResponse(content_); |
| } else if (result > 0) { |
| // The URLLoader just filled "result" number of bytes into our buffer. |
| // Save them and perform another read. |
| content_.append(buf_, result); |
| ReadMore(); |
| } else { |
| // A read error occurred. |
| ReportResponse("A read error occurred"); |
| } |
| } |
| |
| void MyInstance::ReportResponse(const std::string& data) { |
| PostMessage(pp::Var(data)); |
| } |
| |
| // This object is the global object representing this plugin library as long |
| // as it is loaded. |
| class MyModule : public pp::Module { |
| public: |
| MyModule() : pp::Module() {} |
| virtual ~MyModule() {} |
| |
| // Override CreateInstance to create your customized Instance object. |
| virtual pp::Instance* CreateInstance(PP_Instance instance) { |
| return new MyInstance(instance); |
| } |
| }; |
| |
| namespace pp { |
| |
| // Factory function for your specialization of the Module object. |
| Module* CreateModule() { |
| return new MyModule(); |
| } |
| |
| } // namespace pp |