Initial source release: v2.0.8-0-679

Change-Id: Idf6316a8faf4b4fdc54265aad12084e5aa60707a
This commit is contained in:
Joey Parrish
2014-05-20 11:06:07 -07:00
parent 53846d38af
commit 66794025d4
87 changed files with 19864 additions and 0 deletions

73
core/test/base64_test.cpp Normal file
View File

@@ -0,0 +1,73 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include <utility>
#include "gtest/gtest.h"
#include "log.h"
#include "string_conversions.h"
namespace {
// Test vectors as suggested by http://tools.ietf.org/html/rfc4648#section-10
const std::string kNullString("");
const std::string kf("f");
const std::string kfo("fo");
const std::string kfoo("foo");
const std::string kfoob("foob");
const std::string kfooba("fooba");
const std::string kfoobar("foobar");
const std::string kfB64("Zg==");
const std::string kfoB64("Zm8=");
const std::string kfooB64("Zm9v");
const std::string kfoobB64("Zm9vYg==");
const std::string kfoobaB64("Zm9vYmE=");
const std::string kfoobarB64("Zm9vYmFy");
// Arbitrary clear test vectors
const std::string kMultipleOf24BitsData("Good day!");
const std::string kOneByteOverData("Hello Friend!");
const std::string kTwoBytesOverData("Hello Friend!!");
const std::string kTestData =
"\030\361\\\366\267> \331\210\360\\-\311:\324\256\376"
"\261\234\241\326d\326\177\346\346\223\333Y\305\214\330";
// Arbitrary encoded test vectors
const std::string kMultipleOf24BitsB64Data("R29vZCBkYXkh");
const std::string kOneByteOverB64Data("SGVsbG8gR29vZ2xlcg==");
const std::string kTwoBytesOverB64Data("SGVsbG8gR29vZ2xlcnM=");
const std::string kB64TestData = "GPFc9rc-INmI8FwtyTrUrv6xnKHWZNZ_5uaT21nFjNg=";
const std::pair<const std::string*, const std::string*> kBase64TestVectors[] = {
make_pair(&kNullString, &kNullString),
make_pair(&kf, &kfB64),
make_pair(&kfo, &kfoB64),
make_pair(&kfoo, &kfooB64),
make_pair(&kfoob, &kfoobB64),
make_pair(&kfooba, &kfoobaB64),
make_pair(&kfoobar, &kfoobarB64),
make_pair(&kMultipleOf24BitsData, &kMultipleOf24BitsB64Data),
make_pair(&kOneByteOverData, &kOneByteOverB64Data),
make_pair(&kTwoBytesOverData, &kTwoBytesOverB64Data),
make_pair(&kTestData, &kB64TestData),
};
} // unnamed namespace
namespace wvcdm {
class Base64EncodeDecodeTest : public ::testing::TestWithParam<
std::pair<const std::string*, const std::string*> > {};
TEST_P(Base64EncodeDecodeTest, EncodeDecodeTest) {
std::pair<const std::string*, const std::string*> values = GetParam();
std::vector<uint8_t> decoded_vector = Base64SafeDecode(values.second->data());
std::string decoded_string(decoded_vector.begin(), decoded_vector.end());
EXPECT_STREQ(values.first->data(), decoded_string.data());
std::string b64_string = Base64SafeEncode(decoded_vector);
EXPECT_STREQ(values.second->data(), b64_string.data());
}
INSTANTIATE_TEST_CASE_P(ExecutesBase64Test, Base64EncodeDecodeTest,
::testing::ValuesIn(kBase64TestVectors));
} // namespace wvcdm

View File

