Source release v2.1.2-0-773 + third_party libs
Change-Id: Ia07608577b65b301c22a8ff4bf7f743c2d3f9274
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user