Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(134)

Side by Side Diff: native_client_sdk/src/examples/demo/drive/drive.cc

Issue 14500010: [NaCl SDK] Google Drive example (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: synchronous on thread Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013 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 #include <ctype.h>
6 #include <stdarg.h>
7 #include <stdio.h>
8 #include <string.h>
9
10 #include <string>
11 #include <vector>
12
13 #include "json/reader.h"
14 #include "json/writer.h"
15 #include "ppapi/c/pp_errors.h"
16 #include "ppapi/cpp/completion_callback.h"
17 #include "ppapi/cpp/instance.h"
18 #include "ppapi/cpp/module.h"
19 #include "ppapi/cpp/url_loader.h"
20 #include "ppapi/cpp/url_request_info.h"
21 #include "ppapi/cpp/url_response_info.h"
22 #include "ppapi/cpp/var.h"
23 #include "ppapi/utility/completion_callback_factory.h"
24
25 namespace {
26
27 const char kTokenMessage[] = "token:";
28 const char kGetFileMessage[] = "getFile";
29 const char kBoundary[] = "NACL_BOUNDARY_600673";
30
31 std::string EncodeUriComponent(const std::string& s) {
32 char hex[] = "0123456789ABCDEF";
33 std::string result;
34 for (size_t i = 0; i < s.length(); ++i) {
35 char c = s[i];
36 if (isalpha(c) || isdigit(c) || strchr("-_.!~*'()", c)) {
37 result += c;
38 } else {
39 result += '%';
40 result += hex[(c >> 4) & 0xf];
41 result += hex[c & 0xf];
42 }
43 }
44 return result;
45 }
46
47 std::string IntToString(int x) {
48 char buffer[32];
49 snprintf(&buffer[0], 32, "%d", x);
50 return &buffer[0];
51 }
52
53 void AddQueryParameter(std::string* s,
noelallen1 2013/04/30 18:46:08 Incorrect indent.
binji 2013/04/30 20:07:27 Done.
54 const std::string& key,
55 const std::string& value,
56 bool first) {
57 *s += first ? '?' : '&';
58 *s += EncodeUriComponent(key);
59 *s += '=';
60 *s += EncodeUriComponent(value);
61 }
62
63 void AddQueryParameter(std::string* s,
64 const std::string& key,
65 int value,
66 bool first) {
67 AddQueryParameter(s, key, IntToString(value), first);
68 }
69
70 void AddAuthTokenHeader(std::string* s, const std::string& auth_token) {
71 *s += "Authorization: Bearer ";
72 *s += auth_token;
73 *s += "\n";
74 }
75
76 void AddHeader(std::string* s,
77 const char* key,
78 const std::string& value) {
79 *s += key;
80 *s += ": ";
81 *s += value;
82 *s += "\n";
83 }
84
85 } // namespace
86
87 //
88 // UrlReader
89 //
90 struct UrlReaderParams {
91 std::string url;
92 std::string method;
93 std::string request_headers;
94 std::string request_body;
95 };
96
97 int32_t ReadUrl(pp::Instance* instance,
98 const UrlReaderParams& params,
99 std::string* output) {
100 pp::URLRequestInfo url_request(instance);
101 pp::URLLoader url_loader(instance);
102
103 url_request.SetURL(params.url);
104 url_request.SetMethod(params.method);
105 url_request.SetHeaders(params.request_headers);
106 url_request.SetRecordDownloadProgress(true);
107 if (params.request_body.size()) {
108 url_request.AppendDataToBody(params.request_body.data(),
109 params.request_body.size());
110 }
111
112 int32_t result = url_loader.Open(url_request, pp::BlockUntilComplete());
113 if (result != PP_OK) {
114 return result;
115 }
116
117 pp::URLResponseInfo url_response = url_loader.GetResponseInfo();
118 if (url_response.GetStatusCode() != 200)
119 return PP_ERROR_FAILED;
120
121 output->clear();
122
123 int64_t bytes_received = 0;
124 int64_t total_bytes_to_be_received = 0;
125 if (url_loader.GetDownloadProgress(&bytes_received,
126 &total_bytes_to_be_received)) {
127 if (total_bytes_to_be_received > 0) {
128 output->reserve(total_bytes_to_be_received);
129 }
130 }
131
132 url_request.SetRecordDownloadProgress(false);
133
134 const size_t kReadBufferSize = 16 * 1024;
135 uint8_t* buffer_ = new uint8_t[kReadBufferSize];
136
137 do {
138 result = url_loader.ReadResponseBody(
139 buffer_, kReadBufferSize, pp::BlockUntilComplete());
140 if (result > 0) {
141 size_t num_bytes =
142 result > kReadBufferSize ? kReadBufferSize : result;
143 output->insert(output->end(), buffer_, buffer_ + num_bytes);
144 }
145 } while (result > 0);
146
147 delete[] buffer_;
148
149 return result;
150 }
151
152 //
153 // FilesList
154 //
155 struct FilesListParams {
156 int max_results;
157 std::string page_token;
158 std::string q;
159 };
160
161 int32_t ListFiles(pp::Instance* instance,
162 const std::string& auth_token,
163 const FilesListParams& params,
164 Json::Value* root) {
165 static const char base_url[] = "https://www.googleapis.com/drive/v2/files";
166
167 UrlReaderParams p;
168 p.method = "GET";
169 p.url = base_url;
170 AddQueryParameter(&p.url, "maxResults", params.max_results, true);
171 if (params.page_token.length())
172 AddQueryParameter(&p.url, "pageToken", params.page_token, false);
173 AddQueryParameter(&p.url, "q", params.q, false);
174 AddQueryParameter(&p.url, "fields", "items(id,downloadUrl)", false);
175 AddAuthTokenHeader(&p.request_headers, auth_token);
176
177 std::string output;
178 int32_t result = ReadUrl(instance, p, &output);
179 if (result != PP_OK) {
180 return result;
181 }
182
183 Json::Reader reader(Json::Features::strictMode());
184 if (!reader.parse(output, *root, false)) {
185 return PP_ERROR_FAILED;
186 }
187
188 return PP_OK;
189 }
190
191 //
192 // FilesInsert
193 //
194 struct FilesInsertParams {
195 std::string file_id;
196 std::string content;
197 std::string description;
198 std::string mime_type;
199 std::string title;
200 };
201
202 std::string BuildRequestBody(const FilesInsertParams& params) {
203 std::string result;
204 result += "--";
205 result += kBoundary;
206 result += "\nContent-Type: application/json; charset=UTF-8\n\n";
207
208 Json::Value value(Json::objectValue);
209 if (!params.description.empty())
210 value["description"] = Json::Value(params.description);
211
212 if (!params.mime_type.empty())
213 value["mimeType"] = Json::Value(params.mime_type);
214
215 if (!params.title.empty())
216 value["title"] = Json::Value(params.title);
217
218 Json::FastWriter writer;
219 std::string metadata = writer.write(value);
220
221 result += metadata;
222 result += "--";
223 result += kBoundary;
224 result += "\nContent-Type: ";
225 result += params.mime_type;
226 result += "\n\n";
227 result += params.content;
228 result += "\n--";
229 result += kBoundary;
230 result += "--";
231 return result;
232 }
233
234 int32_t InsertFile(pp::Instance* instance,
235 const std::string& auth_token,
236 const FilesInsertParams& params,
237 Json::Value* root) {
238 static const char base_url[] =
239 "https://www.googleapis.com/upload/drive/v2/files";
240 const char* method = "POST";
241
242 UrlReaderParams p;
243 p.url = base_url;
244
245 // If file_id is defined, we are actually updating an existing file.
246 if (!params.file_id.empty()) {
247 p.url += "/";
248 p.url += params.file_id;
249 p.method = "PUT";
250 } else {
251 p.method = "POST";
252 }
253
254 AddQueryParameter(&p.url, "uploadType", "multipart", true);
255 AddQueryParameter(&p.url, "fields", "id,downloadUrl", false);
256 AddAuthTokenHeader(&p.request_headers, auth_token);
257 AddHeader(&p.request_headers,
258 "Content-Type",
259 std::string("multipart/related; boundary=") + kBoundary + "\n");
260 p.request_body = BuildRequestBody(params);
261
262 std::string output;
263 int32_t result = ReadUrl(instance, p, &output);
264 if (result != PP_OK) {
265 return result;
266 }
267
268 Json::Reader reader(Json::Features::strictMode());
269 if (!reader.parse(output, *root, false)) {
270 return PP_ERROR_FAILED;
271 }
272
273 return PP_OK;
274 }
275
276 //
277 // Instance
278 //
279 class Instance : public pp::Instance {
280 public:
281 explicit Instance(PP_Instance instance);
282 virtual ~Instance();
283 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]);
284 virtual void HandleMessage(const pp::Var& var_message);
285
286 void PostMessagef(const char* format, ...);
287
288 private:
289 static void* ThreadThunk(void* param);
290 void ThreadMainLoop();
291 bool ThreadRequest();
292 bool ThreadGetFileMetadata(const char* title, Json::Value* metadata);
293 bool ThreadCreateFile(const char* title,
294 const char* description,
295 const char* content,
296 Json::Value* metadata);
297 bool ThreadUpdateFile(const std::string& file_id,
298 const std::string& content,
299 Json::Value* metadata);
300 bool ThreadDownloadFile(const Json::Value& metadata, std::string* output);
301 bool GetMetadataKey(const Json::Value& metadata,
302 const char* key,
303 std::string* output);
304
305 std::string auth_token_;
306 pthread_t thread_;
307 pthread_mutex_t mutex_;
308 pthread_cond_t cond_;
309 bool has_request_;
310 };
311
312 Instance::Instance(PP_Instance instance) : pp::Instance(instance) {}
313
314 Instance::~Instance() {}
noelallen1 2013/04/30 18:46:08 required?
binji 2013/04/30 20:07:27 Done.
315
316 bool Instance::Init(uint32_t argc, const char* argn[], const char* argv[]) {
noelallen1 2013/04/30 18:46:08 Unused args. We should be consistent, see file_io
binji 2013/04/30 20:07:27 Done.
317 pthread_create(&thread_, NULL, &Instance::ThreadThunk, this);
318 pthread_mutex_init(&mutex_, NULL);
319 pthread_cond_init(&cond_, NULL);
320 return true;
321 }
322
323 void Instance::HandleMessage(const pp::Var& var_message) {
324 if (!var_message.is_string()) {
325 return;
326 }
327
328 std::string message = var_message.AsString();
329 if (!strncmp(message.c_str(), kTokenMessage, strlen(kTokenMessage))) {
330 // Auth token
331 auth_token_ = message.c_str() + strlen(kTokenMessage);
noelallen1 2013/04/30 18:46:08 message.subst(strlen(kTokenMessage))?
noelallen1 2013/04/30 18:46:08 Is this thread safe? You are setting auth_token_
binji 2013/04/30 20:07:27 Done.
332 } else if (!strncmp(
333 message.c_str(), kGetFileMessage, strlen(kGetFileMessage))) {
noelallen1 2013/04/30 18:46:08 message.compare(0, strlen(kGetFileMessage), kGetFi
binji 2013/04/30 20:07:27 Done.
334 // getFile
335 pthread_mutex_lock(&mutex_);
336 has_request_ = true;
337 pthread_cond_signal(&cond_);
338 pthread_mutex_unlock(&mutex_);
339 }
340 }
341
342 void Instance::PostMessagef(const char* format, ...) {
343 const size_t kBufferSize = 1024;
344 char buffer[kBufferSize];
345 va_list args;
346 va_start(args, format);
347 vsnprintf(&buffer[0], kBufferSize, format, args);
348
349 PostMessage(buffer);
350 }
351
352 // static
353 void* Instance::ThreadThunk(void* param) {
354 static_cast<Instance*>(param)->ThreadMainLoop();
355 return NULL;
356 }
357
358 void Instance::ThreadMainLoop() {
359 while (1) {
360 pthread_mutex_lock(&mutex_);
361 while (!has_request_) {
362 pthread_cond_wait(&cond_, &mutex_);
363 }
364 has_request_ = false;
365 pthread_mutex_unlock(&mutex_);
366
367 // Perform the request.
368 ThreadRequest();
369 }
370 }
371
372 bool Instance::ThreadRequest() {
373 static int request_count = 0;
374 static const char kTitle[] = "hello nacl.txt";
375 Json::Value metadata;
376 std::string output;
377
378 PostMessagef("log:\n Got request (#%d).\n", ++request_count);
379 PostMessagef("log: Looking for file: \"%s\".\n", kTitle);
380
381 if (!ThreadGetFileMetadata(kTitle, &metadata)) {
382 PostMessagef("log: Not found! Creating a new file...\n");
383 // No data found, write a new file.
384 static const char kDescription[] = "A file generated by NaCl!";
noelallen1 2013/04/30 18:46:08 Why is this local to ThreadRequest, but kToken is
binji 2013/04/30 20:07:27 Done.
385 static const char kInitialContent[] = "Hello, Google Drive!";
386
387 if (!ThreadCreateFile(kTitle, kDescription, kInitialContent, &metadata)) {
388 PostMessagef("log: Creating the new file failed...\n");
389 return false;
390 }
391 } else {
392 PostMessagef("log: Found it! Downloading the file...\n");
393 // Found the file, download it's data.
394 output.clear();
395 if (!ThreadDownloadFile(metadata, &output)) {
396 PostMessagef("log: Downloading the file failed...\n");
397 return false;
398 }
399
400 // Modify it.
401 output += "\nHello, again Google Drive!";
402
403 std::string file_id;
404 if (!GetMetadataKey(metadata, "id", &file_id)) {
405 PostMessagef("log: Couldn't find the file id...\n");
406 return false;
407 }
408
409 PostMessagef("log: Updating the file...\n");
410 if (!ThreadUpdateFile(file_id, output, &metadata)) {
411 PostMessagef("log: Failed to update the file...\n");
412 return false;
413 }
414 }
415
416 PostMessagef("log: Done!\n");
417 PostMessagef("log: Downloading the newly written file...\n");
418 output.clear();
419 if (!ThreadDownloadFile(metadata, &output)) {
420 PostMessagef("log: Downloading the file failed...\n");
421 return false;
422 }
423
424 PostMessagef("log: Done!\n");
425 PostMessage(output);
noelallen1 2013/04/30 18:46:08 Why PostMessage here but not above? Both cases ar
binji 2013/04/30 20:07:27 Done.
426 return true;
427 }
428
429 bool Instance::ThreadGetFileMetadata(const char* title, Json::Value* metadata) {
430 FilesListParams p;
431 p.max_results = 1;
432 p.q = "title = \'";
noelallen1 2013/04/30 18:46:08 Var name p.q?
binji 2013/04/30 20:07:27 Done.
433 p.q += title;
434 p.q += "\'";
435
436 Json::Value root;
437 int32_t result = ListFiles(this, auth_token_, p, &root);
438 if (result != PP_OK) {
439 PostMessagef("log: ListFiles failed with result %d\n", result);
440 return false;
441 }
442
443 // Extract the first item's metadata.
444 if (!root.isMember("items")) {
445 PostMessagef("log: ListFiles returned no items...\n");
446 return false;
447 }
448
449 Json::Value items = root["items"];
450 if (!items.isValidIndex(0)) {
451 PostMessage("log: Expected items[0] to be valid.\n");
452 return false;
453 }
454
455 *metadata = items[0U];
456 return true;
457 }
458
459 bool Instance::ThreadCreateFile(const char* title,
460 const char* description,
461 const char* content,
462 Json::Value* metadata) {
463 FilesInsertParams p;
464 p.content = content;
465 p.description = description;
466 p.mime_type = "text/plain";
467 p.title = title;
468
469 int32_t result = InsertFile(this, auth_token_, p, metadata);
470 if (result != PP_OK) {
471 PostMessagef("log: Creating file failed with result %d\n", result);
472 return false;
473 }
474
475 return true;
476 }
477
478 bool Instance::ThreadUpdateFile(const std::string& file_id,
479 const std::string& content,
480 Json::Value* metadata) {
481 FilesInsertParams p;
482 p.file_id = file_id;
483 p.content = content;
484 p.mime_type = "text/plain";
485
486 int32_t result = InsertFile(this, auth_token_, p, metadata);
487 if (result != PP_OK) {
488 PostMessagef("log: Updating file failed with result %d\n", result);
489 return false;
490 }
491
492 return true;
493 }
494
495 bool Instance::ThreadDownloadFile(const Json::Value& metadata,
496 std::string* output) {
497 UrlReaderParams p;
498 p.method = "GET";
499
500 if (!GetMetadataKey(metadata, "downloadUrl", &p.url)) {
501 return false;
502 }
503
504 AddAuthTokenHeader(&p.request_headers, auth_token_);
505
506 int32_t result = ReadUrl(this, p, output);
507 if (result != PP_OK) {
508 PostMessagef("log: Downloading failed with result %d\n", result);
509 return false;
510 }
511
512 return true;
513 }
514
515 bool Instance::GetMetadataKey(const Json::Value& metadata,
516 const char* key,
517 std::string* output) {
518 Json::Value value = metadata[key];
519 if (!value.isString()) {
520 PostMessagef("log: Expected metadata.%s to be a string.\n", key);
521 return false;
522 }
523
524 *output = value.asString();
525 return true;
526 }
527
528
529 class Module : public pp::Module {
530 public:
531 Module() : pp::Module() {}
532 virtual ~Module() {}
533
534 virtual pp::Instance* CreateInstance(PP_Instance instance) {
535 return new Instance(instance);
536 }
537 };
538
539 namespace pp {
540
541 Module* CreateModule() { return new ::Module(); }
542
543 } // namespace pp
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698