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