Source release v2.1.2-0-773 + third_party libs

Change-Id: Ia07608577b65b301c22a8ff4bf7f743c2d3f9274
This commit is contained in:
Joey Parrish
2014-06-10 13:36:59 -07:00
parent 557c42130a
commit aaa3c6192a
42 changed files with 2425 additions and 918 deletions

View File

@@ -33,7 +33,6 @@ wvcdm::KeyId g_key_id_pssh;
wvcdm::KeyId g_key_id_unwrapped;
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
@@ -138,16 +137,16 @@ class WvCdmEngineTest : public testing::Test {
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);
UrlRequest url_request(server_url + client_auth);
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);
bool ok = url_request.GetResponse(&response);
LOGD("response: %s\n", response.c_str());
EXPECT_TRUE(ok);
int status_code = url_request.GetStatusCode(response);
EXPECT_EQ(kHttpOk, status_code);
@@ -254,14 +253,12 @@ int main(int argc, char **argv) {
// The following variables are configurable through command line options.
g_license_server.assign(config.license_server());
g_key_id_pssh.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 },
@@ -277,11 +274,6 @@ int main(int argc, char **argv) {
g_key_id_pssh.assign(optarg);
break;
}
case 'p': {
g_port.clear();
g_port.assign(optarg);
break;
}
case 's': {
g_license_server.clear();
g_license_server.assign(optarg);
@@ -304,11 +296,6 @@ int main(int argc, char **argv) {
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 << " ";
@@ -327,12 +314,10 @@ int main(int argc, char **argv) {
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_pssh << std::endl << std::endl;
g_key_id_pssh = wvcdm::a2bs_hex(g_key_id_pssh);
config.set_license_server(g_license_server);
config.set_port(g_port);
config.set_key_id(g_key_id_pssh);
// Extract the key ID from the PSSH box.

View File

@@ -3,14 +3,23 @@
#include "config_test_env.h"
namespace {
const std::string kWidevineKeySystem = "com.widevine.alpha";
// Youtube Content Protection license server data
const std::string kYtCpLicenseServer =
"http://wv-ref-eme-player.appspot.com/proxy";
const std::string kYtCpClientAuth = "";
const std::string kYtCpKeyId =
"000000347073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000014" // Widevine system id
"0801121030313233343536373839616263646566"; // pssh data
"000000427073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000022" // Widevine system id
"08011a0d7769646576696e655f7465737422" // pssh data (streaming)
"0f73747265616d696e675f636c697031";
const std::string kYtCpOfflineKeyId =
"000000407073736800000000" // blob size and pssh
"EDEF8BA979D64ACEA3C827DCD51D21ED00000020" // Widevine system id
"08011a0d7769646576696e655f7465737422" //pssh data (offline)
"0d6f66666c696e655f636c697031";
// Youtube license server data
const std::string kYtLicenseServer =
@@ -34,6 +43,13 @@ const std::string kGpLicenseServer =
const std::string kGpClientAuth =
"?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine";
const std::string kGpClientOfflineQueryParameters =
"&offline=true";
const std::string kGpClientOfflineRenewalQueryParameters =
"&offline=true&renewal=true";
const std::string kGpClientOfflineReleaseQueryParameters =
"&offline=true&release=true";
const std::string kGpKeyId =
"000000347073736800000000" // blob size and pssh
"edef8ba979d64acea3c827dcd51d21ed00000014" // Widevine system id
@@ -63,23 +79,49 @@ const std::string kServerSdkLicenseServer =
const wvcdm::ConfigTestEnv::LicenseServerConfiguration license_servers[] = {
{ wvcdm::kGooglePlayServer, kGpLicenseServer, kGpClientAuth, kGpKeyId,
kDefaultHttpsPort, true, true },
kGpKeyId },
{ wvcdm::kYouTubeContentProtectionServer, kYtCpLicenseServer,
kYtCpClientAuth, kYtCpKeyId, kDefaultHttpPort, false, false }
kYtCpClientAuth, kYtCpKeyId, kYtCpOfflineKeyId }
};
} // 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),
use_chunked_transfer_(license_servers[server_id].use_chunked_transfer),
use_secure_transfer_(license_servers[server_id].use_secure_transfer),
wrong_key_id_(kWrongKeyId) {}
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id) {
Init(server_id);
}
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming) {
Init(server_id);
if (!streaming)
key_id_ = license_servers[server_id].offline_key_id;
}
ConfigTestEnv::ConfigTestEnv(LicenseServerId server_id, bool streaming,
bool renew, bool release) {
Init(server_id);
if (!streaming) {
key_id_ = license_servers[server_id].offline_key_id;
if (wvcdm::kGooglePlayServer == server_id) {
if (renew) {
client_auth_.append(kGpClientOfflineRenewalQueryParameters);
} else if (release) {
client_auth_.append(kGpClientOfflineReleaseQueryParameters);
} else {
client_auth_.append(kGpClientOfflineQueryParameters);
}
}
}
}
void ConfigTestEnv::Init(LicenseServerId server_id) {
client_auth_ = license_servers[server_id].client_tag;
key_id_ = license_servers[server_id].key_id;
key_system_ = kWidevineKeySystem;
license_server_ = license_servers[server_id].url;
provisioning_server_url_ = kProductionProvisioningServerUrl;
wrong_key_id_= kWrongKeyId;
}
} // namespace wvcdm

View File