@@ -0,0 +1,324 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include <errno.h>
#include <getopt.h>
#if defined(CHROMIUM_BUILD)
#include "base/at_exit.h"
#include "base/message_loop/message_loop.h"
#endif
#include "cdm_engine.h"
#include "config_test_env.h"
#include "gtest/gtest.h"
#include "license_request.h"
#include "log.h"
#include "properties.h"
#include "scoped_ptr.h"
#include "string_conversions.h"
#include "url_request.h"
#include "wv_cdm_types.h"
namespace {
// Http OK response code.
const int kHttpOk = 200;
// Default license server, can be configured using --server command line option
// Default key id (pssh), can be configured using --keyid command line option
std::string g_client_auth;
wvcdm::KeyId g_key_id;
wvcdm::CdmKeySystem g_key_system;
std::string g_license_server;
std::string g_port;
wvcdm::KeyId g_wrong_key_id;
int g_use_full_path = 0; // cannot use boolean in getopt_long
// WHAT: This is an RSA certificate message from the provisioning server.
// The client sends this certificate to a license server for
// device authentication by the license server.
// WHY: This certificate is used to test the CDM engine's provisioning
// response handling.
static wvcdm::CdmProvisioningResponse kValidJsonProvisioningResponse =
"{\"signedResponse\": {"
"\"message\": \"CrAJYTyIdLPiA2jBzMskbE_gFQj69wv23VlJ2e3MBKtK4nJwKyNYGyyluqKo"
"TP751tvoADf86iLrf73mEzF58eSlaOjCpJRf2R3dojbNeSTy3JICmCc8vKtMjZRX9QWTvJbq_cg"
"yMB8FQC8enuYhOaw1yJDYyCFHgik34NrUVUfmvaKKdSKQimqAZmjXi6P0znAn-XdPtz2xJVRxZp"
"NH3QCD1bGcH_O1ercBW2JwF9KNalKFsxQrBhIwvyx-q-Ah4vf4r3M2HzY6JTHvcYGGc7dJNA3Xe"
"WfCrYIvg0SGCP_z7Y2wICIA36VMwR3gnwNZlKkx6WGCCgsaU6IbLm4HpRBZfajuiOlasoYN4z1R"
"lQ14Z32fdaFy8xOqLl-ZukxjWa7wv9zOSveH6JcHap1FS3R-RZ7E5WhfjxSTS0nWWZgmAjS2PkP"
"9g4GPNsnpsrVymI39j6R6jPoc3__2EGN6qAvmp4pFKR7lQyslgNn2vYLuE0Ps5mIXVkxNiZOO3T"
"jxgZyHaHOm1KmAZKI0EfddMATJCTt-UeLG3haqS_pYaBWcQ_xzWhoEHWU7_6ZaWrWemV8CVCg6s"
"OB1SRI5MrkRBBSV0r8UKddLJGthZVjuTG75KK72KE9yhe86mCadvfVYe5keJ5GOC-t1EiFzBo4c"
"4oqwkOCkkmYX_BEuZ3pOWztFp1_Br2Tl_fziw4O2vNIPCXB9yEewV6PkYPziTue3x4vRqD_mYjm"
"1ia8fxISQnEC0vrqvrFFs9fLAHPlsvaRFnhv_XKpRwFoBdfqWTakb3k6uRz0Oh2SJ8euzFIyQNB"
"efesMWk45DSrQjnlwlKXwZSiDKjAss0W2WwIb9F_x5LdB1Aa-CBudLVdxf62ggYaNZ57qx3YeHA"
"jkqMGIF7Fq09D4OxM0jRsnrmXbJWKleUpJi7nHJgQGZk2ifN95gjuTNcRaGfYXMOsDoWdkrNAq0"
"LScsPB06xEUR0DcO9vWx0zAEK7gsxxHziR7ZaYiIIkPysRR92r2NoLFPOUXf8j8ait-51jZmPKn"
"bD6adieLy6ujSl907QsUgyGvokLs1OCsYHZr-X6vnyMjdk4G3QfmWwRepD_CMyXGvtLbTNCto7E"
"L_M2yPZveAwYWwNlBtWK21gwIU2dgY298z7_S6jaQBc29f25sREjvN793ttYsPaeyom08qHYDnb"
"jae3XX-2qqde6AGXlv__jO8WDZ5od6DWu2ThqV10ijVGFfGniRsSruzq0iq8zuAqTOGhmA9Dw7b"
"rNlI95P4LpJA5pbjmNdnX7CQa2oHUuojmwlXRYuOA28PNEf-sc7ZPmMyFzedJi4EpkqzeQspEdH"
"yNMf23iEjK6GOff7dgAaxg9vYHyprhkEml4BdmFVYwCYQy8o6KRcA0NgJb8c3tg4d3aRXWp6L-F"
"sVhwqvq6FLOunSTNRIqhr2mOjRpU5w4mx-9GJRtk4XEcKT9YgUHGOUjGwfhQ5gBQDyZZVTddIUb"
"MOThsSg7zr38oUCfgXeZaai3X2foKo1Bt94Q_q18dw5xNAN5e7rSwfilltHL23zbZduuhWkvp8S"
"dag_NbO2C4IRMkzbjQBmiO9ixjXRhdqHlRRWcfR0wbQvEhD47egRVfnhKZ0W9G2-FGhyGuwJCq4"
"CCAISEAfZ_94TqpXBImeAUzYhNr0Y48SbiwUijgIwggEKAoIBAQDRigR9nFm4mfBUh1Y3SGyOcF"
"E-yK2NtfDiQe9l70KtkOeH4sB6MMB8g1QKPbUE8SBjPvXVJC_2DAWKjALzk4Aw-K-VmYe_Ag9CH"
"JiS-XcfUYEGgK4jVMxadEq3LufEEREKUZnzjgQlR39dzgjFqIrC1bwfy3_99RsjPt6QpWPg36PI"
"O4UKlmwBDTFzSOJB-4IV8Opy5Zv84BqPuyO9P5e3bXj_shRfy_XAGG2HGP_PpOCZWEfxuce0Iyu"
"vpTPLQpTOgNw-VvUBGCWMZFoERopmqp_pQwWZ2a-EwlT_vvYY4SkuNjflBskR70xz4QzEo9665g"
"k6I-HbHrTv29KEiAllAgMBAAEomSASgAIkKz1CSdFJVKcpO56jW0vsjKp92_cdqXBSEY3nuhzug"
"_LFluMJx_IqATUcCOY-w6w0yKn2ezfZGE0MDIaCngEgQFI_DRoaSOBNNeirF59uYM0sK3P2eGS9"
"G6F0l-OUXJdSO0b_LO8AbAK9LA3j7UHaajupJI1mdc4VtJfPRTsml2vIeKhDWXWaSvmeHgfF_tp"
"-OV7oPuk6Ub26xpCp2He2rEAblCYEl25Zlz97K4DhyTOV5_xuSdSt-KbTLY9cWM5i9ncND1RzCc"
"4qOixKarnMM5DdpZhs3B5xVj3yBAM1mVxPD2sZnqHSEN2EK7BMlHEnnyxhX0MGE36TQZR7P-I-G"
"rUFCq8CCAESEDAxMjM0NTY3ODlBQkNERUYYspIEIo4CMIIBCgKCAQEApwA2YGXcvVRaKkC04RWU"
"WBFPlFjd3qcfPCzgiAkpYVdnXlZ-7iePWTSaKqqdtE76p2rUyXpTwU6f4zT3PbfJEEdPKNo_zjF"
"7_QYQ6_e-kvmv-z5o2u4aZEzzKfJznjnY9m_YsoCCcY61pPLCPs0KyrYEzZoTi1RzVCVUjL6Yem"
"et2rNOs_qCqEpnmFZXVHHNEn_towHAaoskA5aIvpdmKrxTyYMGUVqIZRMY5Drta_FhW0zIHvTCr"
"gheLV_4En-i_LshGDDa_kD7AcouNw7O3XaHgkYLOnePwHIHLH-dHoZb7Scp3wOXYu9E01s925xe"
"G3s5tAttBGu7uyxfz7N6BQIDAQABKNKF2MwEEoADe9NAqNAxHpU13bMgz8LPySZJU8hY1RLwcfT"
"UM47Xb3m-F-s2cfI7w08668f79kD45uRRzkVc8GbRIlVyzVC0WgIvtxEkYRKfgF_J7snUe2J2NN"
"1FrkK7H3oYhcfPyYZH_SPZJr5HPoBFQTmS5A4l24U1dzQ6Z7_q-oS6uT0DiagTnzWhEg6AEnIkT"
"sJtK3cZuKGYq3NDefZ7nslPuLXxdXl6SAEOtrk-RvCY6EBqYOuPUXgxXOEPbyM289R6aHQyPPYw"
"qs9Pt9_E4BuMqCsbf5H5mLms9FA-wRx6mK2IaOboT4tf9_YObp3hVeL3WyxzXncETzJdE1GPGlO"
"t_x5S_MylgJKbiWQYSdmqs3fzYExunw3wvI4tPHT_O8A_xKjyTEAvE5cBuCkfjwT716qUOzFUzF"
"gZYLHnFiQLZekZUbUUlWY_CwU9Cv0UtxqQ6Oa835_Ug8_n1BwX6BPbmbcWe2Y19laSnDWg4JBNl"
"F2CyP9N75jPtW9rVfjUSqKEPOwaIgwzNDkyMjM3NDcAAAA=\","
"\"signature\": \"r-LpoZcbbr2KtoPaFnuWTVBh4Gup1k8vn0ClW2qm32A=\"}}";
} // namespace
namespace wvcdm {
class WvCdmEngineTest : public testing::Test {
public:
virtual void SetUp() {
cdm_engine_.OpenSession(g_key_system, NULL, &session_id_);
}
virtual void TearDown() {
cdm_engine_.CloseSession(session_id_);
}
protected:
void GenerateKeyRequest(const std::string& key_system,
const std::string& key_id) {
CdmAppParameterMap app_parameters;
std::string server_url;
std::string init_data = key_id;
CdmKeySetId key_set_id;
if (!Properties::extract_pssh_data()) {
EXPECT_TRUE(CdmEngine::ExtractWidevinePssh(key_id, &init_data));
}
EXPECT_EQ(KEY_MESSAGE,
cdm_engine_.GenerateKeyRequest(session_id_,
key_set_id,
init_data,
kLicenseTypeStreaming,
app_parameters,
&key_msg_,
&server_url));
}
void GenerateRenewalRequest(const std::string& key_system,
const std::string& init_data) {
EXPECT_EQ(KEY_MESSAGE,
cdm_engine_.GenerateRenewalRequest(session_id_,
&key_msg_,
&server_url_));
}
// posts a request and extracts the drm message from the response
std::string GetKeyRequestResponse(const std::string& server_url,
const std::string& client_auth) {
// Use secure connection and chunk transfer coding.
UrlRequest url_request(server_url + client_auth, g_port, true, true);
if (!url_request.is_connected()) {
return "";
}
url_request.PostRequest(key_msg_);
std::string response;
int resp_bytes = url_request.GetResponse(&response);
LOGD("response:\r\n%s", response.c_str());
LOGD("end %d bytes response dump", resp_bytes);
int status_code = url_request.GetStatusCode(response);
EXPECT_EQ(kHttpOk, status_code);
if (status_code != kHttpOk) {
return "";
} else {
std::string drm_msg;
LicenseRequest lic_request;
lic_request.GetDrmMessage(response, drm_msg);
LOGV("drm msg: %u bytes\r\n%s", drm_msg.size(),
HexEncode(reinterpret_cast<const uint8_t*>(drm_msg.data()),
drm_msg.size()).c_str());
return drm_msg;
}
}
void VerifyNewKeyResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data){
std::string resp = GetKeyRequestResponse(server_url,
client_auth);
CdmKeySetId key_set_id;
EXPECT_EQ(cdm_engine_.AddKey(session_id_, resp, &key_set_id), KEY_ADDED);
}
void VerifyRenewalKeyResponse(const std::string& server_url,
const std::string& client_auth,
std::string& init_data) {
std::string resp = GetKeyRequestResponse(server_url,
client_auth);
EXPECT_EQ(cdm_engine_.RenewKey(session_id_, resp), wvcdm::KEY_ADDED);
}
CdmEngine cdm_engine_;
std::string key_msg_;
std::string session_id_;
std::string server_url_;
};
TEST(WvCdmProvisioningTest, ProvisioningTest) {
CdmEngine cdm_engine;
CdmProvisioningRequest prov_request;
std::string provisioning_server_url;
cdm_engine.GetProvisioningRequest(&prov_request, &provisioning_server_url);
cdm_engine.HandleProvisioningResponse(kValidJsonProvisioningResponse);
}
TEST_F(WvCdmEngineTest, BaseMessageTest) {
GenerateKeyRequest(g_key_system, g_key_id);
GetKeyRequestResponse(g_license_server, g_client_auth);
}
TEST_F(WvCdmEngineTest, WrongMessageTest) {
std::string wrong_message = a2bs_hex(g_wrong_key_id);
GenerateKeyRequest(g_key_system, wrong_message);
// We should receive a response with no license, i.e. the extracted license
// response message should be empty.
ASSERT_EQ("", GetKeyRequestResponse(g_license_server, g_client_auth));
}
TEST_F(WvCdmEngineTest, NormalDecryption) {
GenerateKeyRequest(g_key_system, g_key_id);
VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id);
}
TEST_F(WvCdmEngineTest, LicenseRenewal) {
GenerateKeyRequest(g_key_system, g_key_id);
VerifyNewKeyResponse(g_license_server, g_client_auth, g_key_id);
GenerateRenewalRequest(g_key_system, g_key_id);
VerifyRenewalKeyResponse(server_url_.empty() ? g_license_server : server_url_,
g_client_auth,
g_key_id);
}
} // namespace wvcdm
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
wvcdm::InitLogging(argc, argv);
wvcdm::ConfigTestEnv config(wvcdm::kGoogleLicenseServerTest);
g_client_auth.assign(config.client_auth());
g_key_system.assign(config.key_system());
g_wrong_key_id.assign(config.wrong_key_id());
// The following variables are configurable through command line options.
g_license_server.assign(config.license_server());
g_key_id.assign(config.key_id());
g_port.assign(config.port());
std::string license_server(g_license_server);
int show_usage = 0;
static const struct option long_options[] = {
{ "use_full_path", no_argument, &g_use_full_path, 0 },
{ "keyid", required_argument, NULL, 'k' },
{ "port", required_argument, NULL, 'p' },
{ "server", required_argument, NULL, 's' },
{ "vmodule", required_argument, NULL, 0 },
{ "v", required_argument, NULL, 0 },
{ NULL, 0, NULL, '\0' }
};
int option_index = 0;
int opt = 0;
while ((opt = getopt_long(argc, argv, "k:p:s:u", long_options, &option_index)) != -1) {
switch (opt) {
case 'k': {
g_key_id.clear();
g_key_id.assign(optarg);
break;
}
case 'p': {
g_port.clear();
g_port.assign(optarg);
break;
}
case 's': {
g_license_server.clear();
g_license_server.assign(optarg);
break;
}
case 'u': {
g_use_full_path = 1;
break;
}
case '?': {
show_usage = 1;
break;
}
}
}
if (show_usage) {
std::cout << std::endl;
std::cout << "usage: " << argv[0] << " [options]" << std::endl << std::endl;
std::cout << " enclose multiple arguments in '' when using adb shell" << std::endl;
std::cout << " e.g. adb shell '" << argv[0] << " --server=\"url\"'" << std::endl << std::endl;
std::cout << std::setw(30) << std::left << " --port=<connection port>";
std::cout << "specifies the port number, in decimal format" << std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << g_port << std::endl;
std::cout << std::setw(30) << std::left << " --server=<server_url>";
std::cout << "configure the license server url, please include http[s] in the url" << std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << license_server << std::endl;
std::cout << std::setw(30) << std::left << " --keyid=<key_id>";
std::cout << "configure the key id or pssh, in hex format" << std::endl;
std::cout << std::setw(30) << std::left << " default keyid:";
std::cout << g_key_id << std::endl;
std::cout << std::setw(30) << std::left << " --use_full_path";
std::cout << "specify server url is not a proxy server" << std::endl;
std::cout << std::endl;
return 0;
}
std::cout << std::endl;
std::cout << "Server: " << g_license_server << std::endl;
std::cout << "Port: " << g_port << std::endl;
std::cout << "KeyID: " << g_key_id << std::endl << std::endl;
g_key_id = wvcdm::a2bs_hex(g_key_id);
config.set_license_server(g_license_server);
config.set_port(g_port);
config.set_key_id(g_key_id);
#if defined(CHROMIUM_BUILD)
base::AtExitManager exit;
base::MessageLoop ttr(base::MessageLoop::TYPE_IO);
#endif
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,108 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "config_test_env.h"
namespace {
// WHAT: URL of provisioning server (returned by GetProvisioningRequest())
const std::string kProductionProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// WHAT: URL of test provisioning server - This is a placeholder for
// an alternate provisioning server.
// WHY: request_license_test uses this url.
const std::string kProductionTestProvisioningServerUrl =
"https://www.googleapis.com/"
"certificateprovisioning/v1/devicecertificates/create"
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
// Google test License Server parameters
// This is a test server that has limited content and relatively
// open access controls. It is maintained for integration testing of
// CDM and OEMCrypto implementations.
// WHAT: URL for test license server.
const std::string kGgLicenseServer =
"http://widevine-proxy.appspot.com/proxy";
// WHAT: Test client authorization string.
// WHY: Needed to pass client info to server.
const std::string kGgClientAuth = "";
// WHAT: License info for test content. This is a valid
// license for test content, registered with the
// test license server.
// Video ID: CJTHyHGbKKA
// KeyId: 20ea8aa187aa50a7aa66723d7225c18f
// ContentID: 0894c7c8719b28a0
//
// SD Video KeyID (142-144): 20EA8AA187AA50A7AA66723D7225C18F
// HD Video KeyID (145,146): 1BEFCD2630185DFEA1A36E5C91C0BA63
// Audio KeyID (148,149,150): 812A73CD62A45336B22FD13D00102106
const std::string kGgKeyId =
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"08011210812A73CD62A45336B22FD13D00102106"; // pssh data
// WHAT: An invalid license id, expected to fail
const std::string kWrongKeyId =
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0901121094889920E8D6520098577DF8F2DD5546"; // pssh data
// Sample license server parameters
// NOTE: This data is not valid. It will be replaced with license
// server-specific data supplied by server administrators.
// WHAT: URL for license server.
const std::string kTtCpLicenseServer =
"http://widevine-proxy.appspot.com/invalid";
// WHAT: Client authorization string for license server request
// WHY: May be needed to pass client info to server.
const std::string kTtCpClientAuth = "";
// WHAT: License ID for test license server. This is not
// a valid license ID unless the ID is registered
// with the server. A valid license ID will be supplied
// by server administrators.
const std::string kTtCpKeyId =
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0801121030313233343536373839616263646566"; // pssh data
// WHAT: Table of license servers useable for testing.
// WHY: Allow testing against multiple license servers.
// Fields:
// id - enum for identifying/selecting this license server
// url - url of license server
// client_tag - default client authorization string
// key_id - license id for test license on this server
// port - http port
// use_chunked_transfer - server communication setting
// use_secure_transfer - server communication setting
const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
{ wvcdm::kGoogleLicenseServerTest, kGgLicenseServer,
kGgClientAuth, kGgKeyId, kDefaultHttpsPort, true, true },
{ wvcdm::kPartnerLicenseServer, kTtCpLicenseServer,
kTtCpClientAuth, kTtCpKeyId, kDefaultHttpPort, false, false }
};
} // namespace
namespace wvcdm {
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id)
: client_auth_(license_servers[server_id].client_tag),
key_id_(license_servers[server_id].key_id),
key_system_("com.widevine.alpha"),
license_server_(license_servers[server_id].url),
port_(license_servers[server_id].port),
provisioning_server_url_(kProductionProvisioningServerUrl),
provisioning_test_server_url_(kProductionTestProvisioningServerUrl),
use_chunked_transfer_(license_servers[server_id].use_chunked_transfer),
use_secure_transfer_(license_servers[server_id].use_secure_transfer),
wrong_key_id_(kWrongKeyId) {}
} // namespace wvcdm

