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.
26const int kBufSize = 1024;
27
28class 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
75void MyInstance::HandleMessage(const pp::Var& message_data) {
76  if (message_data.is_string() && message_data.AsString() == "go")
77    StartRequest("./fetched_content.html");
78}
79
80void 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
92void 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
107void 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
137void 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
152void 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.
158class 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
169namespace pp {
170
171// Factory function for your specialization of the Module object.
172Module* CreateModule() {
173  return new MyModule();
174}
175
176}  // namespace pp
177