@@ -6,11 +6,6 @@
#include <string>
#include "wv_cdm_types.h"
namespace {
const std::string kDefaultHttpsPort = "443";
const std::string kDefaultHttpPort = "80";
}
namespace wvcdm {
typedef enum {
kGooglePlayServer,
@@ -25,24 +20,22 @@ class ConfigTestEnv {
std::string url;
std::string client_tag;
std::string key_id;
std::string port;
bool use_chunked_transfer;
bool use_secure_transfer;
std::string offline_key_id;
} LicenseServerConfiguration;
explicit ConfigTestEnv(LicenseServerId server_id);
ConfigTestEnv(LicenseServerId server_id, bool streaming);
ConfigTestEnv(LicenseServerId server_id, bool streaming, bool renew,
bool release);
~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_;
}
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); }
@@ -52,17 +45,15 @@ class ConfigTestEnv {
void set_license_server(std::string& license_server) {
license_server_.assign(license_server);
}
void set_port(std::string& port) { port_.assign(port); }
private:
void Init(LicenseServerId server_id);
std::string client_auth_;
KeyId key_id_;
CdmKeySystem key_system_;
std::string license_server_;
std::string port_;
std::string provisioning_server_url_;
bool use_chunked_transfer_;
bool use_secure_transfer_;
KeyId wrong_key_id_;
CORE_DISALLOW_COPY_AND_ASSIGN(ConfigTestEnv);

View File

@@ -26,7 +26,11 @@ using ::testing::StrEq;
namespace {
const uint32_t kCertificateLen = 700;
const uint32_t kWrappedKeyLen = 500;
const uint32_t kProtobufEstimatedLen = 75;
const uint32_t kProtobufEstimatedOverhead = 75;
const uint32_t kLicenseRequestLen = 300;
const uint32_t kLicenseLen = 500;
const uint32_t kProviderSessionTokenLen = 128;
// Structurally valid test certificate.
// The data elements in this module are used to test the storage and
@@ -901,8 +905,7 @@ LicenseInfo license_update_test_data[] = {
"D30B08B2C4673551293A2E68747470733A2F2F746573742E676F6F676C65"
"2E636F6D2F6C6963656E73652F47657443656E634C6963656E736512200A"
"1C78D0E574D0827C3AE78A05EEC90BAC31D10686EC19EB0599F75B2D1AB4"
"C5"
)},
"C5")},
// license being released. all fields are identical except for license
// state and hashed file data
{"", DeviceFiles::kLicenseStateReleasing, "", "", "", "", "", "",
@@ -1001,6 +1004,287 @@ LicenseInfo license_update_test_data[] = {
"7186A244EF561E3B07DC459BC681A0798B180667EA448327F6BBBD30212A"
"49")}};
struct UsageInfo {
std::string provider_session_token;
std::string license_request;
std::string license;
std::string file_data;
};
UsageInfo kUsageInfoTestData[] = {
{"", "", "", // 0 usage info records
wvcdm::a2bs_hex(
"0A06080210012A00122095053501C5FA405B7EF01DA94685C6B20CB36493"
"A9CF1653B720E2BEA3B77929")},
{// 1 usage info record
wvcdm::a2bs_hex(
"924B035FBDA56AE5EF0ED05A08DE7AECC8ABE1835E0C4A548F7803937F4C3B4520EB7"
"F3334FFCDFA00DE56408F09D5019FCE87072D0DC6789817468974B2EA51EE3944B8D7"
"E0A88E4F16EBB80F03BD845231A01E6146841CBAEF0134DCD9300DB2D92732992C0F2"
"310D8E386FB31C67B9477010DEF9D99C4272589572A26A17E"),
wvcdm::a2bs_hex(
"1E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315DB32B6D3FDD1A8E8A09"
"4174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E154BC1548FC40EC70927"
"75531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6BE37645E6800F053B1D"
"A9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D344A419C0F0034A1B5"
"F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410F218E52A853AD214FD"
"05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3BCBAE42DF9EA65E42E"
"151827086EADE71C138B972CC3992CF9ADA944C063816352ED8658D3FA07BE0F32239"
"E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E510445294F44E511BD9B1A"
"F19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E"),
wvcdm::a2bs_hex(
"40FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C029F"
"D2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B037"
"72BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA991C8"
"BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C592436C8"
"B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2EFC6"
"780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A660"
"2B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F4C3"
"5FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24B06"
"53A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6374"
"D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6FB1A"
"C91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB8904A5"
"999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267ACA2"
"E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82989"
"C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A771"
"F561B165987E552824B0C914E708E425C3"),
wvcdm::a2bs_hex(
"0AB307080210012AAC070AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE"
"1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D"
"0DC6789817468974B2EA51EE3944B8D7E0A88E4F16EBB80F03BD845231A01E6146841"
"CBAEF0134DCD9300DB2D92732992C0F2310D8E386FB31C67B9477010DEF9D99C42725"
"89572A26A17E12AC021E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315D"
"B32B6D3FDD1A8E8A094174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E15"
"4BC1548FC40EC7092775531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6B"
"E37645E6800F053B1DA9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D"
"344A419C0F0034A1B5F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410"
"F218E52A853AD214FD05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3"
"BCBAE42DF9EA65E42E151827086EADE71C138B972CC3992CF9ADA944C063816352ED8"
"658D3FA07BE0F32239E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E5104"
"45294F44E511BD9B1AF19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E1AF"
"40340FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C0"
"29FD2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B"
"03772BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA99"
"1C8BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C59243"
"6C8B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2E"
"FC6780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A"
"6602B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F"
"4C35FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24"
"B0653A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6"
"374D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6F"
"B1AC91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB890"
"4A5999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267A"
"CA2E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82"
"989C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A"
"771F561B165987E552824B0C914E708E425C3122051C8F84C5713500997DC5B325BAE"
"D208B224DFAEB2B034E58046A62F503FED6E")},
{// 2 usage info records
wvcdm::a2bs_hex(
"7290396E183156BDF830B7BF31BA762CB2675528C9004FD24A61DAFB587ABCF1D36F8"
"7795EE0B3DA0B425616A66C82349B2E3BB8841C1335536865F919ED2AE671487B608B"
"21A362D888E0AB4F7AB7175B82F108617C3503F175435788AECAF7FFBFE76995D93CD"
"79424A843A247A8D8A6054A5B5404C9C057AACAD91A203229"),
wvcdm::a2bs_hex(
"3478A2D76DEB90BE713B03A11037EA7C305D1AF65099E3F2B92C4D4443A8F481C1177"
"DEF0A3CB49BA5F1448A10AF1207AD2D361B4A1F961B4B1F215B76A9A5005B414EF45E"
"AFBCF2636ABFC01413B27DD11871103579F8C041A799E22888D9ADB798E92A5E29BC4"
"6DECBC90991C65FE151C49F18068C1B65D0E90A9ECDA9248B87C120D5FD8EC81D4D36"
"B529FB2DAD39E0D39578B13B158E2B07C752D86F1A9D8160C93930C1F4F9E1D0D8E2C"
"5AB308732EB27722A6BF8BE852624C2BE3E4FE85819B89BEBA6535FCFBE85FA63A57B"
"D0FBAF284C64FFD97A146B76B3F37B576FC091C03E2222FBD24C2211344B7E2417EFC"
"36C4A54DCCC460CF810E7EA8AC6386D6AB567C819FED88A22CE55EF9BBE62C2CBC7AE"
"EDE5E5A69FF3472418CE2F4514496C59D26E72F3BFE0131F"),
wvcdm::a2bs_hex(
"C45FDCB3296A0EBE24FF381E027E6E2EF1AC289C67D3B858330669A81E8131583D2F1"
"40FD64615BDED0ED8316ABFD9C7E887433E1CAA6EA8E0C4F87ADB2A7FC3CF6FF87A7F"
"02AFF03BF5DB640AD8DDB572C41532E673618DCD8C33EF2BFE4E25EE821DF7D742B09"
"90398543B16EFCDBB03C6327B79D3664CED442E894020F4410ECC178C92AAEDFE39DC"
"563AC226FE9E0EF22E1C896C4F2835CDFDCD50B6C4DBA2B27A3B65DE3963D0A5F6E44"
"2A3C32008AB9D1ACBE4F366990EB43F8EE213B71E98DA090282680ABDD649BECA8970"
"0764561379F1DD23490CE967632ECA349AF8E1CBFA1F3A4F39F453614C8FFB5A17975"
"6243CB1FDB515834229BC64917C47A2F2E1116FAAC13368015312C31FD41215106469"
"BEE77D0EF2FE10CF645B3E82902EAF53A676933D0EC433949C1833BE52E76602CC3E4"
"E784C002E20624BCE0F38F9CBC478439899DA7F15554D0ACADEC140C00C8FA8FC9886"
"2D9933938781B30CB9C76899B3A48DBF170DDA0A18ED37D77F048ABBC85CB19469638"
"C2A32AA3180CF3943BD6B8C5CB26F2EA70868F18B0707C882054141086997A1AE5B70"
"9D4D0AA2B358990F244BA76C8E40791D29A0C63C9EF620B97FDFFA9B671E5A65AFCC1"
"C94CAACE0443E9D91F14028935BEA3988831BEBBFD3EB7C3A5AC9605B3534712A0912"
"4345ACB09665E357E58946871BC140D365"),
wvcdm::a2bs_hex(
"0ADF0E080210012AD80E0AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE"
"1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D"
"0DC6789817468974B2EA51EE3944B8D7E0A88E4F16EBB80F03BD845231A01E6146841"
"CBAEF0134DCD9300DB2D92732992C0F2310D8E386FB31C67B9477010DEF9D99C42725"
"89572A26A17E12AC021E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315D"
"B32B6D3FDD1A8E8A094174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E15"
"4BC1548FC40EC7092775531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6B"
"E37645E6800F053B1DA9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D"
"344A419C0F0034A1B5F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410"
"F218E52A853AD214FD05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3"
"BCBAE42DF9EA65E42E151827086EADE71C138B972CC3992CF9ADA944C063816352ED8"
"658D3FA07BE0F32239E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E5104"
"45294F44E511BD9B1AF19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E1AF"
"40340FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C0"
"29FD2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B"
"03772BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA99"
"1C8BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C59243"
"6C8B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2E"
"FC6780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A"
"6602B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F"
"4C35FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24"
"B0653A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6"
"374D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6F"
"B1AC91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB890"
"4A5999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267A"
"CA2E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82"
"989C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A"
"771F561B165987E552824B0C914E708E425C30AA9070A80017290396E183156BDF830"
"B7BF31BA762CB2675528C9004FD24A61DAFB587ABCF1D36F87795EE0B3DA0B425616A"
"66C82349B2E3BB8841C1335536865F919ED2AE671487B608B21A362D888E0AB4F7AB7"
"175B82F108617C3503F175435788AECAF7FFBFE76995D93CD79424A843A247A8D8A60"
"54A5B5404C9C057AACAD91A20322912AC023478A2D76DEB90BE713B03A11037EA7C30"
"5D1AF65099E3F2B92C4D4443A8F481C1177DEF0A3CB49BA5F1448A10AF1207AD2D361"
"B4A1F961B4B1F215B76A9A5005B414EF45EAFBCF2636ABFC01413B27DD11871103579"
"F8C041A799E22888D9ADB798E92A5E29BC46DECBC90991C65FE151C49F18068C1B65D"
"0E90A9ECDA9248B87C120D5FD8EC81D4D36B529FB2DAD39E0D39578B13B158E2B07C7"
"52D86F1A9D8160C93930C1F4F9E1D0D8E2C5AB308732EB27722A6BF8BE852624C2BE3"
"E4FE85819B89BEBA6535FCFBE85FA63A57BD0FBAF284C64FFD97A146B76B3F37B576F"
"C091C03E2222FBD24C2211344B7E2417EFC36C4A54DCCC460CF810E7EA8AC6386D6AB"
"567C819FED88A22CE55EF9BBE62C2CBC7AEEDE5E5A69FF3472418CE2F4514496C59D2"
"6E72F3BFE0131F1AF403C45FDCB3296A0EBE24FF381E027E6E2EF1AC289C67D3B8583"
"30669A81E8131583D2F140FD64615BDED0ED8316ABFD9C7E887433E1CAA6EA8E0C4F8"
"7ADB2A7FC3CF6FF87A7F02AFF03BF5DB640AD8DDB572C41532E673618DCD8C33EF2BF"
"E4E25EE821DF7D742B0990398543B16EFCDBB03C6327B79D3664CED442E894020F441"
"0ECC178C92AAEDFE39DC563AC226FE9E0EF22E1C896C4F2835CDFDCD50B6C4DBA2B27"
"A3B65DE3963D0A5F6E442A3C32008AB9D1ACBE4F366990EB43F8EE213B71E98DA0902"
"82680ABDD649BECA89700764561379F1DD23490CE967632ECA349AF8E1CBFA1F3A4F3"
"9F453614C8FFB5A179756243CB1FDB515834229BC64917C47A2F2E1116FAAC1336801"
"5312C31FD41215106469BEE77D0EF2FE10CF645B3E82902EAF53A676933D0EC433949"
"C1833BE52E76602CC3E4E784C002E20624BCE0F38F9CBC478439899DA7F15554D0ACA"
"DEC140C00C8FA8FC98862D9933938781B30CB9C76899B3A48DBF170DDA0A18ED37D77"
"F048ABBC85CB19469638C2A32AA3180CF3943BD6B8C5CB26F2EA70868F18B0707C882"
"054141086997A1AE5B709D4D0AA2B358990F244BA76C8E40791D29A0C63C9EF620B97"
"FDFFA9B671E5A65AFCC1C94CAACE0443E9D91F14028935BEA3988831BEBBFD3EB7C3A"
"5AC9605B3534712A09124345ACB09665E357E58946871BC140D3651220464E4A1BB23"
"1A5B0287888B34CA0A8CF5396EB2B8313377DC5ED5C41A9B389A9")},
{// 3 usage info records
wvcdm::a2bs_hex(
"983358221FB8DBF892047F00AA661F217EEC4E7A1626E8F98E025509E4D65A685E7D9"
"B169B98B16934F6E43E0E0E854A3FA9EB8E9A9D08E9D9B3A6C766AA44F7C655879BA2"
"DF5F38732FB7EDCA66D8C13A855B15E32CC9389B7DD119BA1F2417825FF1F52970F8E"
"985D34DD353D2AC8B24267353E5B8406C098427C4559A90CC"),
wvcdm::a2bs_hex(
"483EAC68243092009D06FAB41DB594ACB22E068C9524810758ECFF8BAB7E1B1ACA988"
"C3987023F01EFEC11529C7326279742E805E755A08EBBD9AA322F305805BE1166AB45"
"CB156FB0A9E6734371F4028707EE01CF2FB08465707E7E5613DD90D74B0D02536E26C"
"F1261CDDA8713943F3620ECC54095C76F8CD3CE31948C3CC0C9EB5582A4D087A54B39"
"1B4CDCBC98E35830B5932F6CF8D16427EF115CFF0A99499513702DD54C758E53248BB"
"5D195F2A2DD1DB18F97562F1F9034E223CEDB1E09ED1B0FE26089C20ED43B5D87B51F"
"6FC6C9F86255FBF70DF233F2665D604355BF9740A3B755521102E0B485C5CCCA607A9"
"A1BEB757BEDEF12327C637D17D6401E3756719F99BBE69B9CE4C8E47C2AC771F35A8E"
"E3FC4D58B2B2269CF85728E4DA7231BC8F0FD7C50E2A1EE9"),
wvcdm::a2bs_hex(
"5826D3A95F78879292612BCE06D845D64285CD45A7EAA6C87A9DBC3290B0B6AC95315"
"809F8CC7938768F9BD342C62CD4CE055866394489D955247CB0535001D50EFF4FEDF0"
"9501C58569B1EB9AA2305A113A5F4D4524AD34148A2DC48D2F522937F44A57FC76F57"
"EB1D4819C438EA42C7F8974FC7D2FE61CAAB3E1F27172FE6B8675DF4CCF1329A6EFB3"
"1F686FB0DC0F8B552D78970708D50C82ADBE333B585F6DE5A0D01D106F8232EB9ED45"
"42A2DC5AA031CC44652E8A42EDCA5AB08B0B5CA61A922E69A119E556F6014642522EA"
"1550F6D6E63EB25ACC03A4DD3F22F4686ED525F994FABA87629AF5939C16BA68C0F09"
"3EFE033CD319180BF69FCB72AC5123EBCB9DCF1AF00F0A68E31FF5B18FA8CFF3DFBB7"
"DA45413799105D67FA78217710D2F6C33394DD4088100013295FF43CF0598E6FE5C05"
"F03417CCD031F01CF63BECD444C750DF198345F155AB2B2AB94394A3C0C0AE05E386D"
"E6CC565AE82398BD0E377D6ABE103B9D5E84582C3772584B759891FC4B121A113370E"
"2DF5372DD81FB6358C64B0F6EB8F26193CA119E4D9D3D38036FA450EE2047CB2CE265"
"0FF37DF85BE23D58C17379FEC08DC0648236A107AE66178EEBF78F05F3B898424FA02"
"668B51F838AFA90D367B5CB425372D8CC3790BEA8AFB8795251FA09340D85A7F0B003"
"134C838F08BB1054D18404C3F69130700E"),
wvcdm::a2bs_hex(
"0A8B16080210012A84160AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE"
"1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D"
"0DC6789817468974B2EA51EE3944B8D7E0A88E4F16EBB80F03BD845231A01E6146841"
"CBAEF0134DCD9300DB2D92732992C0F2310D8E386FB31C67B9477010DEF9D99C42725"
"89572A26A17E12AC021E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315D"
"B32B6D3FDD1A8E8A094174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E15"
"4BC1548FC40EC7092775531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6B"
"E37645E6800F053B1DA9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D"
"344A419C0F0034A1B5F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410"
"F218E52A853AD214FD05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3"
"BCBAE42DF9EA65E42E151827086EADE71C138B972CC3992CF9ADA944C063816352ED8"
"658D3FA07BE0F32239E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E5104"
"45294F44E511BD9B1AF19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E1AF"
"40340FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C0"
"29FD2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B"
"03772BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA99"
"1C8BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C59243"
"6C8B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2E"
"FC6780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A"
"6602B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F"
"4C35FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24"
"B0653A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6"
"374D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6F"
"B1AC91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB890"
"4A5999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267A"
"CA2E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82"
"989C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A"
"771F561B165987E552824B0C914E708E425C30AA9070A80017290396E183156BDF830"
"B7BF31BA762CB2675528C9004FD24A61DAFB587ABCF1D36F87795EE0B3DA0B425616A"
"66C82349B2E3BB8841C1335536865F919ED2AE671487B608B21A362D888E0AB4F7AB7"
"175B82F108617C3503F175435788AECAF7FFBFE76995D93CD79424A843A247A8D8A60"
"54A5B5404C9C057AACAD91A20322912AC023478A2D76DEB90BE713B03A11037EA7C30"
"5D1AF65099E3F2B92C4D4443A8F481C1177DEF0A3CB49BA5F1448A10AF1207AD2D361"
"B4A1F961B4B1F215B76A9A5005B414EF45EAFBCF2636ABFC01413B27DD11871103579"
"F8C041A799E22888D9ADB798E92A5E29BC46DECBC90991C65FE151C49F18068C1B65D"
"0E90A9ECDA9248B87C120D5FD8EC81D4D36B529FB2DAD39E0D39578B13B158E2B07C7"
"52D86F1A9D8160C93930C1F4F9E1D0D8E2C5AB308732EB27722A6BF8BE852624C2BE3"
"E4FE85819B89BEBA6535FCFBE85FA63A57BD0FBAF284C64FFD97A146B76B3F37B576F"
"C091C03E2222FBD24C2211344B7E2417EFC36C4A54DCCC460CF810E7EA8AC6386D6AB"
"567C819FED88A22CE55EF9BBE62C2CBC7AEEDE5E5A69FF3472418CE2F4514496C59D2"
"6E72F3BFE0131F1AF403C45FDCB3296A0EBE24FF381E027E6E2EF1AC289C67D3B8583"
"30669A81E8131583D2F140FD64615BDED0ED8316ABFD9C7E887433E1CAA6EA8E0C4F8"
"7ADB2A7FC3CF6FF87A7F02AFF03BF5DB640AD8DDB572C41532E673618DCD8C33EF2BF"
"E4E25EE821DF7D742B0990398543B16EFCDBB03C6327B79D3664CED442E894020F441"
"0ECC178C92AAEDFE39DC563AC226FE9E0EF22E1C896C4F2835CDFDCD50B6C4DBA2B27"
"A3B65DE3963D0A5F6E442A3C32008AB9D1ACBE4F366990EB43F8EE213B71E98DA0902"
"82680ABDD649BECA89700764561379F1DD23490CE967632ECA349AF8E1CBFA1F3A4F3"
"9F453614C8FFB5A179756243CB1FDB515834229BC64917C47A2F2E1116FAAC1336801"
"5312C31FD41215106469BEE77D0EF2FE10CF645B3E82902EAF53A676933D0EC433949"
"C1833BE52E76602CC3E4E784C002E20624BCE0F38F9CBC478439899DA7F15554D0ACA"
"DEC140C00C8FA8FC98862D9933938781B30CB9C76899B3A48DBF170DDA0A18ED37D77"
"F048ABBC85CB19469638C2A32AA3180CF3943BD6B8C5CB26F2EA70868F18B0707C882"
"054141086997A1AE5B709D4D0AA2B358990F244BA76C8E40791D29A0C63C9EF620B97"
"FDFFA9B671E5A65AFCC1C94CAACE0443E9D91F14028935BEA3988831BEBBFD3EB7C3A"
"5AC9605B3534712A09124345ACB09665E357E58946871BC140D3650AA9070A8001983"
"358221FB8DBF892047F00AA661F217EEC4E7A1626E8F98E025509E4D65A685E7D9B16"
"9B98B16934F6E43E0E0E854A3FA9EB8E9A9D08E9D9B3A6C766AA44F7C655879BA2DF5"
"F38732FB7EDCA66D8C13A855B15E32CC9389B7DD119BA1F2417825FF1F52970F8E985"
"D34DD353D2AC8B24267353E5B8406C098427C4559A90CC12AC02483EAC68243092009"
"D06FAB41DB594ACB22E068C9524810758ECFF8BAB7E1B1ACA988C3987023F01EFEC11"
"529C7326279742E805E755A08EBBD9AA322F305805BE1166AB45CB156FB0A9E673437"
"1F4028707EE01CF2FB08465707E7E5613DD90D74B0D02536E26CF1261CDDA8713943F"
"3620ECC54095C76F8CD3CE31948C3CC0C9EB5582A4D087A54B391B4CDCBC98E35830B"
"5932F6CF8D16427EF115CFF0A99499513702DD54C758E53248BB5D195F2A2DD1DB18F"
"97562F1F9034E223CEDB1E09ED1B0FE26089C20ED43B5D87B51F6FC6C9F86255FBF70"
"DF233F2665D604355BF9740A3B755521102E0B485C5CCCA607A9A1BEB757BEDEF1232"
"7C637D17D6401E3756719F99BBE69B9CE4C8E47C2AC771F35A8EE3FC4D58B2B2269CF"
"85728E4DA7231BC8F0FD7C50E2A1EE91AF4035826D3A95F78879292612BCE06D845D6"
"4285CD45A7EAA6C87A9DBC3290B0B6AC95315809F8CC7938768F9BD342C62CD4CE055"
"866394489D955247CB0535001D50EFF4FEDF09501C58569B1EB9AA2305A113A5F4D45"
"24AD34148A2DC48D2F522937F44A57FC76F57EB1D4819C438EA42C7F8974FC7D2FE61"
"CAAB3E1F27172FE6B8675DF4CCF1329A6EFB31F686FB0DC0F8B552D78970708D50C82"
"ADBE333B585F6DE5A0D01D106F8232EB9ED4542A2DC5AA031CC44652E8A42EDCA5AB0"
"8B0B5CA61A922E69A119E556F6014642522EA1550F6D6E63EB25ACC03A4DD3F22F468"
"6ED525F994FABA87629AF5939C16BA68C0F093EFE033CD319180BF69FCB72AC5123EB"
"CB9DCF1AF00F0A68E31FF5B18FA8CFF3DFBB7DA45413799105D67FA78217710D2F6C3"
"3394DD4088100013295FF43CF0598E6FE5C05F03417CCD031F01CF63BECD444C750DF"
"198345F155AB2B2AB94394A3C0C0AE05E386DE6CC565AE82398BD0E377D6ABE103B9D"
"5E84582C3772584B759891FC4B121A113370E2DF5372DD81FB6358C64B0F6EB8F2619"
"3CA119E4D9D3D38036FA450EE2047CB2CE2650FF37DF85BE23D58C17379FEC08DC064"
"8236A107AE66178EEBF78F05F3B898424FA02668B51F838AFA90D367B5CB425372D8C"
"C3790BEA8AFB8795251FA09340D85A7F0B003134C838F08BB1054D18404C3F6913070"
"0E12202FF1FBA9926A24A1F79970EC427DDF87B4421488F7952499BC33CEB282D9E48"
"A")}};
} // namespace
class MockFile : public File {
@@ -1053,6 +1337,9 @@ class DeviceFilesSecurityLevelTest
: public DeviceFilesTest,
public ::testing::WithParamInterface<CdmSecurityLevel> {};
class DeviceFilesUsageInfoTest : public DeviceFilesTest,
public ::testing::WithParamInterface<int> {};
MATCHER(IsCreateFileFlagSet, "") { return File::kCreate & arg; }
MATCHER(IsBinaryFileFlagSet, "") { return File::kBinary & arg; }
MATCHER_P(IsStrEq, str, "") {
@@ -1060,18 +1347,29 @@ MATCHER_P(IsStrEq, str, "") {
// as well as pointer to data but that will introduce a dependency on tr1
return memcmp(arg, str.c_str(), str.size()) == 0;
}
MATCHER_P2(Contains, str1, str2, "") {
MATCHER_P3(Contains, str1, str2, size, "") {
// Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1
std::string data(arg, str1.size() + str2.size() + kProtobufEstimatedLen);
std::string data(
arg, size + str1.size() + str2.size() + kProtobufEstimatedOverhead);
return (data.find(str1) != std::string::npos &&
data.find(str2) != std::string::npos);
}
MATCHER_P4(Contains, str1, str2, str3, size, "") {
// Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1
std::string data(arg, size + str1.size() + str2.size() + str3.size() +
kProtobufEstimatedOverhead);
return (data.find(str1) != std::string::npos &&
data.find(str2) != std::string::npos &&
data.find(str3) != std::string::npos);
}
MATCHER_P6(Contains, str1, str2, str3, str4, str5, str6, "") {
// Estimating the length of data. We can have gmock provide length
// as well as pointer to data but that will introduce a dependency on tr1
std::string data(arg, str1.size() + str2.size() + str3.size() + str4.size() +
str5.size() + str6.size() + kProtobufEstimatedLen);
str5.size() + str6.size() +
kProtobufEstimatedOverhead);
return (data.find(str1) != std::string::npos &&
data.find(str2) != std::string::npos &&
data.find(str3) != std::string::npos &&
@@ -1100,14 +1398,15 @@ TEST_P(DeviceFilesStoreTest, StoreCertificate) {
EXPECT_CALL(file, Open(StrEq(device_certificate_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.WillOnce(Return(true));
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key),
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key, 0),
Gt(certificate.size() + wrapped_private_key.size())))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
}
@@ -1132,7 +1431,8 @@ TEST_F(DeviceFilesTest, ReadCertificate) {
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
std::string certificate, wrapped_private_key;
ASSERT_TRUE(
@@ -1161,14 +1461,15 @@ TEST_P(DeviceFilesSecurityLevelTest, SecurityLevel) {
EXPECT_CALL(file, Open(StrEq(device_certificate_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.WillOnce(Return(true));
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key),
EXPECT_CALL(file, Write(Contains(certificate, wrapped_private_key, 0),
Gt(certificate.size() + wrapped_private_key.size())))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(file, Close()).Times(1);
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, security_level));
EXPECT_TRUE(device_files.Init(security_level));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreCertificate(certificate, wrapped_private_key));
}
@@ -1208,7 +1509,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) {
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreLicense(
license_test_data[license_num].key_set_id,
license_test_data[license_num].license_state,
@@ -1226,7 +1528,8 @@ INSTANTIATE_TEST_CASE_P(StoreLicense, DeviceFilesStoreTest,
TEST_F(DeviceFilesTest, StoreLicenses) {
MockFile file;
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.Times(kNumberOfLicenses).WillRepeatedly(Return(true));
.Times(kNumberOfLicenses)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
for (size_t i = 0; i < kNumberOfLicenses; ++i) {
@@ -1251,7 +1554,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) {
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
for (size_t i = 0; i < kNumberOfLicenses; i++) {
EXPECT_TRUE(device_files.StoreLicense(
license_test_data[i].key_set_id, license_test_data[i].license_state,
@@ -1286,7 +1590,8 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) {
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
DeviceFiles::LicenseState license_state;
CdmInitData pssh_data;
CdmKeyMessage key_request;
@@ -1338,12 +1643,11 @@ TEST_F(DeviceFilesTest, SecurityLevelPathBackwardCompatibility) {
std::string old_path = base_path + DeviceFiles::GetCertificateFileName();
old_files.push_back(DeviceFiles::GetCertificateFileName());
EXPECT_CALL(file, IsRegularFile(StrEq(old_path)))
.WillOnce(Return(true));
EXPECT_CALL(file, IsRegularFile(StrEq(old_path))).WillOnce(Return(true));
EXPECT_CALL(file, Remove(StrEq(old_path))).WillOnce(Return(true));
for (size_t i = 0; i < security_dirs.size(); ++i) {
new_path = base_path + security_dirs[i] +
DeviceFiles::GetCertificateFileName();
new_path =
base_path + security_dirs[i] + DeviceFiles::GetCertificateFileName();
EXPECT_CALL(file, Copy(StrEq(old_path), StrEq(new_path)))
.WillOnce(Return(true));
}
@@ -1377,7 +1681,8 @@ TEST_F(DeviceFilesTest, SecurityLevelPathBackwardCompatibility) {
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
Properties::Init();
std::string certificate, wrapped_private_key;
@@ -1391,12 +1696,14 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
license_update_test_data[0].key_set_id +
DeviceFiles::GetLicenseFileNameExtension();
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_))).Times(2)
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
EXPECT_CALL(file, Open(StrEq(license_path),
AllOf(IsCreateFileFlagSet(), IsBinaryFileFlagSet())))
.Times(2).WillRepeatedly(Return(true));
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, Write(IsStrEq(license_update_test_data[0].file_data),
Eq(license_update_test_data[0].file_data.size())))
.WillOnce(ReturnArg<1>());
@@ -1407,7 +1714,8 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) {
EXPECT_CALL(file, Read(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
EXPECT_TRUE(device_files.StoreLicense(
license_update_test_data[0].key_set_id,
license_update_test_data[0].license_state,
@@ -1437,7 +1745,9 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
size_t size = license_test_data[0].file_data.size();
EXPECT_CALL(file, Exists(StrEq(license_path))).Times(2).WillOnce(Return(true))
EXPECT_CALL(file, Exists(StrEq(license_path)))
.Times(2)
.WillOnce(Return(true))
.WillOnce(Return(false));
EXPECT_CALL(file, FileSize(StrEq(license_path))).WillOnce(Return(size));
EXPECT_CALL(file, Open(StrEq(license_path), IsBinaryFileFlagSet()))
@@ -1451,7 +1761,8 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(&file, kSecurityLevelL1));
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
DeviceFiles::LicenseState license_state;
CdmInitData pssh_data;
CdmKeyMessage key_request;
@@ -1475,4 +1786,154 @@ TEST_F(DeviceFilesTest, DeleteLicense) {
EXPECT_FALSE(device_files.LicenseExists(license_test_data[0].key_set_id));
}
TEST_P(DeviceFilesUsageInfoTest, Read) {
MockFile file;
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName();
int index = GetParam();
std::string data;
if (index >= 0) {
data = kUsageInfoTestData[index].file_data;
}
if (index >= 0) {
EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(true));
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
.WillOnce(Return(true));
EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))).WillOnce(DoAll(
SetArrayArgument<0>(data.begin(), data.end()), Return(data.size())));
EXPECT_CALL(file, Close()).Times(1);
} else {
EXPECT_CALL(file, Exists(StrEq(path))).Times(2).WillRepeatedly(
Return(false));
EXPECT_CALL(file, FileSize(_)).Times(0);
EXPECT_CALL(file, Open(_, _)).Times(0);
EXPECT_CALL(file, Close()).Times(0);
}
EXPECT_CALL(file, Write(_, _)).Times(0);
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
std::vector<std::pair<CdmKeyMessage, CdmKeyResponse> > license_info;
ASSERT_TRUE(device_files.RetrieveUsageInfo(&license_info));
if (index >= 0) {
EXPECT_EQ(index, license_info.size());
for (size_t i = 0; i < license_info.size(); ++i) {
bool found = false;
for (size_t j = 0; j <= static_cast<size_t>(index); ++j) {
if ((license_info[i].first.compare(
kUsageInfoTestData[j].license_request) == 0) &&
(license_info[i].second.compare(kUsageInfoTestData[j].license) ==
0)) {
found = true;
}
}
EXPECT_TRUE(found);
}
} else {
EXPECT_EQ(0, license_info.size());
}
}
TEST_P(DeviceFilesUsageInfoTest, Store) {
MockFile file;
std::string pst(GenerateRandomData(kProviderSessionTokenLen));
std::string license_request(GenerateRandomData(kLicenseRequestLen));
std::string license(GenerateRandomData(kLicenseLen));
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName();
int index = GetParam();
std::string data;
if (index >= 0) {
data = kUsageInfoTestData[index].file_data;
}
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(index >= 0));
if (index >= 0) {
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))).WillOnce(DoAll(
SetArrayArgument<0>(data.begin(), data.end()), Return(data.size())));
EXPECT_CALL(file, Close()).Times(2);
} else {
EXPECT_CALL(file, FileSize(_)).Times(0);
EXPECT_CALL(file, Open(_, _)).Times(1).WillOnce(Return(true));
EXPECT_CALL(file, Close()).Times(1);
}
EXPECT_CALL(file, Write(Contains(pst, license_request, license, data.size()),
Gt(pst.size() + license_request.size() +
license.size()))).WillOnce(ReturnArg<1>());
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
ASSERT_TRUE(device_files.StoreUsageInfo(pst, license_request, license));
}
TEST_P(DeviceFilesUsageInfoTest, Delete) {
MockFile file;
std::string path = device_base_path_ + DeviceFiles::GetUsageInfoFileName();
int index = GetParam();
if (index < 0) return;
std::string data, pst, prev_data, prev_pst, prev_license;
if (index >= 0) {
data = kUsageInfoTestData[index].file_data;
if (index >= 1) {
pst = kUsageInfoTestData[index].provider_session_token;
prev_data = kUsageInfoTestData[index - 1].file_data;
prev_pst = kUsageInfoTestData[index - 1].provider_session_token;
prev_license = kUsageInfoTestData[index - 1].license;
}
}
EXPECT_CALL(file, IsDirectory(StrEq(device_base_path_)))
.WillRepeatedly(Return(true));
EXPECT_CALL(file, CreateDirectory(_)).Times(0);
EXPECT_CALL(file, Exists(StrEq(path))).WillOnce(Return(index >= 0));
EXPECT_CALL(file, FileSize(StrEq(path))).WillOnce(Return(data.size()));
if (index >= 1) {
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(file, Write(Contains(prev_pst, prev_license, prev_data.size()),
Gt(prev_pst.size() + prev_license.size())))
.WillOnce(ReturnArg<1>());
EXPECT_CALL(file, Close()).Times(2);
} else {
EXPECT_CALL(file, Open(StrEq(path), IsBinaryFileFlagSet()))
.WillOnce(Return(true));
EXPECT_CALL(file, Write(_, _)).Times(0);
EXPECT_CALL(file, Close()).Times(1);
}
EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))).WillOnce(DoAll(
SetArrayArgument<0>(data.begin(), data.end()), Return(data.size())));
DeviceFiles device_files;
EXPECT_TRUE(device_files.Init(kSecurityLevelL1));
device_files.SetTestFile(&file);
if (index >= 1) {
ASSERT_TRUE(device_files.DeleteUsageInfo(pst));
} else {
ASSERT_FALSE(device_files.DeleteUsageInfo(pst));
}
}
INSTANTIATE_TEST_CASE_P(UsageInfo, DeviceFilesUsageInfoTest,
::testing::Values(-1, 0, 1, 2, 3));
} // namespace wvcdm