View File

@@ -0,0 +1,80 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef CDM_TEST_CONFIG_TEST_ENV_H_
#define CDM_TEST_CONFIG_TEST_ENV_H_
#include <string>
#include "wv_cdm_types.h"
namespace {
const std::string kDefaultHttpsPort = "443";
const std::string kDefaultHttpPort = "80";
}
namespace wvcdm {
//WHAT: Index into table of alternate license servers (license_servers[]).
//WHY: Allow testing against multiple license server.
typedef enum {
kGoogleLicenseServerTest,
kPartnerLicenseServer
} LicenseServerId;
// Configures default test environment.
class ConfigTestEnv {
public:
typedef struct {
LicenseServerId id;
std::string url;
std::string client_tag;
std::string key_id;
std::string port;
bool use_chunked_transfer;
bool use_secure_transfer;
} LicenseServerConfiguration;
explicit ConfigTestEnv(LicenseServerId server_id);
~ConfigTestEnv() {};
const std::string& client_auth() const { return client_auth_; }
const KeyId& key_id() const { return key_id_; }
const CdmKeySystem& key_system() const { return key_system_; }
const std::string& license_server() const { return license_server_; }
const std::string& port() const { return port_; }
const std::string& provisioning_server_url() const {
return provisioning_server_url_;
}
const std::string& provisioning_test_server_url() const {
return provisioning_test_server_url_;
}
bool use_chunked_transfer() { return use_chunked_transfer_; }
bool use_secure_transfer() { return use_secure_transfer_; }
const KeyId& wrong_key_id() const { return wrong_key_id_; }
void set_key_id(KeyId& key_id) { key_id_.assign(key_id); }
void set_key_system(CdmKeySystem& key_system) {
key_system_.assign(key_system);
}
void set_license_server(std::string& license_server) {
license_server_.assign(license_server);
}
void set_port(std::string& port) { port_.assign(port); }
private:
std::string client_auth_;
KeyId key_id_;
CdmKeySystem key_system_;
std::string license_server_;
std::string port_;
std::string provisioning_server_url_;
std::string provisioning_test_server_url_;
bool use_chunked_transfer_;
bool use_secure_transfer_;
KeyId wrong_key_id_;
CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv);
};
}; // namespace wvcdm
#endif // CDM_TEST_CONFIG_TEST_ENV_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,266 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "device_files.h"
#include "file_store.h"
#include "gtest/gtest.h"
#include "properties.h"
#include "test_vectors.h"
namespace {
const std::string kTestDirName = "test_dir";
const std::string kTestFileName = "test.txt";
const std::string kTestFileName2 = "test2.txt";
const std::string kTestFileNameExt = ".txt";
const std::string kWildcard = "*";
} // namespace
namespace wvcdm {
class FileTest : public testing::Test {
protected:
virtual void SetUp() { CreateTestDir(); }
virtual void TearDown() { RemoveTestDir(); }
void CreateTestDir() {
File file;
if (!file.Exists(test_vectors::kTestDir)) {
EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir));
}
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
}
void RemoveTestDir() {
File file;
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
}
std::string GenerateRandomData(uint32_t len) {
std::string data(len, 0);
for (size_t i = 0; i < len; i++) {
data[i] = rand() % 256;
}
return data;
}
};
TEST_F(FileTest, FileExists) {
File file;
EXPECT_TRUE(file.Exists(test_vectors::kFileExists));
EXPECT_TRUE(file.Exists(test_vectors::kDirExists));
EXPECT_FALSE(file.Exists(test_vectors::kFileDoesNotExist));
EXPECT_FALSE(file.Exists(test_vectors::kDirDoesNotExist));
}
TEST_F(FileTest, CreateDirectory) {
File file;
std::string dir_wo_delimiter =
test_vectors::kTestDir.substr(0, test_vectors::kTestDir.size() - 1);
if (file.Exists(dir_wo_delimiter)) EXPECT_TRUE(file.Remove(dir_wo_delimiter));
EXPECT_FALSE(file.Exists(dir_wo_delimiter));
EXPECT_TRUE(file.CreateDirectory(dir_wo_delimiter));
EXPECT_TRUE(file.Exists(dir_wo_delimiter));
EXPECT_TRUE(file.Remove(dir_wo_delimiter));
EXPECT_TRUE(file.CreateDirectory(test_vectors::kTestDir));
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
}
TEST_F(FileTest, RemoveDir) {
File file;
EXPECT_TRUE(file.Remove(test_vectors::kTestDir));
EXPECT_FALSE(file.Exists(test_vectors::kTestDir));
}
TEST_F(FileTest, OpenFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File handle;
EXPECT_TRUE(handle.Remove(path));
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(handle.Exists(path));
}
TEST_F(FileTest, RemoveDirAndFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(file.Exists(path));
EXPECT_TRUE(file.Remove(path));
EXPECT_FALSE(file.Exists(path));
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(file.Exists(path));
RemoveTestDir();
EXPECT_FALSE(file.Exists(test_vectors::kTestDir));
EXPECT_FALSE(file.Exists(path));
}
TEST_F(FileTest, RemoveWildcardFiles) {
std::string path1 = test_vectors::kTestDir + kTestFileName;
std::string path2 = test_vectors::kTestDir + kTestFileName2;
std::string wildcard_path =
test_vectors::kTestDir + kWildcard + kTestFileNameExt;
File file;
EXPECT_TRUE(file.Open(path1, File::kCreate));
file.Close();
EXPECT_TRUE(file.Open(path2, File::kCreate));
file.Close();
EXPECT_TRUE(file.Exists(path1));
EXPECT_TRUE(file.Exists(path2));
EXPECT_TRUE(file.Remove(wildcard_path));
EXPECT_FALSE(file.Exists(path1));
EXPECT_FALSE(file.Exists(path2));
}
TEST_F(FileTest, IsDir) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(file.Exists(path));
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
EXPECT_FALSE(file.IsDirectory(path));
EXPECT_TRUE(file.IsDirectory(test_vectors::kTestDir));
}
TEST_F(FileTest, IsRegularFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
EXPECT_TRUE(file.Open(path, File::kCreate));
file.Close();
EXPECT_TRUE(file.Exists(path));
EXPECT_TRUE(file.Exists(test_vectors::kTestDir));
EXPECT_TRUE(file.IsRegularFile(path));
EXPECT_FALSE(file.IsRegularFile(test_vectors::kTestDir));
}
TEST_F(FileTest, FileSize) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
std::string write_data = GenerateRandomData(600);
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
EXPECT_TRUE(file.Exists(path));
EXPECT_EQ(static_cast<ssize_t>(write_data.size()), file.FileSize(path));
}
TEST_F(FileTest, WriteReadTextFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
std::string write_data = "This is a test";
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
EXPECT_TRUE(file.Exists(path));
std::string read_data;
read_data.resize(file.FileSize(path));
File rd_file;
EXPECT_TRUE(rd_file.Open(path, File::kReadOnly));
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
rd_file.Close();
EXPECT_EQ(write_data, read_data);
}
TEST_F(FileTest, WriteReadBinaryFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
std::string write_data = GenerateRandomData(600);
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
EXPECT_TRUE(file.Exists(path));
std::string read_data;
read_data.resize(file.FileSize(path));
File rd_file;
EXPECT_TRUE(rd_file.Open(path, File::kReadOnly));
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
rd_file.Close();
EXPECT_EQ(write_data, read_data);
}
TEST_F(FileTest, CopyFile) {
std::string path = test_vectors::kTestDir + kTestFileName;
File file;
file.Remove(path);
std::string write_data = GenerateRandomData(600);
File wr_file;
EXPECT_TRUE(wr_file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(wr_file.Write(write_data.data(), write_data.size()));
wr_file.Close();
ASSERT_TRUE(file.Exists(path));
std::string path_copy = test_vectors::kTestDir + kTestFileName2;
EXPECT_FALSE(file.Exists(path_copy));
EXPECT_TRUE(file.Copy(path, path_copy));
std::string read_data;
read_data.resize(file.FileSize(path_copy));
File rd_file;
EXPECT_TRUE(rd_file.Open(path_copy, File::kReadOnly));
EXPECT_TRUE(rd_file.Read(&read_data[0], read_data.size()));
rd_file.Close();
EXPECT_EQ(write_data, read_data);
EXPECT_EQ(file.FileSize(path), file.FileSize(path_copy));
}
TEST_F(FileTest, ListEmptyDirectory) {
std::vector<std::string> files;
File file;
EXPECT_TRUE(file.List(test_vectors::kTestDir, &files));
EXPECT_EQ(0u, files.size());
}
TEST_F(FileTest, ListFiles) {
File file;
std::string path = test_vectors::kTestDir + kTestDirName;
EXPECT_TRUE(file.CreateDirectory(path));
path = test_vectors::kTestDir + kTestFileName;
std::string write_data = GenerateRandomData(600);
EXPECT_TRUE(file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(file.Write(write_data.data(), write_data.size()));
file.Close();
EXPECT_TRUE(file.Exists(path));
path = test_vectors::kTestDir + kTestFileName2;
write_data = GenerateRandomData(600);
EXPECT_TRUE(file.Open(path, File::kCreate | File::kBinary));
EXPECT_TRUE(file.Write(write_data.data(), write_data.size()));
file.Close();
EXPECT_TRUE(file.Exists(path));
std::vector<std::string> files;
EXPECT_TRUE(file.List(test_vectors::kTestDir, &files));
EXPECT_EQ(3u, files.size());
for (size_t i = 0; i < files.size(); ++i) {
EXPECT_TRUE(files[i] == kTestDirName ||
files[i] == kTestFileName ||
files[i] == kTestFileName2);
}
}
} // namespace wvcdm

278
core/test/http_socket.cpp Normal file
View File

@@ -0,0 +1,278 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "http_socket.h"
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <string.h>
#include <sys/socket.h>
#include "log.h"
#include "openssl/bio.h"
#include "openssl/err.h"
#include "openssl/x509.h"
namespace wvcdm {
SSL_CTX* HttpSocket::InitSslContext(void) {
const SSL_METHOD* method;
SSL_CTX* ctx;
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
method = SSLv3_client_method();
ctx = SSL_CTX_new(method);
if (NULL == ctx) {
LOGE("failed to create SSL context");
}
return ctx;
}
void HttpSocket::ShowServerCertificate(const SSL* ssl) {
X509* cert;
char* line;
// gets the server certificate
cert = SSL_get_peer_certificate(ssl);
if (cert != NULL) {
LOGV("server certificate:");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
LOGV("subject: %s", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
LOGV("issuer: %s", line);
free(line);
X509_free(cert);
} else {
LOGE("Failed to get server certificate");
}
}
HttpSocket::HttpSocket()
: secure_connect_(true),
socket_fd_(-1),
ssl_(NULL),
ssl_ctx_(NULL),
timeout_enabled_(false) {
SSL_library_init();
}
HttpSocket::~HttpSocket() { CloseSocket(); }
void HttpSocket::CloseSocket() {
if (socket_fd_ != -1) {
close(socket_fd_);
socket_fd_ = -1;
}
if (secure_connect_) {
if (ssl_) {
SSL_free(ssl_);
ssl_ = NULL;
}
if (ssl_ctx_) {
CloseSslContext(ssl_ctx_);
ssl_ctx_ = NULL;
}
}
}
// Extracts the domain name and resource path from the input url parameter.
// The results are put in domain_name and resource_path respectively.
// The format of the url can begin with <protocol/scheme>:://domain server/...
// or dowmain server/resource_path
void HttpSocket::GetDomainNameAndPathFromUrl(const std::string& url,
std::string& domain_name,
std::string& resource_path) {
domain_name.clear();
resource_path.clear();
size_t start = url.find("//");
size_t end = url.npos;
if (start != url.npos) {
end = url.find("/", start + 2);
if (end != url.npos) {
domain_name.assign(url, start + 2, end - start - 2);
resource_path.assign(url, end + 1, url.npos);
} else {
domain_name.assign(url, start + 2, url.npos);
}
} else {
// no scheme/protocol in url
end = url.find("/");
if (end != url.npos) {
domain_name.assign(url, 0, end);
resource_path.assign(url, end + 1, url.npos);
} else {
domain_name.assign(url);
}
}
// strips port number if present, e.g. https://www.domain.com:8888/...
end = domain_name.find(":");
if (end != domain_name.npos) {
domain_name.erase(end);
}
}
bool HttpSocket::Connect(const char* url, const std::string& port,
bool enable_timeout, bool secure_connection) {
secure_connect_ = secure_connection;
if (secure_connect_) ssl_ctx_ = InitSslContext();
GetDomainNameAndPathFromUrl(url, domain_name_, resource_path_);
socket_fd_ = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd_ < 0) {
LOGE("cannot open socket %d", errno);
return false;
}
int reuse = 1;
if (setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) ==
-1) {
CloseSocket();
LOGE("setsockopt error %d", errno);
return false;
}
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo* addr_info = NULL;
bool status = true;
int ret = getaddrinfo(domain_name_.c_str(), port.c_str(), &hints, &addr_info);
if (ret != 0) {
CloseSocket();
LOGE("getaddrinfo failed with %d", ret);
status = false;
} else {
if (connect(socket_fd_, addr_info->ai_addr, addr_info->ai_addrlen) == -1) {
CloseSocket();
LOGE("cannot connect socket to %s, error=%d", domain_name_.c_str(),
errno);
status = false;
}
}
timeout_enabled_ = enable_timeout;
if (addr_info != NULL) {
freeaddrinfo(addr_info);
}
if (!status) return false;
// secures connection
if (secure_connect_ && ssl_ctx_) {
ssl_ = SSL_new(ssl_ctx_);
if (!ssl_) {
LOGE("failed SSL_new");
return false;
}
BIO* a_bio = BIO_new_socket(socket_fd_, BIO_NOCLOSE);
if (!a_bio) {
LOGE("BIO_new_socket error");
return false;
}
SSL_set_bio(ssl_, a_bio, a_bio);
int ret = SSL_connect(ssl_);
if (1 != ret) {
char buf[256];
LOGE("SSL_connect error:%s", ERR_error_string(ERR_get_error(), buf));
return false;
}
}
return true;
}
int HttpSocket::Read(char* data, int len) { return (Read(data, len, 0)); }
// makes non-blocking mode only during read, it supports timeout for read
// returns -1 for error, number of bytes read for success
int HttpSocket::Read(char* data, int len, int timeout_in_ms) {
bool use_timeout = (timeout_enabled_ && (timeout_in_ms > 0));
int original_flags = 0;
if (use_timeout) {
original_flags = fcntl(socket_fd_, F_GETFL, 0);
if (original_flags == -1) {
LOGE("fcntl error %d", errno);
return -1;
}
if (fcntl(socket_fd_, F_SETFL, original_flags | O_NONBLOCK) == -1) {
LOGE("fcntl error %d", errno);
return -1;
}
}
int total_read = 0;
int read = 0;
int to_read = len;
while (to_read > 0) {
if (use_timeout) {
fd_set read_fds;
struct timeval tv;
tv.tv_sec = timeout_in_ms / 1000;
tv.tv_usec = (timeout_in_ms % 1000) * 1000;
FD_ZERO(&read_fds);
FD_SET(socket_fd_, &read_fds);
if (select(socket_fd_ + 1, &read_fds, NULL, NULL, &tv) == -1) {
LOGE("select failed");
break;
}
if (!FD_ISSET(socket_fd_, &read_fds)) {
LOGD("socket read timeout");
break;
}
}
if (secure_connect_)
read = SSL_read(ssl_, data, to_read);
else
read = recv(socket_fd_, data, to_read, 0);
if (read > 0) {
to_read -= read;
data += read;
total_read += read;
} else if (read == 0) {
// in blocking mode, zero read mean's peer closed.
// in non-blocking mode, select said that there is data. so it should not
// happen
break;
} else {
LOGE("recv returned %d, error = %d", read, errno);
break;
}
}
if (use_timeout) {
fcntl(socket_fd_, F_SETFL, original_flags); // now blocking again
}
return total_read;
}
int HttpSocket::Write(const char* data, int len) {
int total_sent = 0;
int sent = 0;
int to_send = len;
while (to_send > 0) {
if (secure_connect_)
sent = SSL_write(ssl_, data, to_send);
else
sent = send(socket_fd_, data, to_send, 0);
if (sent > 0) {
to_send -= sent;
data += sent;
total_sent += sent;
} else if (sent == 0) {
usleep(10); // retry later
} else {
LOGE("send returned error %d", errno);
}
}
return total_sent;
}
} // namespace wvcdm

