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// Tests PPB_URLRequestInfo interface.
6
7#include "ppapi/tests/test_url_request.h"
8
9#include <string.h>
10#include <string>
11
12#include "ppapi/c/ppb_file_io.h"
13#include "ppapi/cpp/completion_callback.h"
14#include "ppapi/cpp/file_io.h"
15#include "ppapi/cpp/file_ref.h"
16#include "ppapi/cpp/file_system.h"
17#include "ppapi/cpp/instance.h"
18#include "ppapi/cpp/var.h"
19#include "ppapi/tests/test_utils.h"
20#include "ppapi/tests/testing_instance.h"
21
22REGISTER_TEST_CASE(URLRequest);
23
24namespace {
25// TODO(polina): move these to test_case.h/cc since other NaCl tests use them?
26
27const PP_Resource kInvalidResource = 0;
28const PP_Instance kInvalidInstance = 0;
29
30// These should not exist.
31// The bottom 2 bits are used to differentiate between different id types.
32// 00 - module, 01 - instance, 10 - resource, 11 - var.
33const PP_Instance kNotAnInstance = 0xFFFFF0;
34const PP_Resource kNotAResource = 0xAAAAA0;
35}
36
37TestURLRequest::TestURLRequest(TestingInstance* instance)
38    : TestCase(instance),
39      ppb_url_request_interface_(NULL),
40      ppb_url_loader_interface_(NULL),
41      ppb_url_response_interface_(NULL),
42      ppb_core_interface_(NULL),
43      ppb_var_interface_(NULL) {
44}
45
46bool TestURLRequest::Init() {
47  ppb_url_request_interface_ = static_cast<const PPB_URLRequestInfo*>(
48      pp::Module::Get()->GetBrowserInterface(PPB_URLREQUESTINFO_INTERFACE));
49  ppb_url_loader_interface_ = static_cast<const PPB_URLLoader*>(
50      pp::Module::Get()->GetBrowserInterface(PPB_URLLOADER_INTERFACE));
51  ppb_url_response_interface_ = static_cast<const PPB_URLResponseInfo*>(
52      pp::Module::Get()->GetBrowserInterface(PPB_URLRESPONSEINFO_INTERFACE));
53  ppb_core_interface_ = static_cast<const PPB_Core*>(
54      pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
55  ppb_var_interface_ = static_cast<const PPB_Var*>(
56      pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE));
57  if (!ppb_url_request_interface_)
58    instance_->AppendError("PPB_URLRequestInfo interface not available");
59  if (!ppb_url_response_interface_)
60    instance_->AppendError("PPB_URLResponseInfo interface not available");
61  if (!ppb_core_interface_)
62    instance_->AppendError("PPB_Core interface not available");
63  if (!ppb_var_interface_)
64    instance_->AppendError("PPB_Var interface not available");
65  if (!ppb_url_loader_interface_) {
66    instance_->AppendError("PPB_URLLoader interface not available");
67  }
68  return EnsureRunningOverHTTP();
69}
70
71void TestURLRequest::RunTests(const std::string& filter) {
72  RUN_TEST(CreateAndIsURLRequestInfo, filter);
73  RUN_TEST(SetProperty, filter);
74  RUN_TEST(AppendDataToBody, filter);
75  RUN_TEST(AppendFileToBody, filter);
76  RUN_TEST(Stress, filter);
77}
78
79PP_Var TestURLRequest::PP_MakeString(const char* s) {
80  return ppb_var_interface_->VarFromUtf8(s, strlen(s));
81}
82
83// Tests
84//   PP_Resource Create(PP_Instance instance)
85//   PP_Bool IsURLRequestInfo(PP_Resource resource)
86std::string TestURLRequest::TestCreateAndIsURLRequestInfo() {
87  // Create: Invalid / non-existent instance -> invalid resource.
88  ASSERT_EQ(ppb_url_request_interface_->Create(kInvalidInstance),
89            kInvalidResource);
90  ASSERT_EQ(ppb_url_request_interface_->Create(kNotAnInstance),
91            kInvalidResource);
92
93  // Create: Valid instance -> valid resource.
94  PP_Resource url_request = ppb_url_request_interface_->Create(
95      instance_->pp_instance());
96  ASSERT_NE(url_request, kInvalidResource);
97
98  // IsURLRequestInfo:
99  // Invalid / non-existent / non-URLRequestInfo resource -> false.
100  ASSERT_NE(PP_TRUE,
101            ppb_url_request_interface_->IsURLRequestInfo(kInvalidResource));
102  ASSERT_NE(PP_TRUE,
103            ppb_url_request_interface_->IsURLRequestInfo(kNotAResource));
104
105  PP_Resource url_loader =
106      ppb_url_loader_interface_->Create(instance_->pp_instance());
107  ASSERT_NE(kInvalidResource, url_loader);
108
109  ASSERT_NE(PP_TRUE, ppb_url_request_interface_->IsURLRequestInfo(url_loader));
110  ppb_url_loader_interface_->Close(url_loader);
111  ppb_core_interface_->ReleaseResource(url_loader);
112
113  // IsURLRequestInfo: Current URLRequestInfo resource -> true.
114  std::string error;
115  if (PP_FALSE == ppb_url_request_interface_->IsURLRequestInfo(url_request))
116    error = "IsURLRequestInfo() failed with a current URLRequestInfo resource";
117
118  // IsURLRequestInfo: Released URLRequestInfo resource -> false.
119  ppb_core_interface_->ReleaseResource(url_request);
120  ASSERT_NE(PP_TRUE, ppb_url_request_interface_->IsURLRequestInfo(url_request));
121
122  return error;  // == PASS() if empty.
123}
124
125// Tests
126//  PP_Bool SetProperty(PP_Resource request,
127//                      PP_URLRequestProperty property,
128//                      struct PP_Var value);
129std::string TestURLRequest::TestSetProperty() {
130  struct PropertyTestData {
131    PropertyTestData(PP_URLRequestProperty prop,
132                     const std::string& name,
133                     PP_Var value, PP_Bool expected) :
134        property(prop), property_name(name),
135        var(value), expected_value(expected) {
136      // var has ref count of 1 on creation.
137    }
138    PP_URLRequestProperty property;
139    std::string property_name;
140    PP_Var var;  // Instance owner is responsible for releasing this var.
141    PP_Bool expected_value;
142  };
143
144  // All bool properties should accept PP_TRUE and PP_FALSE, while rejecting
145  // all other variable types.
146#define TEST_BOOL(_name)                                              \
147    PropertyTestData(ID_STR(_name), PP_MakeBool(PP_TRUE), PP_TRUE),   \
148    PropertyTestData(ID_STR(_name), PP_MakeBool(PP_FALSE), PP_TRUE),  \
149    PropertyTestData(ID_STR(_name), PP_MakeUndefined(), PP_FALSE),    \
150    PropertyTestData(ID_STR(_name), PP_MakeNull(), PP_FALSE),         \
151    PropertyTestData(ID_STR(_name), PP_MakeInt32(0), PP_FALSE),       \
152    PropertyTestData(ID_STR(_name), PP_MakeDouble(0.0), PP_FALSE)
153
154  // These property types are always invalid for string properties.
155#define TEST_STRING_INVALID(_name)                                    \
156    PropertyTestData(ID_STR(_name), PP_MakeNull(), PP_FALSE),         \
157    PropertyTestData(ID_STR(_name), PP_MakeBool(PP_FALSE), PP_FALSE), \
158    PropertyTestData(ID_STR(_name), PP_MakeInt32(0), PP_FALSE),       \
159    PropertyTestData(ID_STR(_name), PP_MakeDouble(0.0), PP_FALSE)
160
161#define TEST_INT_INVALID(_name)                                         \
162    PropertyTestData(ID_STR(_name), PP_MakeUndefined(), PP_FALSE),      \
163    PropertyTestData(ID_STR(_name), PP_MakeNull(), PP_FALSE),           \
164    PropertyTestData(ID_STR(_name), PP_MakeBool(PP_FALSE), PP_FALSE),   \
165    PropertyTestData(ID_STR(_name), PP_MakeString("notint"), PP_FALSE), \
166    PropertyTestData(ID_STR(_name), PP_MakeDouble(0.0), PP_FALSE)
167
168  // SetProperty accepts plenty of invalid values (malformed urls, negative
169  // thresholds, etc). Error checking is delayed until request opening (aka url
170  // loading).
171#define ID_STR(arg) arg, #arg
172    PropertyTestData test_data[] = {
173      TEST_BOOL(PP_URLREQUESTPROPERTY_STREAMTOFILE),
174      TEST_BOOL(PP_URLREQUESTPROPERTY_FOLLOWREDIRECTS),
175      TEST_BOOL(PP_URLREQUESTPROPERTY_RECORDDOWNLOADPROGRESS),
176      TEST_BOOL(PP_URLREQUESTPROPERTY_RECORDUPLOADPROGRESS),
177      TEST_BOOL(PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS),
178      TEST_BOOL(PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS),
179      TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_URL),
180      TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_METHOD),
181      TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_HEADERS),
182      TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL),
183      TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING),
184      TEST_STRING_INVALID(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT),
185      TEST_INT_INVALID(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD),
186      TEST_INT_INVALID(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD),
187      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL),
188                       PP_MakeString("http://www.google.com"), PP_TRUE),
189      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL),
190                       PP_MakeString("foo.jpg"), PP_TRUE),
191      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD),
192                       PP_MakeString("GET"), PP_TRUE),
193      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD),
194                       PP_MakeString("POST"), PP_TRUE),
195      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_HEADERS),
196                       PP_MakeString("Accept: text/plain"), PP_TRUE),
197      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_HEADERS),
198                       PP_MakeString(""), PP_TRUE),
199      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL),
200                       PP_MakeString("http://www.google.com"), PP_TRUE),
201      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL),
202                       PP_MakeString(""), PP_TRUE),
203      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_CUSTOMREFERRERURL),
204                       PP_MakeUndefined(), PP_TRUE),
205      PropertyTestData(
206          ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING),
207          PP_MakeString("base64"), PP_TRUE),
208      PropertyTestData(
209          ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING),
210          PP_MakeString(""), PP_TRUE),
211      PropertyTestData(
212          ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING),
213          PP_MakeUndefined(), PP_TRUE),
214      PropertyTestData(
215          ID_STR(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT),
216          PP_MakeString("My Crazy Plugin"), PP_TRUE),
217      PropertyTestData(
218          ID_STR(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT),
219          PP_MakeString(""), PP_TRUE),
220      PropertyTestData(
221          ID_STR(PP_URLREQUESTPROPERTY_CUSTOMUSERAGENT),
222          PP_MakeUndefined(), PP_TRUE),
223      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL),
224                       PP_MakeUndefined(), PP_FALSE),
225      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD),
226                       PP_MakeUndefined(), PP_FALSE),
227      PropertyTestData(
228          ID_STR(PP_URLREQUESTPROPERTY_HEADERS),
229          PP_MakeString("Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA=="),
230          PP_TRUE),
231      PropertyTestData(
232          ID_STR(PP_URLREQUESTPROPERTY_HEADERS),
233          PP_MakeString("Accept-Encoding: *\n"
234                        "Accept-Charset: iso-8859-5, unicode-1-1;q=0.8"),
235          PP_TRUE),
236      PropertyTestData(
237          ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD),
238          PP_MakeInt32(0), PP_TRUE),
239      PropertyTestData(
240          ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD),
241          PP_MakeInt32(100), PP_TRUE),
242      PropertyTestData(
243          ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD),
244          PP_MakeInt32(0), PP_TRUE),
245      PropertyTestData(
246          ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD),
247          PP_MakeInt32(100), PP_TRUE),
248      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_URL),
249                       PP_MakeString("::::::::::::"), PP_TRUE),
250      PropertyTestData(ID_STR(PP_URLREQUESTPROPERTY_METHOD),
251          PP_MakeString("INVALID"), PP_TRUE),
252      PropertyTestData(
253          ID_STR(PP_URLREQUESTPROPERTY_CUSTOMCONTENTTRANSFERENCODING),
254          PP_MakeString("invalid"), PP_TRUE),
255      PropertyTestData(
256          ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERUPPERTHRESHOLD),
257          PP_MakeInt32(-100), PP_TRUE),
258      PropertyTestData(
259          ID_STR(PP_URLREQUESTPROPERTY_PREFETCHBUFFERLOWERTHRESHOLD),
260          PP_MakeInt32(-100), PP_TRUE),
261
262    };
263  std::string error;
264
265  PP_Resource url_request = ppb_url_request_interface_->Create(
266      instance_->pp_instance());
267  if (url_request == kInvalidResource)
268    error = "Failed to create a URLRequestInfo";
269
270  // Loop over all test data even if we encountered an error to release vars.
271  for (size_t i = 0;
272       i < sizeof(test_data) / sizeof(test_data[0]);
273       ++i) {
274    if (error.empty() && test_data[i].expected_value !=
275        ppb_url_request_interface_->SetProperty(url_request,
276                                                test_data[i].property,
277                                                test_data[i].var)) {
278      pp::Var var(pp::Var::DontManage(), test_data[i].var);
279      error = std::string("Setting property ") +
280          test_data[i].property_name + " to " + var.DebugString() +
281          " did not return " + (test_data[i].expected_value ? "True" : "False");
282      error = test_data[i].property_name;
283    }
284    ppb_var_interface_->Release(test_data[i].var);
285  }
286
287  ppb_core_interface_->ReleaseResource(url_request);
288  return error;  // == PASS() if empty.
289}
290
291std::string TestURLRequest::LoadAndCompareBody(
292    PP_Resource url_request, const std::string& expected_body) {
293  TestCompletionCallback callback(instance_->pp_instance(), PP_REQUIRED);
294
295  PP_Resource url_loader =
296      ppb_url_loader_interface_->Create(instance_->pp_instance());
297  ASSERT_NE(kInvalidResource, url_loader);
298
299  callback.WaitForResult(ppb_url_loader_interface_->Open(
300      url_loader, url_request,
301      callback.GetCallback().pp_completion_callback()));
302  CHECK_CALLBACK_BEHAVIOR(callback);
303  ASSERT_EQ(PP_OK, callback.result());
304
305  std::string error;
306  PP_Resource url_response =
307      ppb_url_loader_interface_->GetResponseInfo(url_loader);
308  if (url_response == kInvalidResource) {
309    error = "PPB_URLLoader::GetResponseInfo() returned invalid resource";
310  } else {
311    PP_Var status = ppb_url_response_interface_->GetProperty(
312        url_response, PP_URLRESPONSEPROPERTY_STATUSCODE);
313    if (status.type != PP_VARTYPE_INT32 && status.value.as_int != 200)
314      error = ReportError("PPB_URLLoader::Open() status", status.value.as_int);
315
316    std::string actual_body;
317    for (; error.empty();) {  // Read the entire body in this loop.
318      const size_t kBufferSize = 32;
319      char buf[kBufferSize];
320      callback.WaitForResult(ppb_url_loader_interface_->ReadResponseBody(
321          url_loader, buf, kBufferSize,
322          callback.GetCallback().pp_completion_callback()));
323      if (callback.failed())
324        error.assign(callback.errors());
325      else if (callback.result() < PP_OK)
326        error.assign(ReportError("PPB_URLLoader::ReadResponseBody()",
327                                 callback.result()));
328      if (callback.result() <= PP_OK || callback.failed())
329        break;
330      actual_body.append(buf, callback.result());
331    }
332    if (actual_body != expected_body)
333      error = "PPB_URLLoader::ReadResponseBody() read unexpected response.";
334  }
335  ppb_core_interface_->ReleaseResource(url_response);
336
337  ppb_url_loader_interface_->Close(url_loader);
338  ppb_core_interface_->ReleaseResource(url_loader);
339  return error;
340}
341
342// Tests
343//   PP_Bool AppendDataToBody(
344//       PP_Resource request, const void* data, uint32_t len);
345std::string TestURLRequest::TestAppendDataToBody() {
346  PP_Resource url_request = ppb_url_request_interface_->Create(
347      instance_->pp_instance());
348  ASSERT_NE(url_request, kInvalidResource);
349
350  std::string postdata("sample postdata");
351  PP_Var post_string_var = PP_MakeString("POST");
352  PP_Var echo_string_var = PP_MakeString("/echo");
353
354  // NULL pointer causes a crash. In general PPAPI implementation does not
355  // test for NULL because they are just a special case of bad pointers that
356  // are not detectable if set to point to an object that does not exist.
357
358  // Invalid resource should fail.
359  ASSERT_EQ(PP_FALSE, ppb_url_request_interface_->AppendDataToBody(
360      kInvalidResource, postdata.data(), postdata.length()));
361
362  // Append data and POST to echoing web server.
363  ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty(
364      url_request, PP_URLREQUESTPROPERTY_METHOD, post_string_var));
365  ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty(
366      url_request, PP_URLREQUESTPROPERTY_URL, echo_string_var));
367
368  // Append data to body and verify the body is what we expect.
369  ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->AppendDataToBody(
370      url_request, postdata.data(), postdata.length()));
371  std::string error = LoadAndCompareBody(url_request, postdata);
372
373  ppb_var_interface_->Release(post_string_var);
374  ppb_var_interface_->Release(echo_string_var);
375  ppb_core_interface_->ReleaseResource(url_request);
376  return error;  // == PASS() if empty.
377}
378
379std::string TestURLRequest::TestAppendFileToBody() {
380  PP_Resource url_request = ppb_url_request_interface_->Create(
381      instance_->pp_instance());
382  ASSERT_NE(url_request, kInvalidResource);
383
384  TestCompletionCallback callback(instance_->pp_instance(), callback_type());
385
386  pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
387  callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
388  CHECK_CALLBACK_BEHAVIOR(callback);
389  ASSERT_EQ(PP_OK, callback.result());
390
391  pp::FileRef ref(file_system, "/test_file");
392  pp::FileIO io(instance_);
393  callback.WaitForResult(io.Open(ref,
394                                 PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_WRITE,
395                                 callback.GetCallback()));
396  CHECK_CALLBACK_BEHAVIOR(callback);
397  ASSERT_EQ(PP_OK, callback.result());
398
399  std::string append_data = "hello\n";
400  callback.WaitForResult(io.Write(0,
401                                  append_data.c_str(),
402                                  append_data.size(),
403                                  callback.GetCallback()));
404  CHECK_CALLBACK_BEHAVIOR(callback);
405  ASSERT_EQ(static_cast<int32_t>(append_data.size()), callback.result());
406
407  PP_Var post_string_var = PP_MakeString("POST");
408  PP_Var echo_string_var = PP_MakeString("/echo");
409
410  // NULL pointer causes a crash. In general PPAPI implementation does not
411  // test for NULL because they are just a special case of bad pointers that
412  // are not detectable if set to point to an object that does not exist.
413
414  // Invalid resource should fail.
415  ASSERT_EQ(PP_FALSE, ppb_url_request_interface_->AppendFileToBody(
416      kInvalidResource, ref.pp_resource(), 0, -1, 0));
417
418  // Append data and POST to echoing web server.
419  ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty(
420      url_request, PP_URLREQUESTPROPERTY_METHOD, post_string_var));
421  ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->SetProperty(
422      url_request, PP_URLREQUESTPROPERTY_URL, echo_string_var));
423
424  // Append file to body and verify the body is what we expect.
425  ASSERT_EQ(PP_TRUE, ppb_url_request_interface_->AppendFileToBody(
426      url_request, ref.pp_resource(), 0, -1, 0));
427  std::string error = LoadAndCompareBody(url_request, append_data);
428
429  ppb_var_interface_->Release(post_string_var);
430  ppb_var_interface_->Release(echo_string_var);
431  ppb_core_interface_->ReleaseResource(url_request);
432  return error;  // == PASS() if empty.
433}
434
435// Allocates and manipulates a large number of resources.
436std::string TestURLRequest::TestStress() {
437  const int kManyResources = 500;
438  PP_Resource url_request_info[kManyResources];
439
440  std::string error;
441  int num_created = kManyResources;
442  for (int i = 0; i < kManyResources; i++) {
443    url_request_info[i] = ppb_url_request_interface_->Create(
444        instance_->pp_instance());
445    if (url_request_info[i] == kInvalidResource) {
446      error = "Create() failed";
447    } else if (PP_FALSE == ppb_url_request_interface_->IsURLRequestInfo(
448        url_request_info[i])) {
449      error = "IsURLRequestInfo() failed";
450    } else if (PP_FALSE == ppb_url_request_interface_->SetProperty(
451        url_request_info[i],
452        PP_URLREQUESTPROPERTY_STREAMTOFILE,
453        PP_MakeBool(PP_FALSE))) {
454      error = "SetProperty() failed";
455    }
456    if (!error.empty()) {
457      num_created = i + 1;
458      break;
459    }
460  }
461  for (int i = 0; i < num_created; i++) {
462    ppb_core_interface_->ReleaseResource(url_request_info[i]);
463    if (PP_TRUE ==
464        ppb_url_request_interface_->IsURLRequestInfo(url_request_info[i]))
465      error = "IsURLREquestInfo() succeeded after release";
466  }
467  return error;  // == PASS() if empty.
468}
469