View File

@@ -5,17 +5,35 @@
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include "log.h"
#include "openssl/bio.h"
#include "openssl/err.h"
#include "openssl/x509.h"
namespace wvcdm {
namespace {
SSL_CTX* HttpSocket::InitSslContext(void) {
// Helper function to tokenize a string. This makes it easier to avoid silly
// parsing bugs that creep in easily when each part of the string is parsed
// with its own piece of code.
bool Tokenize(const std::string& source, const std::string& delim,
const size_t offset, std::string* substring_output,
size_t* next_offset) {
size_t start_of_delim = source.find(delim, offset);
if (start_of_delim == std::string::npos) {
return false;
}
substring_output->assign(source, offset, start_of_delim - offset);
*next_offset = start_of_delim + delim.size();
return true;
}
SSL_CTX* InitSslContext() {
const SSL_METHOD* method;
SSL_CTX* ctx;
@@ -23,21 +41,18 @@ SSL_CTX* HttpSocket::InitSslContext(void) {
SSL_load_error_strings();
method = SSLv3_client_method();
ctx = SSL_CTX_new(method);
if (NULL == ctx) {
if (!ctx)
LOGE("failed to create SSL context");
}
return ctx;
}
void HttpSocket::ShowServerCertificate(const SSL* ssl) {
X509* cert;
char* line;
// unused, may be useful for debugging SSL-related issues.
void ShowServerCertificate(const SSL* ssl) {
// gets the server certificate
cert = SSL_get_peer_certificate(ssl);
if (cert != NULL) {
X509* cert = SSL_get_peer_certificate(ssl);
if (cert) {
char* line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
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);
@@ -49,183 +64,252 @@ void HttpSocket::ShowServerCertificate(const SSL* ssl) {
}
}
HttpSocket::HttpSocket()
: secure_connect_(true),
socket_fd_(-1),
// Wait for a socket to be ready for reading or writing.
// Establishing a connection counts as "ready for write".
// Returns false on select error or timeout.
// Returns true when the socket is ready.
bool SocketWait(int fd, bool for_read, int timeout_in_ms) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
struct timeval tv;
tv.tv_sec = timeout_in_ms / 1000;
tv.tv_usec = (timeout_in_ms % 1000) * 1000;
fd_set *read_fds = NULL;
fd_set *write_fds = NULL;
if (for_read) {
read_fds = &fds;
} else {
write_fds = &fds;
}
int ret = select(fd + 1, read_fds, write_fds, NULL, &tv);
if (ret == 0) {
LOGE("socket timed out");
return false;
} else if (ret == -1) {
LOGE("select failed, errno = %d", errno);
return false;
}
// socket ready.
return true;
}
} // namespace
namespace wvcdm {
// Parses the URL and extracts all relevant information.
// static
bool HttpSocket::ParseUrl(const std::string& url,
std::string* scheme,
bool* secure_connect,
std::string* domain_name,
int* port,
std::string* path) {
size_t offset = 0;
if (!Tokenize(url, "://", offset, scheme, &offset)) {
LOGE("Invalid URL, scheme not found: %s", url.c_str());
return false;
}
// If the scheme is http or https, set secure_connect and port accordingly.
// Otherwise, consider the scheme unsupported and fail.
if (*scheme == "http") {
*secure_connect = false;
*port = 80;
} else if (*scheme == "https") {
*secure_connect = true;
*port = 443;
} else {
LOGE("Invalid URL, scheme not supported: %s", url.c_str());
return false;
}
if (!Tokenize(url, "/", offset, domain_name, &offset)) {
// The rest of the URL belongs to the domain name.
domain_name->assign(url, offset, std::string::npos);
// No explicit path after the domain name.
path->assign("/");
} else {
// The rest of the URL, including the preceding slash, belongs to the path.
path->assign(url, offset - 1, std::string::npos);
}
// The domain name may optionally contain a port which overrides the default.
std::string domain_name_without_port;
size_t port_offset;
if (Tokenize(*domain_name, ":", 0, &domain_name_without_port,
&port_offset)) {
*port = atoi(domain_name->c_str() + port_offset);
if (*port <= 0 || *port >= 65536) {
LOGE("Invalid URL, port not valid: %s", url.c_str());
return false;
}
domain_name->assign(domain_name_without_port);
}
return true;
}
HttpSocket::HttpSocket(const std::string& url)
: socket_fd_(-1),
ssl_(NULL),
ssl_ctx_(NULL),
timeout_enabled_(false) {
ssl_ctx_(NULL) {
valid_url_ = ParseUrl(url, &scheme_, &secure_connect_, &domain_name_, &port_,
&resource_path_);
SSL_library_init();
}
HttpSocket::~HttpSocket() { CloseSocket(); }
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;
}
if (ssl_) {
SSL_free(ssl_);
ssl_ = NULL;
}
if (ssl_ctx_) {
SSL_CTX_free(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);
}
bool HttpSocket::Connect(int timeout_in_ms) {
if (!valid_url_) {
return false;
}
// 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_);
// get a socket
socket_fd_ = socket(AF_INET, SOCK_STREAM, 0);
if (socket_fd_ < 0) {
LOGE("cannot open socket %d", errno);
LOGE("cannot open socket, errno = %d", errno);
return false;
}
int reuse = 1;
if (setsockopt(socket_fd_, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) ==
-1) {
// set the socket in non-blocking mode
int original_flags = fcntl(socket_fd_, F_GETFL, 0);
if (original_flags == -1) {
LOGE("fcntl error, errno = %d", errno);
CloseSocket();
return false;
}
if (fcntl(socket_fd_, F_SETFL, original_flags | O_NONBLOCK) == -1) {
LOGE("fcntl error, errno = %d", errno);
CloseSocket();
LOGE("setsockopt error %d", errno);
return false;
}
// lookup the server IP
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);
int ret = getaddrinfo(domain_name_.c_str(), NULL, &hints, &addr_info);
if (ret != 0) {
CloseSocket();
LOGE("getaddrinfo failed with %d", ret);
status = false;
LOGE("getaddrinfo failed, errno = %d", ret);
return false;
}
// set the port
struct sockaddr_in* addr_ipv4 = reinterpret_cast<struct sockaddr_in*>(
addr_info->ai_addr);
addr_ipv4->sin_port = htons(port_);
// connect to the server
ret = connect(socket_fd_, addr_info->ai_addr, addr_info->ai_addrlen);
freeaddrinfo(addr_info);
if (ret == 0) {
// connected right away.
} else {
if (connect(socket_fd_, addr_info->ai_addr, addr_info->ai_addrlen) == -1) {
if (errno != EINPROGRESS) {
// failed right away.
LOGE("cannot connect to %s, errno = %d", domain_name_.c_str(), errno);
CloseSocket();
LOGE("cannot connect socket to %s, error=%d", domain_name_.c_str(),
errno);
status = false;
return false;
} else {
// in progress. block until timeout expired or connection established.
if (!SocketWait(socket_fd_, /* for_read */ false, timeout_in_ms)) {
LOGE("cannot connect to %s", domain_name_.c_str());
CloseSocket();
return false;
}
}
}
timeout_enabled_ = enable_timeout;
if (addr_info != NULL) {
freeaddrinfo(addr_info);
}
if (!status) return false;
// set up SSL if needed
if (secure_connect_) {
ssl_ctx_ = InitSslContext();
if (!ssl_ctx_) {
CloseSocket();
return false;
}
// secures connection
if (secure_connect_ && ssl_ctx_) {
ssl_ = SSL_new(ssl_ctx_);
if (!ssl_) {
LOGE("failed SSL_new");
CloseSocket();
return false;
}
BIO* a_bio = BIO_new_socket(socket_fd_, BIO_NOCLOSE);
if (!a_bio) {
LOGE("BIO_new_socket error");
CloseSocket();
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;
}
do {
ret = SSL_connect(ssl_);
if (ret != 1) {
int ssl_err = SSL_get_error(ssl_, ret);
if (ssl_err != SSL_ERROR_WANT_READ &&
ssl_err != SSL_ERROR_WANT_WRITE) {
char buf[256];
LOGE("SSL_connect error: %s", ERR_error_string(ERR_get_error(), buf));
CloseSocket();
return false;
}
bool for_read = ssl_err == SSL_ERROR_WANT_READ;
if (!SocketWait(socket_fd_, for_read, timeout_in_ms)) {
LOGE("cannot connect to %s", domain_name_.c_str());
CloseSocket();
return false;
}
}
} while (ret != 1);
}
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
// Returns -1 for error, number of bytes read for success.
// The timeout here only applies to the span between packets of data, for the
// sake of simplicity.
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 (!SocketWait(socket_fd_, /* for_read */ true, timeout_in_ms)) {
LOGE("unable to read from %s", domain_name_.c_str());
return -1;
}
int read;
if (secure_connect_)
read = SSL_read(ssl_, data, to_read);
else
@@ -236,27 +320,26 @@ int HttpSocket::Read(char* data, int len, int timeout_in_ms) {
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
// The connection has been closed. No more data.
break;
} else {
LOGE("recv returned %d, error = %d", read, errno);
break;
LOGE("recv returned %d, errno = %d", read, errno);
return -1;
}
}
if (use_timeout) {
fcntl(socket_fd_, F_SETFL, original_flags); // now blocking again
}
return total_read;
}
int HttpSocket::Write(const char* data, int len) {
// Returns -1 for error, number of bytes written for success.
// The timeout here only applies to the span between packets of data, for the
// sake of simplicity.
int HttpSocket::Write(const char* data, int len, int timeout_in_ms) {
int total_sent = 0;
int sent = 0;
int to_send = len;
while (to_send > 0) {
int sent;
if (secure_connect_)
sent = SSL_write(ssl_, data, to_send);
else
@@ -267,11 +350,17 @@ int HttpSocket::Write(const char* data, int len) {
data += sent;
total_sent += sent;
} else if (sent == 0) {
usleep(10); // retry later
// We filled up the pipe. Wait for room to write.
if (!SocketWait(socket_fd_, /* for_read */ false, timeout_in_ms)) {
LOGE("unable to write to %s", domain_name_.c_str());
return -1;
}
} else {
LOGE("send returned error %d", errno);
LOGE("send returned %d, errno = %d", sent, errno);
return -1;
}
}
return total_sent;
}

View File

@@ -4,43 +4,52 @@
#define CDM_TEST_HTTP_SOCKET_H_
#include <string>
#include "openssl/ssl.h"
#include "wv_cdm_types.h"
#include <gtest/gtest_prod.h>
#include <openssl/ssl.h>
#include "wv_cdm_types.h" // CORE_DISALLOW_COPY_AND_ASSIGN
namespace wvcdm {
// Provides basic Linux based TCP socket interface.
class HttpSocket {
public:
HttpSocket();
// A scheme (http:// or https://) is required for the URL.
explicit HttpSocket(const std::string& url);
~HttpSocket();
bool Connect(int timeout_in_ms);
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);
const std::string& scheme() const { return scheme_; }
bool secure_connect() const { return secure_connect_; }
const std::string& domain_name() const { return domain_name_; }
int port() const { return port_; }
const std::string& resource_path() const { return resource_path_; }
int Read(char* data, int len, int timeout_in_ms);
int Write(const char* data, int len);
int Write(const char* data, int len, int timeout_in_ms);
private:
void CloseSslContext(SSL_CTX* ctx) const {
if (ctx) SSL_CTX_free(ctx);
}
SSL_CTX* InitSslContext(void);
void ShowServerCertificate(const SSL* ssl);
static bool ParseUrl(const std::string& url,
std::string* scheme,
bool* secure_connect,
std::string* domain_name,
int* port,
std::string* path);
FRIEND_TEST(HttpSocketTest, ParseUrlTest);
std::string domain_name_;
std::string scheme_;
bool secure_connect_;
std::string domain_name_;
int port_;
std::string resource_path_;
bool valid_url_;
int socket_fd_;
SSL* ssl_;
SSL_CTX* ssl_ctx_;
bool timeout_enabled_;
CORE_DISALLOW_COPY_AND_ASSIGN(HttpSocket);
};

View File

@@ -3,6 +3,7 @@
#include <errno.h>
#include "gtest/gtest.h"
#include "http_socket.h"
#include "scoped_ptr.h"
#include "log.h"
#include "string_conversions.h"
#include "url_request.h"
@@ -10,10 +11,14 @@
namespace {
// Arbitrary URL for tests.
const std::string kHttpsTestServer("https://www.google.com");
const std::string kHttpTestServer("http://www.google.com");
// This URL and data are used by RoundTripTest, and can be overridden on the
// command line.
std::string gTestServer(kHttpsTestServer);
std::string gTestData("Hello");
// Arbitrary buffer size and timeout settings.
const int kHttpBufferSize = 4096;
char gBuffer[kHttpBufferSize];
const int kTimeout = 3000;
}
namespace wvcdm {
@@ -21,153 +26,179 @@ namespace wvcdm {
class HttpSocketTest : public testing::Test {
public:
HttpSocketTest() {}
~HttpSocketTest() { socket_.CloseSocket(); }
~HttpSocketTest() {}
protected:
bool Connect(const std::string& server_url, bool secure_connection) {
bool Connect(const std::string& server_url) {
socket_.reset(new HttpSocket(server_url));
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());
if (socket_->Connect(kTimeout)) {
LOGD("connected to %s", socket_->domain_name().c_str());
return true;
} else {
LOGE("failed to connect to %s", socket_.domain_name().c_str());
LOGE("failed to connect to %s", server_url.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(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(socket_->domain_name());
request.append("\r\n");
// Important! Otherwise, the HTTP 1.1 default behavior for a server is to
// keep the connection open for a subsequent request.
request.append("Connection: close\r\n");
request.append("User-Agent: httpSocketTest/1.0\r\n");
char buffer[32] = {0};
snprintf(buffer, sizeof(buffer), "%d", static_cast<int>(data.size()));
request.append("Content-Length: ");
memset(gBuffer, 0, kHttpBufferSize);
snprintf(gBuffer, kHttpBufferSize, "%d\r\n", static_cast<int>(data.size()));
request.append(gBuffer);
request.append(buffer);
request.append("\r\n");
request.append("Content-Type: multipart/form-data\r\n");
// newline terminates header
// an extra newline terminates HTTP headers.
request.append("\r\n");
// append data
request.append(data);
socket_.Write(request.c_str(), request.size());
socket_->Write(request.c_str(), request.size(), kTimeout);
LOGD("request: %s", request.c_str());
return true;
}
bool GetResponse() {
int bytes = socket_.Read(gBuffer, kHttpBufferSize, 1000);
bool GetResponse(std::string* response) {
char buffer[kHttpBufferSize];
int bytes = socket_->Read(buffer, sizeof(buffer), kTimeout);
if (bytes < 0) {
LOGE("read error = ", errno);
LOGE("read error, errno = %d", 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;
}
LOGD("read %d bytes", bytes);
response->assign(buffer, bytes);
return true;
}
HttpSocket socket_;
scoped_ptr<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());
struct ParseUrlTests {
const char* url;
const char* scheme;
bool secure_connect;
const char* domain_name;
int port;
const char* path;
};
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());
ParseUrlTests parse_url_tests[] = {
{
"https://code.google.com/p/googletest/wiki/Primer", // url
"https", // scheme
true, // secure_connect
"code.google.com", // domain_name
443, // port
"/p/googletest/wiki/Primer", // path
},
{
"http://code.google.com/p/googletest/wiki/Primer/", // url
"http", // scheme
false, // secure_connect
"code.google.com", // domain_name
80, // port
"/p/googletest/wiki/Primer/", // path
},
{
"http://code.google.com/", // url
"http", // scheme
false, // secure_connect
"code.google.com", // domain_name
80, // port
"/", // path
},
{
"http://code.google.com", // url
"http", // scheme
false, // secure_connect
"code.google.com", // domain_name
80, // port
"/", // path
},
{
"http://10.11.12.13:8888/drm", // url
"http", // scheme
false, // secure_connect
"10.11.12.13", // domain_name
8888, // port
"/drm", // path
},
{
"http://10.11.12.13:8888", // url
"http", // scheme
false, // secure_connect
"10.11.12.13", // domain_name
8888, // port
"/", // path
},
{
"https://10.11.12.13:8888", // url
"https", // scheme
true, // secure_connect
"10.11.12.13", // domain_name
8888, // port
"/", // path
},
{ NULL } // list terminator
};
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());
TEST_F(HttpSocketTest, ParseUrlTest) {
std::string scheme;
bool secure_connect;
std::string domain_name;
int port;
std::string path;
ParseUrlTests* test = NULL;
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 arbitrary 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());
for (test = &parse_url_tests[0]; test->url != NULL; ++test) {
bool ok = HttpSocket::ParseUrl(test->url, &scheme, &secure_connect,
&domain_name, &port, &path);
EXPECT_TRUE(ok);
if (ok) {
EXPECT_EQ(test->scheme, scheme);
EXPECT_EQ(test->secure_connect, secure_connect);
EXPECT_EQ(test->domain_name, domain_name);
EXPECT_EQ(test->port, port);
EXPECT_EQ(test->path, path);
}
}
}
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();
EXPECT_TRUE(Connect(kHttpsTestServer));
EXPECT_TRUE(Connect(kHttpTestServer));
EXPECT_FALSE(Connect("ww.g.c"));
EXPECT_FALSE(Connect("http://ww.g.c"));
EXPECT_FALSE(Connect("https://ww.g.c"));
}
TEST_F(HttpSocketTest, RoundTripTest) {
int secure_connection =
(gTestServer.find("https") != std::string::npos) ? true : false;
ASSERT_TRUE(Connect(gTestServer, secure_connection));
ASSERT_TRUE(Connect(gTestServer));
EXPECT_TRUE(PostRequest(gTestData));
GetResponse();
socket_.CloseSocket();
std::string response;
EXPECT_TRUE(GetResponse(&response));
LOGD("response: %s", response.c_str());
}
} // namespace wvcdm

View File

@@ -10,52 +10,16 @@
#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
}
const int kReadBufferSize = 1024;
const int kConnectTimeoutMs = 5000;
const int kWriteTimeoutMs = 3000;
const int kReadTimeoutMs = 3000;
// 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) {
void ConcatenateChunkedResponse(const std::string http_response,
std::string* modified_response) {
if (http_response.empty()) return;
modified_response->clear();
@@ -103,33 +67,52 @@ void UrlRequest::ConcatenateChunkedResponse(const std::string http_response,
}
}
int UrlRequest::GetResponse(std::string* message) {
message->clear();
} // namespace
namespace wvcdm {
UrlRequest::UrlRequest(const std::string& url)
: is_connected_(false),
socket_(url) {
if (socket_.Connect(kConnectTimeoutMs)) {
is_connected_ = true;
} else {
LOGE("failed to connect to %s, port=%d", socket_.domain_name().c_str(),
socket_.port());
}
}
UrlRequest::~UrlRequest() {}
bool UrlRequest::GetResponse(std::string* message) {
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);
// Keep reading until end of stream (0 bytes read) or timeout. Partial
// buffers worth of data can and do happen, especially with OpenSSL in
// non-blocking mode.
while (true) {
char read_buffer[kReadBufferSize];
int bytes = socket_.Read(read_buffer, sizeof(read_buffer), kReadTimeoutMs);
if (bytes > 0) {
response.append(buffer_, bytes);
if (bytes < static_cast<int>(kHttpBufferSize)) {
attempts = kSingleReadAttempt;
}
response.append(read_buffer, bytes);
} else if (bytes < 0) {
LOGE("read error, errno = %d", errno);
return false;
} else {
if (bytes < 0) LOGE("read error = ", errno);
// bytes == 0 indicates nothing to read
// end of stream.
break;
}
}
ConcatenateChunkedResponse(response, message);
LOGD("HTTP response: (%d): %s", message->size(), b2a_hex(*message).c_str());
return message->size();
return true;
}
// static
int UrlRequest::GetStatusCode(const std::string& response) {
const std::string kHttpVersion("HTTP/1.1");
const std::string kHttpVersion("HTTP/1.1 ");
int status_code = -1;
size_t pos = response.find(kHttpVersion);
@@ -140,79 +123,48 @@ int UrlRequest::GetStatusCode(const std::string& response) {
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
bool UrlRequest::PostRequestWithPath(const std::string& path,
const std::string& data) {
std::string request;
// calls AppendChunkToUpload repeatedly for multiple chunks
AppendChunkToUpload(data);
request.append("POST ");
request.append(path);
request.append(" HTTP/1.1\r\n");
// terminates last chunk with 0\r\n, then ends header with an empty line
request_.append("0\r\n\r\n");
request.append("Host: ");
request.append(socket_.domain_name());
request.append("\r\n");
socket_.Write(request_.c_str(), request_.size());
return true;
request.append("Connection: close\r\n");
request.append("User-Agent: Widevine CDM v1.0\r\n");
// buffer to store length of data as a string
char data_size_buffer[32] = {0};
snprintf(data_size_buffer, sizeof(data_size_buffer), "%zd", data.size());
request.append("Content-Length: ");
request.append(data_size_buffer); // appends size of data
request.append("\r\n");
request.append("\r\n"); // empty line to terminate headers
request.append(data);
int ret = socket_.Write(request.c_str(), request.size(), kWriteTimeoutMs);
LOGD("HTTP request: (%d): %s", request.size(), b2a_hex(request).c_str());
return ret != -1;
}
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;
return PostRequestWithPath(socket_.resource_path(), data);
}
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
std::string path = socket_.resource_path();
path.append("&signedRequest=");
path.append(data);
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;
return PostRequestWithPath(path, "");
}
} // namespace wvcdm

View File

@@ -13,29 +13,22 @@ namespace wvcdm {
// 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);
explicit UrlRequest(const std::string& url);
~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);
bool GetResponse(std::string* message);
static int GetStatusCode(const std::string& response);
private:
static const unsigned int kHttpBufferSize = 4096;
char buffer_[kHttpBufferSize];
bool chunk_transfer_mode_;
bool PostRequestWithPath(const std::string& path, const std::string& data);
bool is_connected_;
std::string port_;
std::string request_;
HttpSocket socket_;
std::string server_url_;
CORE_DISALLOW_COPY_AND_ASSIGN(UrlRequest);
};