50
core/test/http_socket.h Normal file
View File

@@ -0,0 +1,50 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef CDM_TEST_HTTP_SOCKET_H_
#define CDM_TEST_HTTP_SOCKET_H_
#include <string>
#include "openssl/ssl.h"
#include "wv_cdm_types.h"
namespace wvcdm {
// Provides basic Linux based TCP socket interface.
class HttpSocket {
public:
HttpSocket();
~HttpSocket();
void CloseSocket();
bool Connect(const char* url, const std::string& port, bool enable_timeout,
bool secure_connection);
void GetDomainNameAndPathFromUrl(const std::string& url,
std::string& domain_name,
std::string& resource_path);
const std::string& domain_name() const { return domain_name_; };
const std::string& resource_path() const { return resource_path_; };
int Read(char* data, int len);
int Read(char* data, int len, int timeout_in_ms);
int Write(const char* data, int len);
private:
void CloseSslContext(SSL_CTX* ctx) const {
if (ctx) SSL_CTX_free(ctx);
}
SSL_CTX* InitSslContext(void);
void ShowServerCertificate(const SSL* ssl);
std::string domain_name_;
bool secure_connect_;
std::string resource_path_;
int socket_fd_;
SSL* ssl_;
SSL_CTX* ssl_ctx_;
bool timeout_enabled_;
CORE_DISALLOW_COPY_AND_ASSIGN(HttpSocket);
};
}; // namespace wvcdm
#endif // CDM_TEST_HTTP_SOCKET_H_

View File

@@ -0,0 +1,211 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include <errno.h>
#include "gtest/gtest.h"
#include "http_socket.h"
#include "log.h"
#include "string_conversions.h"
#include "url_request.h"
namespace {
// Random URL for tests.
const std::string kHttpsTestServer("https://www.google.com");
std::string gTestServer(kHttpsTestServer);
std::string gTestData("Hello");
const int kHttpBufferSize = 4096;
char gBuffer[kHttpBufferSize];
}
namespace wvcdm {
class HttpSocketTest : public testing::Test {
public:
HttpSocketTest() {}
~HttpSocketTest() { socket_.CloseSocket(); }
protected:
bool Connect(const std::string& server_url, bool secure_connection) {
std::string port = secure_connection ? "443" : "80";
if (socket_.Connect(server_url.c_str(), port, true, secure_connection)) {
LOGD("connected to %s", socket_.domain_name().c_str());
} else {
LOGE("failed to connect to %s", socket_.domain_name().c_str());
return false;
}
return true;
}
bool PostRequest(const std::string& data) {
std::string request("POST ");
if (socket_.resource_path().empty())
request.append(socket_.domain_name());
else
request.append(socket_.resource_path());
request.append(" HTTP/1.1\r\n");
request.append("Host: ");
request.append(socket_.domain_name());
request.append("\r\nUser-Agent: httpSocketTest/1.0\r\n");
request.append("Content-Length: ");
memset(gBuffer, 0, kHttpBufferSize);
snprintf(gBuffer, kHttpBufferSize, "%d\r\n", static_cast<int>(data.size()));
request.append(gBuffer);
request.append("Content-Type: multipart/form-data\r\n");
// newline terminates header
request.append("\r\n");
// append data
request.append(data);
socket_.Write(request.c_str(), request.size());
LOGD("request: %s", request.c_str());
return true;
}
bool GetResponse() {
int bytes = socket_.Read(gBuffer, kHttpBufferSize, 1000);
if (bytes < 0) {
LOGE("read error = ", errno);
return false;
} else {
LOGD("read %d bytes", bytes);
std::string response(gBuffer, bytes);
LOGD("response: %s", response.c_str());
LOGD("end response dump");
return true;
}
}
HttpSocket socket_;
std::string domain_name_;
std::string resource_path_;
};
TEST_F(HttpSocketTest, GetDomainNameAndPathFromUrlTest) {
socket_.GetDomainNameAndPathFromUrl(
"https://code.google.com/p/googletest/wiki/Primer", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl(
"http://code.google.com/p/googletest/wiki/Primer/", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("p/googletest/wiki/Primer/", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("http://code.google.com/", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("http://code.google.com", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl(
"code.google.com/p/googletest/wiki/Primer", domain_name_, resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("p/googletest/wiki/Primer", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("code.google.com", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("code.google.com/", domain_name_,
resource_path_);
EXPECT_STREQ("code.google.com", domain_name_.c_str());
EXPECT_STREQ("", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("", domain_name_, resource_path_);
EXPECT_TRUE(domain_name_.empty());
EXPECT_TRUE(resource_path_.empty());
// Test with random numeric URL
socket_.GetDomainNameAndPathFromUrl("http://10.11.12.13:8888/drm",
domain_name_, resource_path_);
EXPECT_STREQ("10.11.12.13", domain_name_.c_str());
EXPECT_STREQ("drm", resource_path_.c_str());
socket_.GetDomainNameAndPathFromUrl("http://10.11.12.13:8888", domain_name_,
resource_path_);
EXPECT_STREQ("10.11.12.13", domain_name_.c_str());
EXPECT_TRUE(resource_path_.empty());
}
TEST_F(HttpSocketTest, ConnectTest) {
const bool kUseSecureConnection = true;
if (gTestServer.find("https") != std::string::npos) {
EXPECT_TRUE(Connect(gTestServer, kUseSecureConnection));
socket_.CloseSocket();
// https connection allows insecure connection through port 80 as well
EXPECT_TRUE(Connect(gTestServer, !kUseSecureConnection));
socket_.CloseSocket();
} else {
EXPECT_TRUE(Connect(gTestServer, !kUseSecureConnection));
socket_.CloseSocket();
// Test for the case that non-https connection must not use port 443
EXPECT_FALSE(Connect(gTestServer, kUseSecureConnection));
socket_.CloseSocket();
}
EXPECT_FALSE(Connect("ww.g.c", kUseSecureConnection));
socket_.CloseSocket();
EXPECT_FALSE(Connect("ww.g.c", !kUseSecureConnection));
socket_.CloseSocket();
}
TEST_F(HttpSocketTest, RoundTripTest) {
int secure_connection =
(gTestServer.find("https") != std::string::npos) ? true : false;
ASSERT_TRUE(Connect(gTestServer, secure_connection));
EXPECT_TRUE(PostRequest(gTestData));
GetResponse();
socket_.CloseSocket();
}
} // namespace wvcdm
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
std::string temp;
std::string test_server(kHttpsTestServer);
std::string test_data(gTestData);
for (int i = 1; i < argc; i++) {
temp.assign(argv[i]);
if (temp.find("--server=") == 0) {
gTestServer.assign(temp.substr(strlen("--server=")));
} else if (temp.find("--data=") == 0) {
gTestData.assign(temp.substr(strlen("--data=")));
} else {
std::cout << "error: unknown option '" << argv[i] << "'" << std::endl;
std::cout << "usage: http_socket_test [options]" << std::endl
<< std::endl;
std::cout << std::setw(30) << std::left << " --server=<server_url>";
std::cout
<< "configure the test server url, please include http[s] in the url"
<< std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << test_server << std::endl;
std::cout << std::setw(30) << std::left << " --data=<data>";
std::cout << "configure data to send, in ascii string format"
<< std::endl;
std::cout << std::setw(30) << std::left << " ";
std::cout << "default: " << test_data << std::endl << std::endl;
return 0;
}
}
std::cout << std::endl;
std::cout << "Server: " << gTestServer << std::endl;
std::cout << "Data: " << gTestData << std::endl;
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,81 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "license_request.h"
#include "log.h"
namespace wvcdm {
static const std::string kTwoBlankLines("\r\n\r\n");
size_t LicenseRequest::FindHeaderEndPosition(
const std::string& response) const {
return(response.find(kTwoBlankLines));
}
// This routine parses the license server's response message and
// extracts the drm message from the response header.
void LicenseRequest::GetDrmMessage(const std::string& response,
std::string& drm_msg) {
if (response.empty()) {
drm_msg.clear();
return;
}
// Extracts DRM message.
// Content-Length = GLS line + Header(s) + empty line + drm message;
// we use the empty line to locate the drm message, and compute
// the drm message length as below instead of using Content-Length
size_t header_end_pos = FindHeaderEndPosition(response);
if (header_end_pos != std::string::npos) {
header_end_pos += kTwoBlankLines.size(); // points to response body
drm_msg.clear();
size_t drm_msg_pos = response.find(kTwoBlankLines, header_end_pos);
if (drm_msg_pos != std::string::npos) {
drm_msg_pos += kTwoBlankLines.size(); // points to drm message
} else {
// For backward compatibility, no blank line after error code
drm_msg_pos = response.find("\r\n", header_end_pos);
if (drm_msg_pos != std::string::npos) {
drm_msg_pos += 2; // points to drm message
}
}
if (drm_msg_pos != std::string::npos) {
drm_msg = response.substr(drm_msg_pos);
} else {
drm_msg = response.substr(header_end_pos);
}
} else {
LOGE("response body not found");
}
}
// Returns heartbeat url in heartbeat_url.
// The heartbeat url is stored as meta data in the response message.
void LicenseRequest::GetHeartbeatUrl(const std::string& response,
std::string& heartbeat_url) {
if (response.empty()) {
heartbeat_url.clear();
return;
}
size_t header_end_pos = FindHeaderEndPosition(response);
if (header_end_pos != std::string::npos) {
header_end_pos += kTwoBlankLines.size(); // points to response body
heartbeat_url.clear();
size_t heartbeat_url_pos = response.find("Heartbeat-Url: ",
header_end_pos);
if (heartbeat_url_pos != std::string::npos) {
heartbeat_url_pos += sizeof("Heartbeat-Url: ");
heartbeat_url.assign(response.substr(heartbeat_url_pos));
} else {
LOGE("heartbeat url not found");
}
} else {
LOGE("response body not found");
}
}
} // namespace wvcdm

View File

@@ -0,0 +1,30 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef CDM_TEST_LICENSE_REQUEST_H_
#define CDM_TEST_LICENSE_REQUEST_H_
#include <string>
#include "wv_cdm_types.h"
namespace wvcdm {
// Parses response from a license request.
// This class assumes a particular response format defined by
// Google license servers.
class LicenseRequest {
public:
LicenseRequest() {};
~LicenseRequest() {};
void GetDrmMessage(const std::string& response, std::string& drm_msg);
void GetHeartbeatUrl(const std::string& response, std::string& heartbeat_url);
private:
size_t FindHeaderEndPosition(const std::string& response) const;
CORE_DISALLOW_COPY_AND_ASSIGN(LicenseRequest);
};
}; // namespace wvcdm
#endif // CDM_TEST_LICENSE_REQUEST_H_

View File

@@ -0,0 +1,788 @@
// Copyright 2012 Google Inc. All Rights Reserved.
#include <sstream>
#include "clock.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "license.h"
#include "policy_engine.h"
#include "wv_cdm_constants.h"
namespace wvcdm {
//protobuf generated classes.
using video_widevine_server::sdk::License;
using video_widevine_server::sdk::License_Policy;
using video_widevine_server::sdk::LicenseIdentification;
using video_widevine_server::sdk::STREAMING;
using video_widevine_server::sdk::OFFLINE;
// gmock methods
using ::testing::Return;
using ::testing::AtLeast;
class MockClock : public Clock {
public:
MOCK_METHOD0(GetCurrentTime, int64_t());
};
class PolicyEngineTest : public ::testing::Test {
protected:
virtual void SetUp() {
mock_clock_ = new MockClock();
policy_engine_ = new PolicyEngine(mock_clock_);
license_start_time_ = 1413517500; // ~ 01/01/2013
license_renewal_delay_ = 604200; // 7 days - 10 minutes
license_renewal_retry_interval_ = 30;
license_duration_ = 604800; // 7 days
playback_duration_ = 86400; // 24 hours
license_.set_license_start_time(license_start_time_);
LicenseIdentification* id = license_.mutable_id();
id->set_version(1);
id->set_type(STREAMING);
License_Policy* policy = license_.mutable_policy();
policy = license_.mutable_policy();
policy->set_can_play(true);
policy->set_can_persist(true);
policy->set_can_renew(true);
policy->set_rental_duration_seconds(license_duration_);
policy->set_playback_duration_seconds(playback_duration_);
policy->set_license_duration_seconds(license_duration_);
policy->set_renewal_recovery_duration_seconds(license_duration_ -
license_renewal_delay_); // 10 minutes
// Note: not a real URL - used for testing Policy/PolicyEngine interfaces
policy->set_renewal_server_url(
"https://test.google.com/license/GetCencLicense");
policy->set_renewal_delay_seconds(license_renewal_delay_);
policy->set_renewal_retry_interval_seconds(
license_renewal_retry_interval_);
policy->set_renew_with_usage(false);
}
virtual void TearDown() {
delete policy_engine_;
// Done by policy engine: delete mock_clock_;
policy_engine_ = NULL;
mock_clock_ = NULL;
}
MockClock* mock_clock_;
PolicyEngine* policy_engine_;
License license_;
License_Policy* policy_;
int64_t license_start_time_;
int64_t license_renewal_delay_;
int64_t license_renewal_retry_interval_;
int64_t license_duration_;
int64_t playback_duration_;
};
TEST_F(PolicyEngineTest, NoLicense) {
EXPECT_FALSE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackSuccess) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 10));
policy_engine_->SetLicense(license_);
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackFailed_CanPlayFalse) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 5));
License_Policy* policy = license_.mutable_policy();
policy->set_can_play(false);
policy_engine_->SetLicense(license_);
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->BeginDecryption();
EXPECT_FALSE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackFails_RentalDurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 3600))
.WillOnce(Return(license_start_time_ + 3601));
License_Policy* policy = license_.mutable_policy();
policy->set_rental_duration_seconds(3600);
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
EXPECT_FALSE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackFails_PlaybackDurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 10000))
.WillOnce(Return(license_start_time_ + 13598))
.WillOnce(Return(license_start_time_ + 13602));
License_Policy* policy = license_.mutable_policy();
policy->set_playback_duration_seconds(3600);
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
EXPECT_FALSE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackFails_LicenseDurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 3600))
.WillOnce(Return(license_start_time_ + 3601));
License_Policy* policy = license_.mutable_policy();
policy->set_license_duration_seconds(3600);
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
EXPECT_FALSE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackOk_RentalDuration0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 3600))
.WillOnce(Return(license_start_time_ + 3601));
License_Policy* policy = license_.mutable_policy();
policy->set_rental_duration_seconds(0);
policy->set_license_duration_seconds(3600);
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
EXPECT_FALSE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackOk_PlaybackDuration0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 10000))
.WillOnce(Return(license_start_time_ + 10005))
.WillOnce(Return(license_start_time_ + 13598))
.WillOnce(Return(license_start_time_ + 13602));
License_Policy* policy = license_.mutable_policy();
policy->set_playback_duration_seconds(0);
policy->set_license_duration_seconds(3600);
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
EXPECT_FALSE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackOk_LicenseDuration0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 3600))
.WillOnce(Return(license_start_time_ + 3601));
License_Policy* policy = license_.mutable_policy();
policy->set_license_duration_seconds(0);
policy->set_rental_duration_seconds(3600);
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
EXPECT_FALSE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackOk_Durations0) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 604800))
.WillOnce(Return(license_start_time_ + 604810));
License_Policy* policy = license_.mutable_policy();
policy->set_rental_duration_seconds(0);
policy->set_playback_duration_seconds(0);
policy->set_license_duration_seconds(0);
policy->set_renewal_delay_seconds(604900);
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackFailed_CanRenewFalse) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
.WillOnce(Return(license_start_time_ + license_duration_ + 10));
License_Policy* policy = license_.mutable_policy();
policy->set_can_renew(false);
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
EXPECT_FALSE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccess) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 15))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ +
license_renewal_retry_interval_ + 10));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
license_.set_license_start_time(license_start_time_ +
license_renewal_delay_ + 15);
LicenseIdentification* id = license_.mutable_id();
id->set_version(2);
policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackFailed_RenewFailedVersionNotUpdated) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40))
.WillOnce(Return(license_start_time_ + license_duration_ + 10));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
license_.set_license_start_time(license_start_time_ +
license_renewal_delay_ + 15);
policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
EXPECT_FALSE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackFailed_RepeatedRenewFailures) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 70))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 80))
.WillOnce(Return(license_start_time_ + license_duration_ + 15));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
EXPECT_FALSE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterExpiry) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 70))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 80))
.WillOnce(Return(license_start_time_ + license_duration_ + 10))
.WillOnce(Return(license_start_time_ + license_duration_ + 30))
.WillOnce(Return(license_start_time_ + license_duration_ + 40));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_EXPIRED_EVENT, event);
EXPECT_FALSE(policy_engine_->can_decrypt());
license_.set_license_start_time(license_start_time_ +
license_duration_ + 20);
LicenseIdentification* id = license_.mutable_id();
id->set_version(2);
License_Policy* policy = license_.mutable_policy();
policy = license_.mutable_policy();
policy->set_playback_duration_seconds(playback_duration_ + 100);
policy->set_license_duration_seconds(license_duration_ + 100);
policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackOk_RenewSuccessAfterFailures) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + license_duration_ -
playback_duration_ + 1))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ - 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 10))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 20))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 40))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 50))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 55))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 67))
.WillOnce(Return(license_start_time_ + license_renewal_delay_ + 200));
policy_engine_->SetLicense(license_);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
license_.set_license_start_time(license_start_time_ +
license_renewal_delay_ + 55);
LicenseIdentification* id = license_.mutable_id();
id->set_version(2);
policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, PlaybackOk_RenewedWithUsage) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 10))
.WillOnce(Return(license_start_time_ + 20))
.WillOnce(Return(license_start_time_ + 40))
.WillOnce(Return(license_start_time_ + 50));
License_Policy* policy = license_.mutable_policy();
policy->set_renew_with_usage(true);
policy_engine_->SetLicense(license_);
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->BeginDecryption();
EXPECT_FALSE(policy_engine_->can_decrypt());
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_TRUE(event_occurred);
EXPECT_EQ(LICENSE_RENEWAL_NEEDED_EVENT, event);
license_.set_license_start_time(license_start_time_ + 30);
policy->set_renew_with_usage(false);
LicenseIdentification* id = license_.mutable_id();
id->set_version(2);
policy_engine_->UpdateLicense(license_);
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
EXPECT_TRUE(policy_engine_->can_decrypt());
}
TEST_F(PolicyEngineTest, QueryFailed_LicenseNotReceived) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_));
CdmQueryMap query_info;
EXPECT_EQ(UNKNOWN_ERROR, policy_engine_->Query(&query_info));
}
TEST_F(PolicyEngineTest, QuerySuccess) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 100));
License_Policy* policy = license_.mutable_policy();
policy_engine_->SetLicense(license_);
CdmQueryMap query_info;
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]);
int64_t remaining_time;
std::istringstream ss;
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_LT(0, remaining_time);
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_LT(0, remaining_time);
EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL],
policy->renewal_server_url());
}
TEST_F(PolicyEngineTest, QuerySuccess_Offline) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 100));
LicenseIdentification* id = license_.mutable_id();
id->set_type(OFFLINE);
License_Policy* policy = license_.mutable_policy();
policy->set_can_play(false);
policy->set_can_persist(false);
policy->set_can_renew(false);
policy_engine_->SetLicense(license_);
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->BeginDecryption();
EXPECT_FALSE(policy_engine_->can_decrypt());
CdmQueryMap query_info;
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]);
EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PLAY_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_RENEW_ALLOWED]);
int64_t remaining_time;
std::istringstream ss;
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(0, remaining_time);
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(0, remaining_time);
EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL],
policy->renewal_server_url());
}
TEST_F(PolicyEngineTest, QuerySuccess_DurationExpired) {
EXPECT_CALL(*mock_clock_, GetCurrentTime())
.WillOnce(Return(license_start_time_ + 1))
.WillOnce(Return(license_start_time_ + 5))
.WillOnce(Return(license_start_time_ + 10))
.WillOnce(Return(license_start_time_ + license_duration_ + 20));
LicenseIdentification* id = license_.mutable_id();
id->set_type(OFFLINE);
License_Policy* policy = license_.mutable_policy();
policy_engine_->SetLicense(license_);
bool event_occurred;
CdmEventType event;
policy_engine_->OnTimerEvent(&event_occurred, &event);
EXPECT_FALSE(event_occurred);
policy_engine_->BeginDecryption();
EXPECT_TRUE(policy_engine_->can_decrypt());
CdmQueryMap query_info;
EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info));
EXPECT_EQ(QUERY_VALUE_OFFLINE, query_info[QUERY_KEY_LICENSE_TYPE]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PERSIST_ALLOWED]);
EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]);
int64_t remaining_time;
std::istringstream ss;
ss.str(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(0, remaining_time);
ss.str(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING]);
ss >> remaining_time;
EXPECT_EQ(0, remaining_time);
EXPECT_EQ(query_info[QUERY_KEY_RENEWAL_SERVER_URL],
policy->renewal_server_url());
}
} // wvcdm

View File

@@ -0,0 +1,54 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "gtest/gtest.h"
#include "timer.h"
namespace wvcdm {
class TestTimerHandler : public TimerHandler {
public:
TestTimerHandler() : timer_events_(0) {};
virtual ~TestTimerHandler() {};
virtual void OnTimerEvent() {
timer_events_++;
}
uint32_t timer_events() { return timer_events_; }
void ResetTimerEvents() { timer_events_ = 0; }
private:
uint32_t timer_events_;
};
TEST(TimerTest, ParametersCheck) {
Timer timer;
EXPECT_FALSE(timer.Start(NULL, 10));
TestTimerHandler handler;
EXPECT_FALSE(timer.Start(&handler, 0));
}
TEST(TimerTest, TimerCheck) {
TestTimerHandler handler;
Timer timer;
uint32_t duration = 10;
EXPECT_EQ(0u, handler.timer_events());
EXPECT_FALSE(timer.IsRunning());
EXPECT_TRUE(timer.Start(&handler, 1));
EXPECT_TRUE(timer.IsRunning());
sleep(duration);
EXPECT_LE(duration-1, handler.timer_events());
EXPECT_LE(handler.timer_events(), duration+1);
timer.Stop();
EXPECT_FALSE(timer.IsRunning());
sleep(duration);
EXPECT_LE(duration-1, handler.timer_events());
EXPECT_LE(handler.timer_events(), duration+1);
}
}

218
core/test/url_request.cpp Normal file
View File

@@ -0,0 +1,218 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#include "url_request.h"
#include <errno.h>
#include <sstream>
#include "http_socket.h"
#include "log.h"
#include "string_conversions.h"
namespace {
const int kMaxReadAttempts = 4;
const int kSingleReadAttempt = 1;
} // namespace
namespace wvcdm {
UrlRequest::UrlRequest(const std::string& url, const std::string& port,
bool secure_connection, bool chunk_transfer_mode)
: chunk_transfer_mode_(chunk_transfer_mode),
is_connected_(false),
port_("80"),
request_(""),
server_url_(url) {
if (!port.empty()) {
port_.assign(port);
}
if (socket_.Connect((server_url_).c_str(), port_, true, secure_connection)) {
is_connected_ = true;
} else {
LOGE("failed to connect to %s, port=%s", socket_.domain_name().c_str(),
port.c_str());
}
}
UrlRequest::~UrlRequest() { socket_.CloseSocket(); }
void UrlRequest::AppendChunkToUpload(const std::string& data) {
// format of chunk:
// size of chunk in hex\r\n
// data\r\n
// . . .
// 0\r\n
// buffer to store length of chunk
memset(buffer_, 0, kHttpBufferSize);
snprintf(buffer_, kHttpBufferSize, "%zx\r\n", data.size());
request_.append(buffer_); // appends size of chunk
LOGD("...\r\n%s", request_.c_str());
request_.append(data);
request_.append("\r\n"); // marks end of data
}
// Concatenate all chunks into one blob and returns the response with
// header information.
void UrlRequest::ConcatenateChunkedResponse(const std::string http_response,
std::string* modified_response) {
if (http_response.empty()) return;
modified_response->clear();
const std::string kChunkedTag = "Transfer-Encoding: chunked\r\n\r\n";
size_t chunked_tag_pos = http_response.find(kChunkedTag);
if (std::string::npos != chunked_tag_pos) {
// processes chunked encoding
size_t chunk_size = 0;
size_t chunk_size_pos = chunked_tag_pos + kChunkedTag.size();
sscanf(&http_response[chunk_size_pos], "%zx", &chunk_size);
if (chunk_size > http_response.size()) {
// precaution, in case we misread chunk size
LOGE("invalid chunk size %u", chunk_size);
return;
}
// Search for chunks in the following format:
// header
// chunk size\r\n <-- chunk_size_pos @ beginning of chunk size
// chunk data\r\n <-- chunk_pos @ beginning of chunk data
// chunk size\r\n
// chunk data\r\n
// 0\r\n
const std::string kCrLf = "\r\n";
size_t chunk_pos = http_response.find(kCrLf, chunk_size_pos);
modified_response->assign(http_response, 0, chunk_size_pos);
while ((chunk_size > 0) && (std::string::npos != chunk_pos)) {
chunk_pos += kCrLf.size();
modified_response->append(http_response, chunk_pos, chunk_size);
// Search for next chunk
chunk_size_pos = chunk_pos + chunk_size + kCrLf.size();
sscanf(&http_response[chunk_size_pos], "%zx", &chunk_size);
if (chunk_size > http_response.size()) {
// precaution, in case we misread chunk size
LOGE("invalid chunk size %u", chunk_size);
break;
}
chunk_pos = http_response.find(kCrLf, chunk_size_pos);
}
} else {
// Response is not chunked encoded
modified_response->assign(http_response);
}
}
int UrlRequest::GetResponse(std::string* message) {
message->clear();
std::string response;
const int kTimeoutInMs = 3000;
int bytes = 0;
for (int attempts = kMaxReadAttempts; attempts > 0; --attempts) {
memset(buffer_, 0, kHttpBufferSize);
bytes = socket_.Read(buffer_, kHttpBufferSize, kTimeoutInMs);
if (bytes > 0) {
response.append(buffer_, bytes);
if (bytes < static_cast<int>(kHttpBufferSize)) {
attempts = kSingleReadAttempt;
}
} else {
if (bytes < 0) LOGE("read error = ", errno);
// bytes == 0 indicates nothing to read
}
}
ConcatenateChunkedResponse(response, message);
LOGD("HTTP response: (%d): %s", message->size(), b2a_hex(*message).c_str());
return message->size();
}
int UrlRequest::GetStatusCode(const std::string& response) {
const std::string kHttpVersion("HTTP/1.1");
int status_code = -1;
size_t pos = response.find(kHttpVersion);
if (pos != std::string::npos) {
pos += kHttpVersion.size();
sscanf(response.substr(pos).c_str(), "%d", &status_code);
}
return status_code;
}
bool UrlRequest::PostRequestChunk(const std::string& data) {
request_.assign("POST /");
request_.append(socket_.resource_path());
request_.append(" HTTP/1.1\r\n");
request_.append("Host: ");
request_.append(socket_.domain_name());
request_.append("\r\nConnection: Keep-Alive\r\n");
request_.append("Transfer-Encoding: chunked\r\n");
request_.append("User-Agent: Widevine CDM v1.0\r\n");
request_.append("Accept-Encoding: gzip,deflate\r\n");
request_.append("Accept-Language: en-us,fr\r\n");
request_.append("Accept-Charset: iso-8859-1,*,utf-8\r\n");
request_.append("\r\n"); // empty line to terminate header
// calls AppendChunkToUpload repeatedly for multiple chunks
AppendChunkToUpload(data);
// terminates last chunk with 0\r\n, then ends header with an empty line
request_.append("0\r\n\r\n");
socket_.Write(request_.c_str(), request_.size());
return true;
}
bool UrlRequest::PostRequest(const std::string& data) {
if (chunk_transfer_mode_) {
return PostRequestChunk(data);
}
request_.assign("POST /");
request_.append(socket_.resource_path());
request_.append(" HTTP/1.1\r\n");
request_.append("Host: ");
request_.append(socket_.domain_name());
request_.append("\r\nConnection: Keep-Alive\r\n");
request_.append("User-Agent: Widevine CDM v1.0\r\n");
request_.append("Accept-Encoding: gzip,deflate\r\n");
request_.append("Accept-Language: en-us,fr\r\n");
request_.append("Accept-Charset: iso-8859-1,*,utf-8\r\n");
std::ostringstream ss;
ss << data.size();
request_.append("Content-Length: ");
request_.append(ss.str());
request_.append("\r\n\r\n");
request_.append(data);
// terminates with \r\n, then ends with an empty line
request_.append("\r\n\r\n");
socket_.Write(request_.c_str(), request_.size());
LOGD("HTTP request: (%d): %s", request_.size(), request_.c_str());
LOGD("HTTP request: (%d): %s", request_.size(), b2a_hex(request_).c_str());
return true;
}
bool UrlRequest::PostCertRequestInQueryString(const std::string& data) {
request_.assign("POST /");
request_.append(socket_.resource_path());
request_.append("&signedRequest=");
request_.append(data);
request_.append(" HTTP/1.1\r\n");
request_.append("User-Agent: Widevine CDM v1.0\r\n");
request_.append("Host: ");
request_.append(socket_.domain_name());
request_.append("\r\nAccept: */*");
request_.append("\r\nContent-Type: application/json");
request_.append("\r\nContent-Length: 0");
request_.append("\r\n"); // empty line to terminate header
request_.append("\r\n"); // terminates the request
socket_.Write(request_.c_str(), request_.size());
LOGD("HTTP request: (%d): %s", request_.size(), request_.c_str());
LOGD("HTTP request: (%d): %s", request_.size(), b2a_hex(request_).c_str());
return true;
}
} // namespace wvcdm

45
core/test/url_request.h Normal file
View File

@@ -0,0 +1,45 @@
// Copyright 2013 Google Inc. All Rights Reserved.
#ifndef CDM_TEST_URL_REQUEST_H_
#define CDM_TEST_URL_REQUEST_H_
#include <string>
#include "http_socket.h"
#include "wv_cdm_types.h"
namespace wvcdm {
// Provides simple HTTP request and response service.
// Only POST request method is implemented.
class UrlRequest {
public:
UrlRequest(const std::string& url, const std::string& port,
bool secure_connect, bool chunk_transfer_mode);
~UrlRequest();
void AppendChunkToUpload(const std::string& data);
void ConcatenateChunkedResponse(const std::string http_response,
std::string* modified_response);
int GetResponse(std::string* message);
int GetStatusCode(const std::string& response);
bool is_connected() const { return is_connected_; }
bool PostRequest(const std::string& data);
bool PostRequestChunk(const std::string& data);
bool PostCertRequestInQueryString(const std::string& data);
private:
static const unsigned int kHttpBufferSize = 4096;
char buffer_[kHttpBufferSize];
bool chunk_transfer_mode_;
bool is_connected_;
std::string port_;
std::string request_;
HttpSocket socket_;
std::string server_url_;
CORE_DISALLOW_COPY_AND_ASSIGN(UrlRequest);
};
}; // namespace wvcdm
#endif // CDM_TEST_URL_REQUEST_H_