mirror of
https://github.com/Netflix/dial-reference.git
synced 2026-06-08 02:49:58 +00:00
dial spec 1.7
This commit is contained in:
133
client/DialClientInput.cpp
Normal file
133
client/DialClientInput.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Netflix, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "DialClientInput.h"
|
||||
#include "DialServer.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool DialClientInput::init(std::string file)
|
||||
{
|
||||
if( file.empty() ) file = DialClientInput::getDefaultFilename();
|
||||
ATRACE("DialClientInput::%s, opening %s\n", __FUNCTION__, file.c_str());
|
||||
string line;
|
||||
ifstream myfile (file.c_str());
|
||||
if (myfile.is_open())
|
||||
{
|
||||
// first fill _actions with commands from the input file
|
||||
while ( myfile.good() )
|
||||
{
|
||||
getline (myfile,line);
|
||||
if( line[0] != '#' && !line.empty() )
|
||||
{
|
||||
if( line.find("addApplication") != line.npos )
|
||||
{
|
||||
// add an application
|
||||
size_t pos = line.find_first_of('=');
|
||||
_applist.push_back( line.substr(pos+1, line.length()));
|
||||
ATRACE("Adding app: %s\n", line.substr(pos+1, line.length()).c_str());
|
||||
}
|
||||
else if( line.find("addErrorApplication") != line.npos )
|
||||
{
|
||||
// add an application
|
||||
size_t pos = line.find_first_of('=');
|
||||
_errorapplist.push_back( line.substr(pos+1, line.length()));
|
||||
ATRACE("Adding error app: %s\n", line.substr(pos+1, line.length()).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
// add a command
|
||||
size_t pos = line.find_first_of(" ");
|
||||
std::string params = line.substr(pos+1, line.length());
|
||||
std::pair<std::string, std::string>
|
||||
action_to_push(line.substr(0, pos),
|
||||
pos == line.npos ? "":params);
|
||||
_actions.push_back(action_to_push);
|
||||
ATRACE("command: %s params: %s\n",
|
||||
line.substr(0, pos).c_str(), params.c_str() );
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
//else ATRACE("COMMENT: %s\n", line.c_str());
|
||||
#endif
|
||||
}
|
||||
myfile.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Unable to open file\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DialClientInput::addApplication( string& application )
|
||||
{
|
||||
// see if the application exits
|
||||
vector<string>::iterator it;
|
||||
for( it = _applist.begin(); it < _applist.end(); it ++ )
|
||||
if( !((*it).compare( application )) ) break;
|
||||
|
||||
// if not, add it
|
||||
if( it < _applist.end() ) _applist.push_back(application);
|
||||
else return false; // already in the list
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DialClientInput::getNextAction( string& command, vector<string>& parameters )
|
||||
{
|
||||
if (_actions.empty()) return false;
|
||||
|
||||
pair<string, string> action = _actions.front();
|
||||
_actions.erase(_actions.begin());
|
||||
|
||||
command = action.first;
|
||||
string params = action.second;
|
||||
size_t pos = params.find_first_of(" "), start = 0;
|
||||
// Parse out the parameters from the string
|
||||
while (1)
|
||||
{
|
||||
parameters.push_back(params.substr( start, pos-start ));
|
||||
if( pos == params.npos ) break;
|
||||
start = pos+1;
|
||||
pos = params.find_first_of(" ", start);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DialClientInput::getApplicationList( vector<string> &list )
|
||||
{
|
||||
list = _applist;
|
||||
}
|
||||
|
||||
|
||||
void DialClientInput::getErrorApplicationList(vector<string> &list)
|
||||
{
|
||||
list = _errorapplist;
|
||||
}
|
||||
|
||||
89
client/DialClientInput.h
Normal file
89
client/DialClientInput.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Netflix, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DIALCLIENTINPUT_H
|
||||
#define DIALCLIENTINPUT_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class DialClientInput
|
||||
{
|
||||
public:
|
||||
DialClientInput() {}
|
||||
~DialClientInput() {}
|
||||
|
||||
/**
|
||||
* Called to initialize
|
||||
*
|
||||
* @param[in] file Name of the file to load
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool init( string file );
|
||||
|
||||
/**
|
||||
* The DialClient application can use this to add applications passed on
|
||||
* the command line. If the application is already in the list, it will
|
||||
* drop the add.
|
||||
*
|
||||
* @param[in] application Name of the application to add.
|
||||
*
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool addApplication( string &application );
|
||||
|
||||
/**
|
||||
* Get the next action to execute
|
||||
*
|
||||
* @param[out] command Command to execute
|
||||
* @param[out] parameters Parameters for the command
|
||||
*/
|
||||
bool getNextAction( string& command, vector<string>& parameters );
|
||||
|
||||
/**
|
||||
* Get a list of valid applications defined in the input file.
|
||||
*
|
||||
* @param[out] list List of valid applications
|
||||
*/
|
||||
void getApplicationList(vector<string> &list);
|
||||
|
||||
/**
|
||||
* Get a list of applications used for error tests (should not exist).
|
||||
*
|
||||
* @param[out] list List of error applications
|
||||
*/
|
||||
void getErrorApplicationList(vector<string> &list);
|
||||
|
||||
static string getDefaultFilename() { return "./dialclient_input.txt"; }
|
||||
|
||||
private:
|
||||
vector<string> _applist;
|
||||
vector<string> _errorapplist;
|
||||
vector< pair<string,string> > _actions;
|
||||
};
|
||||
|
||||
#endif // DIALCLIENTINPUT_H
|
||||
587
client/DialConformance.cpp
Normal file
587
client/DialConformance.cpp
Normal file
@@ -0,0 +1,587 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Netflix, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "DialConformance.h"
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// Number of milliseconds to sleep between launches when doing a launch=ALL
|
||||
#define LAUNCH_SLEEP 6000
|
||||
#define MAX_PARAMETER_LENGTH 60
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
* This class will be reponsible for reporting the conformance test results.
|
||||
* Requirements:
|
||||
* * Results should be in HTML
|
||||
* * Results should be easily readable
|
||||
* * Results should clearly distinguish between pass and fail
|
||||
* * If there is a failure, there should be ample information provided
|
||||
* for debugging
|
||||
*/
|
||||
class OutputWriter
|
||||
{
|
||||
public:
|
||||
OutputWriter( string fileName ) :
|
||||
_filename(fileName),
|
||||
_pCurrentTest(NULL),
|
||||
_isComplete(false)
|
||||
{
|
||||
}
|
||||
~OutputWriter()
|
||||
{ }
|
||||
|
||||
void setOutputFile( string fileName )
|
||||
{
|
||||
_filename = fileName;
|
||||
}
|
||||
|
||||
void setTest( string testName )
|
||||
{
|
||||
assert( _pCurrentTest == NULL );
|
||||
_pCurrentTest = new Test(testName);
|
||||
_tests.push_back(_pCurrentTest);
|
||||
}
|
||||
void clearTest()
|
||||
{
|
||||
// still holding a ref in the test vector. That will get deleted
|
||||
// when the test is complete
|
||||
_pCurrentTest = NULL;
|
||||
}
|
||||
|
||||
void setError( string error ) { _pCurrentTest->addError(error); }
|
||||
void setResult( bool result ) { _pCurrentTest->setResult( result ); }
|
||||
|
||||
void start( string& friendlyname, string& uuid, string ipaddress )
|
||||
{
|
||||
_file.open(_filename.c_str());
|
||||
_file << "<html>\n<body>\n";
|
||||
_file << "<font size=\"6\"><b>FriendlyName: " << friendlyname;
|
||||
_file << "<br>\nIP: " << ipaddress;
|
||||
_file << "<br>\nUUID: " << uuid;
|
||||
|
||||
{
|
||||
time_t rawtime;
|
||||
struct tm * timeinfo;
|
||||
char buffer [80];
|
||||
|
||||
time ( &rawtime );
|
||||
timeinfo = localtime ( &rawtime );
|
||||
|
||||
//strftime (buffer,80,"%I:%M%p.",timeinfo);
|
||||
strftime (buffer,80,"%c",timeinfo);
|
||||
_file << "<br>\nTime of Run: " << buffer << "</b></font>";
|
||||
}
|
||||
_file << "<table border=\"10\">\n";
|
||||
_file << "<tr>\n";
|
||||
_file << "<th> RESULT </th>"; //header 1
|
||||
_file << "<th> Test Executed </th>"; //header 2
|
||||
_file << "<th> Errors </th>"; //header 2
|
||||
_file << "</tr>\n";
|
||||
}
|
||||
|
||||
void complete()
|
||||
{
|
||||
vector<Test*>::iterator it;
|
||||
for( it = _tests.begin(); it < _tests.end(); it++ )
|
||||
{
|
||||
write("<tr>");
|
||||
// write the name and the result
|
||||
stringstream result;
|
||||
bool testResult = (*it)->getResult();
|
||||
result << "<td><b> <font color=\"" << (testResult ? "green" : "red") << "\"> ";
|
||||
result << (testResult ? "SUCCESS" : "FAIL") << " </b></font></td>\n<td>" << (*it)->getName() << "</td>";
|
||||
write( result.str() );
|
||||
|
||||
// write errors if they exist
|
||||
write("<td>");
|
||||
vector<string> errors = (*it)->getErrors();
|
||||
if( !errors.empty() )
|
||||
{
|
||||
vector<string>::iterator it2;
|
||||
write("<b>");
|
||||
for( it2 = errors.begin(); it2 < errors.end(); it2++ )
|
||||
write( (*it2) );
|
||||
write("</b>");
|
||||
}
|
||||
else
|
||||
write("NO ERROR");
|
||||
write("</td>\n</tr>");
|
||||
|
||||
// delete the test
|
||||
delete (*it);
|
||||
}
|
||||
_file << "</table></body>\n</html>\n";
|
||||
_file.close();
|
||||
|
||||
_isComplete = true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Abstraction of a test
|
||||
class Test
|
||||
{
|
||||
public:
|
||||
Test(string& name) :
|
||||
_testname(name),
|
||||
_testPassed(false) {}
|
||||
~Test() {}
|
||||
|
||||
string getName() { return _testname; }
|
||||
void setResult( bool result ) { _testPassed = result; }
|
||||
bool getResult() { return _testPassed; }
|
||||
|
||||
void addError( string error ) { _errors.push_back(error); }
|
||||
vector<string> getErrors() { return _errors; }
|
||||
|
||||
private:
|
||||
string _testname;
|
||||
bool _testPassed;
|
||||
vector<string> _errors;
|
||||
};
|
||||
|
||||
string _filename;
|
||||
ofstream _file;
|
||||
vector<Test*> _tests;
|
||||
Test* _pCurrentTest;
|
||||
bool _isComplete;
|
||||
|
||||
int write( string line )
|
||||
{
|
||||
_file << line.c_str() << "\n";
|
||||
return line.length()+1;
|
||||
}
|
||||
};
|
||||
|
||||
static const string defaultOutputFile = "./report.html";
|
||||
static OutputWriter gWriter(defaultOutputFile);
|
||||
|
||||
DialConformance *DialConformance::sConformance = 0;
|
||||
|
||||
DialConformance* DialConformance::create(void)
|
||||
{
|
||||
assert( DialConformance::sConformance == 0 );
|
||||
return new DialConformance();
|
||||
}
|
||||
|
||||
DialConformance* DialConformance::instance(void)
|
||||
{
|
||||
return DialConformance::sConformance;
|
||||
}
|
||||
|
||||
DialConformance::DialConformance()
|
||||
{
|
||||
assert( DialConformance::sConformance == 0 );
|
||||
DialConformance::sConformance = this;
|
||||
}
|
||||
|
||||
DialConformance::~DialConformance()
|
||||
{
|
||||
assert( DialConformance::sConformance == this );
|
||||
DialConformance::sConformance = 0;
|
||||
}
|
||||
|
||||
void DialConformance::extractParamValue(
|
||||
string& param, string& value )
|
||||
{
|
||||
// TODO: Add support for quotes after the equals
|
||||
size_t posBegin = param.find("=");
|
||||
value = param.substr(posBegin+1, param.length()-posBegin);
|
||||
}
|
||||
|
||||
bool DialConformance::validateParams( vector<string>& params,
|
||||
string& responseHeaders, string& responseBody )
|
||||
{
|
||||
bool testPassed = true;
|
||||
vector<string>::iterator it;
|
||||
for( it = params.begin(); it < params.end() && testPassed == true; it++ )
|
||||
{
|
||||
ATRACE("%s:: params:%s \n", __FUNCTION__, (*it).c_str());
|
||||
if( (*it).find("httpresponse=") != (*it).npos )
|
||||
testPassed = validateHttpResponse( responseHeaders, *it );
|
||||
if( (*it).find("httpresponseheader=") != (*it).npos )
|
||||
testPassed = validateHttpHeaders( responseHeaders, *it );
|
||||
if( (*it).find("resultbody=") != (*it).npos )
|
||||
testPassed = validateResponseBody( responseBody, *it );
|
||||
}
|
||||
return testPassed;
|
||||
}
|
||||
|
||||
|
||||
bool DialConformance::validateHttpResponse(
|
||||
string& headers, string& param )
|
||||
{
|
||||
bool retval = false;
|
||||
string responseCode;
|
||||
extractParamValue( param, responseCode );
|
||||
|
||||
ATRACE("%s: Searching for %s\n", __FUNCTION__, responseCode.c_str());
|
||||
if( headers.find(responseCode) != headers.npos )
|
||||
{
|
||||
ATRACE("%s: %s FOUND\n", __FUNCTION__, responseCode.c_str());
|
||||
retval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ATRACE("%s: %s not found\n", __FUNCTION__, responseCode.c_str());
|
||||
stringstream error;
|
||||
error << responseCode << " not found\n";
|
||||
gWriter.setError( error.str() );
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool DialConformance::validateHttpHeaders(
|
||||
string& headers, string& param )
|
||||
{
|
||||
bool retval = false;
|
||||
string responseCode;
|
||||
extractParamValue( param, responseCode );
|
||||
|
||||
ATRACE("%s: Searching for %s\n", __FUNCTION__, responseCode.c_str());
|
||||
if( headers.find(responseCode) != headers.npos )
|
||||
{
|
||||
ATRACE("%s: %s FOUND\n", __FUNCTION__, responseCode.c_str());
|
||||
retval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ATRACE("%s: %s not found\n", __FUNCTION__, responseCode.c_str());
|
||||
stringstream error;
|
||||
error << responseCode << " not found\n";
|
||||
gWriter.setError( error.str() );
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool DialConformance::validateResponseBody(
|
||||
string& body, string& param )
|
||||
{
|
||||
bool retval = false;
|
||||
string responseCode;
|
||||
extractParamValue( param, responseCode );
|
||||
|
||||
ATRACE("%s: Searching for %s\n", __FUNCTION__, responseCode.c_str());
|
||||
if( body.find(responseCode) != body.npos )
|
||||
{
|
||||
ATRACE("%s: %s FOUND\n", __FUNCTION__, responseCode.c_str());
|
||||
retval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ATRACE("%s: %s not found\n", __FUNCTION__, responseCode.c_str());
|
||||
stringstream error;
|
||||
error << responseCode << " not found\n";
|
||||
error << "response: " << body;
|
||||
gWriter.setError( error.str() );
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void DialConformance::getPayload(vector<string>& params, string& payload )
|
||||
{
|
||||
vector<string>::iterator it;
|
||||
for( it = params.begin(); it < params.end(); it++ )
|
||||
{
|
||||
if( (*it).find("param") != (*it).npos )
|
||||
{
|
||||
// Check to see if the param is using quotes. If it is, split on
|
||||
// that. If not, split on a space
|
||||
size_t posEnd, pos = (*it).find( "=" );
|
||||
if( (*it)[pos+1] == '\"' )
|
||||
{
|
||||
posEnd = (*it).find( "\"", pos+2 );
|
||||
payload = (*it).substr( pos+2, posEnd-(pos+2) );
|
||||
}
|
||||
else
|
||||
{
|
||||
posEnd = (*it).find( " ", pos );
|
||||
payload = (*it).substr( pos+1, posEnd );
|
||||
}
|
||||
ATRACE("payload = %s\n", payload.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DialConformance::execute_launch( Application* pApp, vector<string>& params )
|
||||
{
|
||||
string payload, responseHeaders, responseBody;
|
||||
getPayload( params, payload );
|
||||
pApp->launch( payload, responseHeaders, responseBody );
|
||||
return (validateParams( params, responseHeaders, responseBody ));
|
||||
}
|
||||
|
||||
bool DialConformance::execute_status( Application* pApp, vector<string>& params )
|
||||
{
|
||||
ATRACE("%s::%d\n", __FUNCTION__, __LINE__);
|
||||
string responseHeaders, responseBody;
|
||||
pApp->status( responseHeaders, responseBody );
|
||||
|
||||
bool retval = validateParams( params, responseHeaders, responseBody );
|
||||
if( retval && (responseHeaders.find("200") != responseHeaders.npos) )
|
||||
{
|
||||
// if the status was successful
|
||||
// Ensure the response body contains the application name
|
||||
if( responseBody.find( pApp->getName() ) == responseBody.npos )
|
||||
{
|
||||
retval = false;
|
||||
stringstream error;
|
||||
error << "Reponse XML did not contain application name: " << pApp->getName();
|
||||
gWriter.setError( error.str() );
|
||||
retval = false;
|
||||
}
|
||||
if( responseBody.find( "urn:dial-multiscreen-org:schemas:dial" )
|
||||
== responseBody.npos )
|
||||
{
|
||||
gWriter.setError( "Response Body did not contain DIAL service "
|
||||
"string: urn:dial-multiscreen-org:schemas:dial" );
|
||||
retval = false;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool DialConformance::execute_stop( Application* pApp, vector<string>& params )
|
||||
{
|
||||
ATRACE("%s::%d\n", __FUNCTION__, __LINE__);
|
||||
string responseHeaders, responseBody;
|
||||
pApp->stop(responseHeaders);
|
||||
return (validateParams( params, responseHeaders, responseBody ));
|
||||
}
|
||||
|
||||
DialConformance::Application* DialConformance::getApplication( string& command )
|
||||
{
|
||||
Application* pApp = NULL;
|
||||
|
||||
// extract the application
|
||||
string app;
|
||||
size_t pos = command.find("=");
|
||||
app = command.substr(pos+1, command.length());
|
||||
assert( app.length() > 0 );
|
||||
|
||||
// find the application
|
||||
vector<Application*>::iterator it;
|
||||
for( it = _apps.begin(); it < _apps.end(); it++ )
|
||||
{
|
||||
ATRACE("Comparing: %s.compare(%s)\n", (*it)->getName().c_str(), app.c_str());
|
||||
if( (*it)->getName().compare(app) == 0)
|
||||
{
|
||||
pApp = (*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(pApp != NULL);
|
||||
return pApp;
|
||||
}
|
||||
|
||||
bool DialConformance::execute_command( string& command, vector<string>& params )
|
||||
{
|
||||
ATRACE("%s::%s\n", __FUNCTION__, command.c_str());
|
||||
bool retval = false;
|
||||
if( command.find("launch") != command.npos )
|
||||
retval = execute_launch(getApplication(command), params);
|
||||
else if ( command.find("status") != command.npos )
|
||||
retval = execute_status(getApplication(command), params);
|
||||
else if ( command.find("stop") != command.npos )
|
||||
retval = execute_stop(getApplication(command), params);
|
||||
else if ( command.find("sleep") != command.npos )
|
||||
{
|
||||
string time;
|
||||
size_t pos = command.find("=");
|
||||
time = command.substr(pos+1, command.length());
|
||||
unsigned long milliseconds = atoi(time.c_str());
|
||||
ATRACE("Sleeping %ld milliseconds \n", milliseconds);
|
||||
usleep(milliseconds*1000);
|
||||
retval = true;
|
||||
}
|
||||
else ATRACE("Can't execute command: %s\n", command.c_str());
|
||||
return retval;
|
||||
}
|
||||
|
||||
void DialConformance::run_internal( DialServer* pServer )
|
||||
{
|
||||
string command;
|
||||
vector<string> params;
|
||||
bool retval;
|
||||
string friendlyName, uuid;
|
||||
|
||||
// only execute the test if we have a friendly name and UUID
|
||||
retval = pServer->getFriendlyName( friendlyName );
|
||||
if (retval) pServer->getUuid( uuid );
|
||||
if (retval) gWriter.start( friendlyName, uuid, pServer->getIpAddress() );
|
||||
while( retval && _input.getNextAction(command, params) )
|
||||
{
|
||||
vector<string> commandlist;
|
||||
|
||||
// Check the command here, if it is command=ALL, then we need to loop through
|
||||
// all of the valid applications
|
||||
size_t pos;
|
||||
if( (pos = command.find("=ALL")) != command.npos )
|
||||
{
|
||||
vector<Application*>::iterator it;
|
||||
for( it = _apps.begin(); it < _apps.end(); it++ )
|
||||
{
|
||||
// strip off ALL and append the application name
|
||||
string newCommand = command.substr(0, pos+1);
|
||||
newCommand.append( (*it)->getName() );
|
||||
Application *pApp = getApplication( newCommand );
|
||||
if( pApp != NULL && (!pApp->isErrorApp()) )
|
||||
{
|
||||
ATRACE("Adding command %s\n", newCommand.c_str());
|
||||
commandlist.push_back( newCommand );
|
||||
}
|
||||
|
||||
if( newCommand.find("launch") != newCommand.npos )
|
||||
{
|
||||
// If we are adding launch commands, insert a sleep so that we don't overwhelm
|
||||
// the target
|
||||
stringstream sleep;
|
||||
sleep << "sleep=" << LAUNCH_SLEEP;
|
||||
commandlist.push_back( sleep.str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ATRACE("Adding command %s\n", command.c_str());
|
||||
commandlist.push_back(command);
|
||||
}
|
||||
|
||||
vector<string>::iterator it;
|
||||
for( it = commandlist.begin(); it < commandlist.end(); it++ )
|
||||
{
|
||||
// construct the name of the test
|
||||
ATRACE("***********************" "\n%s: Command: %s \n",
|
||||
__FUNCTION__, (*it).c_str());
|
||||
stringstream test;
|
||||
test << "Command:" << (*it);
|
||||
|
||||
// The input code will always push a parameter. If there are no
|
||||
// parameters, it will push an empty string, so test for that here.
|
||||
if( params.size() > 0 && !params[0].empty() ) test << " Params:";
|
||||
|
||||
vector<string>::iterator it2;
|
||||
for( it2 = params.begin(); it2 < params.end(); it2++)
|
||||
{
|
||||
ATRACE("param: %s ", (*it2).c_str());
|
||||
|
||||
// check to see if we have a really large parameter, if we do,
|
||||
// truncate it to a reasonable length
|
||||
string param = (*it2);
|
||||
if( param.size() > MAX_PARAMETER_LENGTH )
|
||||
{
|
||||
param = (*it2).substr(0, MAX_PARAMETER_LENGTH-3 );
|
||||
param.append("...");
|
||||
}
|
||||
test << param << " ";
|
||||
}
|
||||
|
||||
if( (*it).find("sleep") == (*it).npos )
|
||||
{
|
||||
// set the test name
|
||||
gWriter.setTest( test.str() );
|
||||
|
||||
// run the test
|
||||
bool retval = execute_command( *it, params );
|
||||
printf("%s\n", retval ? "SUCCESS":"FAILED");
|
||||
|
||||
// set the result and prep for the next test
|
||||
gWriter.setResult( retval );
|
||||
gWriter.clearTest();
|
||||
}
|
||||
else
|
||||
{
|
||||
//just sleep here
|
||||
string time;
|
||||
size_t pos = (*it).find("=");
|
||||
time = (*it).substr(pos+1, command.length());
|
||||
unsigned long milliseconds = atoi(time.c_str());
|
||||
ATRACE("Sleeping %ld milliseconds \n", milliseconds);
|
||||
usleep(milliseconds*1000);
|
||||
}
|
||||
}
|
||||
|
||||
// clear the parameters for the next loop
|
||||
params.clear();
|
||||
}
|
||||
if( retval ) gWriter.complete();
|
||||
}
|
||||
|
||||
int DialConformance::run(
|
||||
DialServer* pServer,
|
||||
vector<string>& appList,
|
||||
string &inputFile,
|
||||
string &outputFile )
|
||||
{
|
||||
printf("Running conformance tests from %s and printing report to %s\n\n",
|
||||
inputFile.empty() ?
|
||||
DialClientInput::getDefaultFilename().c_str() :
|
||||
inputFile.c_str(),
|
||||
outputFile.empty() ?
|
||||
defaultOutputFile.c_str() :
|
||||
outputFile.c_str() );
|
||||
//read the input file
|
||||
_input.init(inputFile);
|
||||
if( !outputFile.empty() ) gWriter.setOutputFile( outputFile );
|
||||
|
||||
// Add applications passed by the client (command line)
|
||||
vector<string>::iterator it;
|
||||
for( it = appList.begin(); it < appList.end(); it++ ) _input.addApplication(*it);
|
||||
|
||||
// build the application list
|
||||
vector<string> listOfApps;
|
||||
_input.getApplicationList(listOfApps); // get the list of apps
|
||||
for( it = listOfApps.begin(); it < listOfApps.end(); it++ )
|
||||
{
|
||||
_apps.push_back( new Application(*it, pServer, false) );
|
||||
ATRACE("Adding application: %s\n", (*it).c_str());
|
||||
}
|
||||
|
||||
_input.getErrorApplicationList(listOfApps); // get the list of apps
|
||||
for( it = listOfApps.begin(); it < listOfApps.end(); it++ )
|
||||
{
|
||||
_apps.push_back( new Application(*it, pServer, true) );
|
||||
ATRACE("Adding Error application: %s\n", (*it).c_str());
|
||||
}
|
||||
|
||||
// run the test
|
||||
run_internal( pServer );
|
||||
|
||||
// clean up the application list
|
||||
vector<Application*>::iterator appit;
|
||||
for ( appit = _apps.begin() ; appit < _apps.end(); appit++ ) delete (*appit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
227
client/DialConformance.h
Normal file
227
client/DialConformance.h
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Netflix, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DIALCONFORMANCE_H
|
||||
#define DIALCONFORMANCE_H
|
||||
|
||||
#include <memory>
|
||||
#include "DialServer.h"
|
||||
#include "DialClientInput.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class DialConformance
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a singleton
|
||||
*/
|
||||
static DialConformance* create();
|
||||
|
||||
/**
|
||||
* Get a pointer to the singleton
|
||||
*/
|
||||
static DialConformance* instance(void);
|
||||
|
||||
~DialConformance();
|
||||
|
||||
/**
|
||||
* Run the test
|
||||
*
|
||||
* @param[in] pServer DIAL server to test
|
||||
* @param[in] appList List of applications from the command line
|
||||
* @param[in] inputFile Input file used to drive tests
|
||||
* @param[in] outputFile File for writing the report. Use an
|
||||
* empty string to use the default (report.html).
|
||||
*
|
||||
* @return 0 if successful, !0 otherwise
|
||||
*/
|
||||
int run(
|
||||
DialServer* pServer,
|
||||
vector<string>& appList,
|
||||
string &inputFile,
|
||||
string &outputFile );
|
||||
private:
|
||||
DialConformance();
|
||||
DialServer* _pServer;
|
||||
static DialConformance* sConformance;
|
||||
DialClientInput _input;
|
||||
|
||||
/**
|
||||
* Class to manage state for each application supported by the DIAL
|
||||
* server. The application list is managed by the input file.
|
||||
*/
|
||||
class Application{
|
||||
public:
|
||||
enum State
|
||||
{
|
||||
STOPPED,
|
||||
LAUNCHING, // application has just been launched, state
|
||||
// has not been confirmed
|
||||
LAUNCHED,
|
||||
STOPPING // application has been stopped, state has not
|
||||
// been confirmed
|
||||
};
|
||||
|
||||
Application( string& name, DialServer *pServer, bool isErrorApp ) :
|
||||
_isErrorApp(isErrorApp),
|
||||
_pServer(pServer),
|
||||
_state(STOPPED),
|
||||
_stopurl(""),
|
||||
_name(name) {}
|
||||
~Application(){}
|
||||
|
||||
/**
|
||||
* State Getter/Setters
|
||||
*/
|
||||
string getName() { return _name; }
|
||||
State getState() { return _state; }
|
||||
|
||||
/**
|
||||
* Returns true if the application is not a valid application
|
||||
*/
|
||||
bool isErrorApp() { return _isErrorApp; }
|
||||
|
||||
/**
|
||||
* Launch an this application using the server that was stored when
|
||||
* the class was created.
|
||||
*
|
||||
* @param[in] payload Data that is put into the POST data.
|
||||
* @param[out] responseHeaders HTML response headers
|
||||
* @param[out] responseBody HTML response body
|
||||
*/
|
||||
void launch(
|
||||
string& payload,
|
||||
string& responseHeaders,
|
||||
string& responseBody )
|
||||
{
|
||||
_pServer->launchApplication(
|
||||
_name, payload, responseHeaders, responseBody );
|
||||
// TODO: Set state
|
||||
|
||||
// find Location in the header, store the stop url
|
||||
if( !responseHeaders.empty() )
|
||||
{
|
||||
size_t pos, tmp = responseHeaders.find("Location");
|
||||
if( tmp != responseHeaders.npos )
|
||||
{
|
||||
pos = responseHeaders.find("http", tmp);
|
||||
size_t posEnd = responseHeaders.find("\n", pos+1);
|
||||
if( posEnd != responseHeaders.npos )
|
||||
_stopurl = responseHeaders.substr( pos, posEnd );
|
||||
|
||||
// chomp off the \r\n chars
|
||||
DialConformance::chomp( _stopurl );
|
||||
ATRACE("StopURL = %s********\n", _stopurl.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of this application
|
||||
*
|
||||
* @param[out] responseHeaders HTML response headers
|
||||
* @param[out] responseBody HTML response body
|
||||
*/
|
||||
void status(
|
||||
string& responseHeaders,
|
||||
string& responseBody )
|
||||
{ _pServer->getStatus( _name, responseHeaders, responseBody ); }
|
||||
|
||||
/**
|
||||
* Stop the application
|
||||
*
|
||||
* @param[out] responseHeaders HTML response headers
|
||||
*/
|
||||
void stop(
|
||||
string& responseHeaders )
|
||||
{ stop(_stopurl, responseHeaders ); }
|
||||
|
||||
/**
|
||||
* Stop the application using a custom stop URL.
|
||||
*
|
||||
* @param[out] responseHeaders HTML response headers
|
||||
*/
|
||||
void stop( string& stopurl, string& responseHeaders)
|
||||
{
|
||||
if( !stopurl.empty() )
|
||||
{
|
||||
_pServer->stopApplication( stopurl, responseHeaders );
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else ATRACE("%s: Not sending stop, stop URL is empty\n", __FUNCTION__);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
bool _isErrorApp;
|
||||
DialServer* _pServer;
|
||||
State _state;
|
||||
string _stopurl;
|
||||
string _name;
|
||||
};
|
||||
// list of applications
|
||||
vector<Application*> _apps;
|
||||
|
||||
// Internal helpers
|
||||
void run_internal( DialServer* pServer );
|
||||
bool execute_command( string& command, vector<string>& params );
|
||||
|
||||
// Get the Application pointer from an application string
|
||||
Application* getApplication( string& command );
|
||||
|
||||
// extract the payload from a list of parameters
|
||||
void getPayload(vector<string>& params, string& payload );
|
||||
|
||||
// Command execution functions
|
||||
bool execute_launch( Application* pApp, vector<string>& params );
|
||||
bool execute_status( Application* pApp, vector<string>& params );
|
||||
bool execute_stop( Application* pApp, vector<string>& params );
|
||||
|
||||
// Helper function to extract a parameter.
|
||||
void extractParamValue( string& param, string& value );
|
||||
|
||||
// Validation functions
|
||||
bool validateParams( vector<string>& params,
|
||||
string& responseHeaders, string& responseBody );
|
||||
bool validateHttpResponse( string& headers, string& params );
|
||||
bool validateHttpHeaders( string& headers, string& params );
|
||||
bool validateResponseBody( string& headers, string& params );
|
||||
|
||||
public:
|
||||
// Helper function to chomp off the carriage return line feed.
|
||||
static void chomp(string& str)
|
||||
{
|
||||
string crlf("\r\n");
|
||||
size_t pos = str.find_last_not_of( crlf );
|
||||
if( pos != str.npos )
|
||||
{
|
||||
ATRACE("CHOMP\n");
|
||||
str.erase(pos+1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // DIALCONFORMANCE_H
|
||||
344
client/DialDiscovery.cpp
Normal file
344
client/DialDiscovery.cpp
Normal file
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Netflix, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <curl/curl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "DialDiscovery.h"
|
||||
#include <assert.h>
|
||||
|
||||
#define SSDP_TIMEOUT 30
|
||||
#define SSDP_RESPONSE_TIMEOUT 3
|
||||
|
||||
using namespace std;
|
||||
|
||||
DialDiscovery *DialDiscovery::sDiscovery = 0;
|
||||
|
||||
//static char ip_addr[INET_ADDRSTRLEN] = "127.0.0.1";
|
||||
//static int my_port = 0;
|
||||
static struct sockaddr_in saddr;
|
||||
typedef struct
|
||||
{
|
||||
struct sockaddr_in saddr;
|
||||
int sock;
|
||||
socklen_t addrlen;
|
||||
}search_conn;
|
||||
|
||||
static pthread_mutex_t list_locker = PTHREAD_MUTEX_INITIALIZER;
|
||||
class ScopeLocker
|
||||
{
|
||||
public:
|
||||
ScopeLocker(pthread_mutex_t &m) : mLock(m)
|
||||
{ pthread_mutex_lock(&mLock); }
|
||||
virtual ~ScopeLocker()
|
||||
{ pthread_mutex_unlock(&mLock); }
|
||||
private:
|
||||
pthread_mutex_t mLock;
|
||||
};
|
||||
|
||||
static const char ssdp_msearch[] =
|
||||
"M-SEARCH * HTTP/1.1\r\n"
|
||||
"HOST: 239.255.255.250:1900\r\n"
|
||||
"MAN: \"ssdp:discover\"\r\n"
|
||||
"MX: 10\r\n"
|
||||
"ST: urn:dial-multiscreen-org:service:dial:1\r\n\r\n";
|
||||
|
||||
static string getLocation(char *pResponse)
|
||||
{
|
||||
string loc(pResponse);
|
||||
size_t prev = 0, index = loc.find("\r\n");
|
||||
string locUrl, retUrl;
|
||||
while( index != string::npos ) {
|
||||
locUrl = loc.substr(prev, index-prev);
|
||||
ATRACE("locUrl: ##%s## prev: %d index %d\n", locUrl.c_str(), (int)prev, (int)index );
|
||||
if( locUrl.find("LOCATION: ") != string::npos ) {
|
||||
index = locUrl.find("\r\n");
|
||||
retUrl = locUrl.substr( 10 );
|
||||
break;
|
||||
}
|
||||
|
||||
prev = index+2; // move past the "\r\n" token
|
||||
index = loc.find("\r\n", prev);
|
||||
}
|
||||
return retUrl;
|
||||
}
|
||||
|
||||
static size_t header_cb(void* ptr, size_t size, size_t nmemb, void* userdata)
|
||||
{
|
||||
if ((size * nmemb) != 0) {
|
||||
string parse((char*)ptr);
|
||||
if( parse.find("Application-URL: ") != string::npos ) {
|
||||
size_t index_start = parse.find(":");
|
||||
index_start += 2;
|
||||
size_t index_end = parse.find("\r\n");
|
||||
string *header = static_cast<string*>(userdata);
|
||||
header->append(parse.substr(index_start, index_end-index_start));
|
||||
ATRACE("Apps URL set to %s\n", header->c_str());
|
||||
#ifndef DEBUG
|
||||
}
|
||||
#else
|
||||
} else {
|
||||
ATRACE("%s: Dropping %s\n", __FUNCTION__, (char*)ptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return (size * nmemb);
|
||||
}
|
||||
|
||||
static size_t receiveData(void *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
if ((size * nmemb) != 0) {
|
||||
string *url = static_cast<string*>(userdata);
|
||||
url->append((char*)ptr);
|
||||
}
|
||||
return (size * nmemb);
|
||||
}
|
||||
|
||||
static void getServerInfo( const string &server, string& appsUrl, string& ddxml )
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode res = CURLE_OK;
|
||||
|
||||
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
|
||||
fprintf(stderr, "curl_global_init() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((curl = curl_easy_init()) == NULL) {
|
||||
fprintf(stderr, "curl_easy_init() failed\n");
|
||||
curl_global_cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
ATRACE("Sending ##%s##\n", server.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_URL, server.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb);
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &appsUrl);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveData);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ddxml);
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
(void)(res);
|
||||
curl_easy_cleanup(curl);
|
||||
//curl_global_cleanup();
|
||||
}
|
||||
|
||||
void DialDiscovery::updateServerList(string& server)
|
||||
{
|
||||
ServerMap::const_iterator it;
|
||||
it = mServerMap.find(server);
|
||||
if( it == mServerMap.end() ) {
|
||||
// not found, add it
|
||||
string appsUrl, ddxml;
|
||||
getServerInfo(server, appsUrl, ddxml);
|
||||
mServerMap[server] = new DialServer(server, appsUrl, ddxml);
|
||||
} else {
|
||||
// just mark that we found it
|
||||
(*it).second->setFound(true);
|
||||
}
|
||||
}
|
||||
|
||||
void DialDiscovery::processServer(char *pResponse)
|
||||
{
|
||||
if (strstr(pResponse, "ST: urn:dial-multiscreen-org:service:dial:1")) {
|
||||
string server;
|
||||
|
||||
ScopeLocker s(list_locker);
|
||||
// parse for LOCATION header
|
||||
server = getLocation(pResponse);
|
||||
ATRACE("FOUND server: %s\n", server.c_str());
|
||||
|
||||
// save the appURL in the server
|
||||
updateServerList(server);
|
||||
#ifndef DEBUG
|
||||
}
|
||||
#else
|
||||
} else {
|
||||
ATRACE("Dropping Server\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void *DialDiscovery::receiveResponses(void *p)
|
||||
{
|
||||
search_conn *pConn = (search_conn*)p;
|
||||
int bytes;
|
||||
char buf[4096] = {0,};
|
||||
while (1) {
|
||||
if (-1 == (bytes = recvfrom(pConn->sock, buf, sizeof(buf) - 1, 0,
|
||||
(struct sockaddr *)&pConn->saddr, &pConn->addrlen))) {
|
||||
perror("recvfrom");
|
||||
break;
|
||||
}
|
||||
buf[bytes] = 0;
|
||||
ATRACE("Received [%s:%d] %s\n", __FUNCTION__, __LINE__, buf);
|
||||
DialDiscovery::instance()->processServer(buf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DialDiscovery::cleanServerList(void)
|
||||
{
|
||||
ScopeLocker s(list_locker);
|
||||
|
||||
ServerMap::const_iterator it;
|
||||
vector<string> removal;
|
||||
for( it = mServerMap.begin(); it != mServerMap.end(); it++ )
|
||||
{
|
||||
if( !(*it).second->isFound() ) {
|
||||
removal.push_back((*it).second->getLocation());
|
||||
}
|
||||
}
|
||||
|
||||
// now remove and delete
|
||||
vector<string>::iterator iter;
|
||||
for( iter = removal.begin(); iter != removal.end(); iter ++)
|
||||
{
|
||||
ATRACE("Removing Server: %s\n", (*iter).c_str());
|
||||
DialServer *p = mServerMap[(*iter)];
|
||||
delete (p);
|
||||
mServerMap.erase((*iter));
|
||||
}
|
||||
}
|
||||
|
||||
void DialDiscovery::resetDiscovery(void)
|
||||
{
|
||||
ScopeLocker s(list_locker);
|
||||
ServerMap::const_iterator it;
|
||||
for( it = mServerMap.begin(); it != mServerMap.end(); it++ ) {
|
||||
(*it).second->setFound(false);
|
||||
}
|
||||
}
|
||||
|
||||
void * DialDiscovery::send_mcast(void *p)
|
||||
{
|
||||
int one = 1, my_sock;
|
||||
socklen_t addrlen;
|
||||
//struct ip_mreq mreq;
|
||||
char send_buf[strlen((char*)ssdp_msearch) + INET_ADDRSTRLEN + 256] = {0,};
|
||||
int send_size;
|
||||
pthread_attr_t attr;
|
||||
search_conn connection;
|
||||
|
||||
// send_size = snprintf(send_buf, sizeof(send_buf), ssdp_msearch, ip_addr, my_port);
|
||||
send_size = snprintf(send_buf, sizeof(send_buf), ssdp_msearch);
|
||||
ATRACE("[%s:%d] %s\n", __FUNCTION__, __LINE__, send_buf);
|
||||
|
||||
if (-1 == (my_sock = socket(AF_INET, SOCK_DGRAM, 0))) {
|
||||
perror("socket");
|
||||
exit(1);
|
||||
}
|
||||
if (-1 == setsockopt(my_sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) {
|
||||
perror("reuseaddr");
|
||||
exit(1);
|
||||
}
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_addr.s_addr = inet_addr("239.255.255.250");
|
||||
saddr.sin_port = htons(1900);
|
||||
|
||||
while (1) {
|
||||
addrlen = sizeof(saddr);
|
||||
ATRACE("Sending SSDP M-SEARCH to %s:%d\n",
|
||||
inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
|
||||
if (-1 == sendto(my_sock, send_buf, send_size, 0, (struct sockaddr *)&saddr, addrlen)) {
|
||||
perror("sendto");
|
||||
continue;
|
||||
}
|
||||
|
||||
// set all servers to not found
|
||||
DialDiscovery::instance()->resetDiscovery();
|
||||
|
||||
// spawn a response thread
|
||||
connection.saddr = saddr;
|
||||
connection.sock = my_sock;
|
||||
connection.addrlen = addrlen;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||
pthread_create(&DialDiscovery::instance()->_responseThread, &attr, DialDiscovery::receiveResponses, &connection);
|
||||
|
||||
// sleep SSDP_RESPONSE_TIMEOUT seconds to allow clients to response
|
||||
sleep(SSDP_RESPONSE_TIMEOUT);
|
||||
DialDiscovery::instance()->cleanServerList();
|
||||
|
||||
sleep(SSDP_TIMEOUT-SSDP_RESPONSE_TIMEOUT);
|
||||
pthread_cancel(DialDiscovery::instance()->_responseThread);
|
||||
}
|
||||
}
|
||||
|
||||
void DialDiscovery::init()
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
||||
pthread_create(&_mcastThread, &attr, DialDiscovery::send_mcast, (void*)ssdp_msearch );
|
||||
}
|
||||
|
||||
void DialDiscovery::getServerList( vector<DialServer*>& list )
|
||||
{
|
||||
for( ServerMap::iterator it = mServerMap.begin(); it != mServerMap.end(); ++it ) {
|
||||
list.push_back( it->second );
|
||||
}
|
||||
}
|
||||
|
||||
bool DialDiscovery::getServer( const string& friendlyName, DialServer &server )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
DialDiscovery::DialDiscovery()
|
||||
{
|
||||
assert( DialDiscovery::sDiscovery == 0 );
|
||||
DialDiscovery::sDiscovery = this;
|
||||
}
|
||||
|
||||
DialDiscovery::~DialDiscovery()
|
||||
{
|
||||
assert( sDiscovery == this );
|
||||
sDiscovery = 0;
|
||||
}
|
||||
|
||||
DialDiscovery * DialDiscovery::create()
|
||||
{
|
||||
assert( sDiscovery == 0 );
|
||||
return new DialDiscovery();
|
||||
}
|
||||
|
||||
DialDiscovery * DialDiscovery::instance()
|
||||
{
|
||||
return DialDiscovery::sDiscovery;
|
||||
}
|
||||
|
||||
94
client/DialDiscovery.h
Normal file
94
client/DialDiscovery.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Netflix, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DIALDISCOVERY_H
|
||||
#define DIALDISCOVERY_H
|
||||
|
||||
#include "DialServer.h"
|
||||
#include <map>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class DialDiscovery
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a singleton
|
||||
*/
|
||||
static DialDiscovery* create(void);
|
||||
|
||||
/**
|
||||
* Get a pointer to the singleton
|
||||
*/
|
||||
static DialDiscovery* instance(void);
|
||||
|
||||
~DialDiscovery();
|
||||
|
||||
/**
|
||||
* Initialize the discover object. This will kick off a periodic
|
||||
* worker thread that will poll for DIAL servers.
|
||||
*
|
||||
*/
|
||||
void init();
|
||||
|
||||
/**
|
||||
* Get the list of servers that have been discovered.
|
||||
*
|
||||
* @param[out] list List of DIAL servers. Returns an empty list if there
|
||||
* are no servers.
|
||||
*/
|
||||
void getServerList(vector<DialServer*>& list);
|
||||
|
||||
/**
|
||||
* Get a DIAL server based on friendly name
|
||||
*
|
||||
* @param[in] friendlyName Friendly name of DIAL server
|
||||
* @param[out] server Server object (if successful)
|
||||
*
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool getServer(
|
||||
const string& friendlyName,
|
||||
DialServer &server );
|
||||
|
||||
private:
|
||||
DialDiscovery();
|
||||
void updateServerList(string& server);
|
||||
static void *receiveResponses(void *p);
|
||||
static void *send_mcast(void *p);
|
||||
void processServer(char *pResponse);
|
||||
void cleanServerList();
|
||||
void resetDiscovery();
|
||||
|
||||
pthread_t _mcastThread;
|
||||
pthread_t _responseThread;
|
||||
|
||||
typedef map<string, DialServer*> ServerMap;
|
||||
ServerMap mServerMap;
|
||||
|
||||
static DialDiscovery* sDiscovery;
|
||||
};
|
||||
|
||||
#endif // DIALDISCOVERY_H
|
||||
249
client/DialServer.cpp
Normal file
249
client/DialServer.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Netflix, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "DialServer.h"
|
||||
#include <curl/curl.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
enum DIAL_COMMAND{
|
||||
COMMAND_LAUNCH,
|
||||
COMMAND_STATUS,
|
||||
COMMAND_KILL
|
||||
};
|
||||
|
||||
static size_t header_cb(void* ptr, size_t size, size_t nmemb, void* userdata)
|
||||
{
|
||||
if ((size * nmemb) != 0) {
|
||||
string newHeader((char*)ptr);
|
||||
string *header = static_cast<string*>(userdata);
|
||||
header->append(newHeader);
|
||||
ATRACE("%s: Adding header: %s", __FUNCTION__, newHeader.c_str());
|
||||
}
|
||||
return (size * nmemb);
|
||||
}
|
||||
|
||||
static size_t receiveData(void *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
{
|
||||
if ((size * nmemb) != 0) {
|
||||
string *body= static_cast<string*>(userdata);
|
||||
body->append((char*)ptr);
|
||||
ATRACE("%s: Adding to Body: %s", __FUNCTION__, (char*)ptr);
|
||||
}
|
||||
return (size * nmemb);
|
||||
}
|
||||
|
||||
int DialServer::sendCommand(
|
||||
string &url,
|
||||
int command,
|
||||
string &payload,
|
||||
string &responseHeaders,
|
||||
string &responseBody )
|
||||
{
|
||||
CURL *curl;
|
||||
CURLcode res = CURLE_OK;
|
||||
|
||||
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
|
||||
{
|
||||
fprintf(stderr, "curl_global_init() failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((curl = curl_easy_init()) == NULL)
|
||||
{
|
||||
fprintf(stderr, "curl_easy_init() failed\n");
|
||||
curl_global_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command == COMMAND_LAUNCH)
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_POST, true);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, payload.size());
|
||||
if( payload.size() )
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload.c_str());
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else ATRACE("Sending empty POST\n");
|
||||
#endif
|
||||
}
|
||||
else if (command == COMMAND_KILL)
|
||||
{
|
||||
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
|
||||
}
|
||||
ATRACE("Sending %s:%s\n",
|
||||
command == COMMAND_LAUNCH ? "LAUNCH" :
|
||||
(command == COMMAND_KILL ? "KILL" : "STATUS"),
|
||||
url.c_str());
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb);
|
||||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &responseHeaders);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receiveData);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBody);
|
||||
res = curl_easy_perform(curl);
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
curl_global_cleanup();
|
||||
return (res == CURLE_OK);
|
||||
}
|
||||
|
||||
string DialServer::getIpAddress()
|
||||
{
|
||||
// m_appsUrl=http://192.168.1.103:36269/apps/
|
||||
if( m_ipAddr.empty() )
|
||||
{
|
||||
size_t begin = m_appsUrl.find("//");
|
||||
if( begin != m_appsUrl.npos )
|
||||
{
|
||||
begin += 2; // move to the start of the IP address
|
||||
size_t end = m_appsUrl.find(":", begin);
|
||||
if( end != m_appsUrl.npos )
|
||||
{
|
||||
m_ipAddr = m_appsUrl.substr( begin, end-begin );
|
||||
ATRACE("IP ADDRESS: %s\n", m_ipAddr.c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_ipAddr;
|
||||
}
|
||||
|
||||
bool DialServer::getFriendlyName( string& name )
|
||||
{
|
||||
bool retval = false;
|
||||
if( !m_ddxml.empty() )
|
||||
{
|
||||
string friendlyName = "<friendlyName>";
|
||||
size_t pos;
|
||||
if( ( pos = m_ddxml.find( friendlyName ) ) != m_ddxml.npos )
|
||||
{
|
||||
string friendlyNameEnd = "</friendlyName>";
|
||||
size_t end = m_ddxml.find( friendlyNameEnd );
|
||||
name = m_ddxml.substr( pos + friendlyName.size(),
|
||||
(end - (pos + friendlyName.size())) );
|
||||
ATRACE("***Friendly name=%s***", name.c_str());
|
||||
retval = true;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else
|
||||
{
|
||||
ATRACE("Friendly name not found\n%s\n", m_ddxml.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool DialServer::getUuid( string& uuid )
|
||||
{
|
||||
bool retval = false;
|
||||
if( !m_ddxml.empty() )
|
||||
{
|
||||
string udn = "<UDN>";
|
||||
size_t pos;
|
||||
if( ( pos = m_ddxml.find( udn ) ) != m_ddxml.npos )
|
||||
{
|
||||
string udnEnd = "</UDN>";
|
||||
size_t end = m_ddxml.find( udnEnd );
|
||||
uuid = m_ddxml.substr( pos + udn.size(),
|
||||
(end - (pos + udn.size())) );
|
||||
ATRACE("***UUID=%s***", uuid.c_str() );
|
||||
retval = true;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else
|
||||
{
|
||||
ATRACE("Friendly name not found\n%s\n", m_ddxml.c_str());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int DialServer::launchApplication(
|
||||
string &application,
|
||||
string &payload,
|
||||
string &responseHeaders,
|
||||
string &responseBody )
|
||||
{
|
||||
ATRACE("%s: Launch %s\n", __FUNCTION__, application.c_str());
|
||||
string appUrl = m_appsUrl;
|
||||
sendCommand( appUrl.append(application), COMMAND_LAUNCH, payload, responseHeaders, responseBody);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DialServer::getStatus(
|
||||
string &application,
|
||||
string &responseHeaders,
|
||||
string &responseBody )
|
||||
{
|
||||
ATRACE("%s: GetStatus %s\n", __FUNCTION__, application.c_str());
|
||||
string emptyPayload;
|
||||
string appUrl = m_appsUrl;
|
||||
sendCommand( appUrl.append(application), COMMAND_STATUS, emptyPayload, responseHeaders, responseBody );
|
||||
|
||||
ATRACE("Body: %s\n", responseBody.c_str());
|
||||
unsigned found = responseBody.find("href=");
|
||||
if( found != string::npos )
|
||||
{
|
||||
// The start of href is after href= and the quote
|
||||
unsigned href_start = found + 5 + 1;
|
||||
|
||||
// get the body from the start of href to the end, then find
|
||||
// the last quote delimiter.
|
||||
string tmp = responseBody.substr( href_start );
|
||||
unsigned href_end = tmp.find("\"");
|
||||
m_stopEndPoint = responseBody.substr( href_start, href_end );
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DialServer::stopApplication(
|
||||
string &application,
|
||||
string &responseHeaders )
|
||||
{
|
||||
ATRACE("%s: Quit %s\n", __FUNCTION__, application.c_str());
|
||||
string emptyPayload, responseBody; // dropping this
|
||||
string appUrl = m_appsUrl;
|
||||
|
||||
// just call status to update the run endpoint
|
||||
getStatus( application, responseHeaders, responseBody );
|
||||
|
||||
sendCommand(
|
||||
(appUrl.append(application)).append("/"+m_stopEndPoint),
|
||||
COMMAND_KILL, emptyPayload, responseHeaders, responseBody );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DialServer::getHttpResponseHeader(
|
||||
string &responseHeaders,
|
||||
string &header,
|
||||
string &value )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
181
client/DialServer.h
Normal file
181
client/DialServer.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Netflix, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef DIALSERVER_H
|
||||
#define DIALSERVER_H
|
||||
|
||||
//#define DEBUG
|
||||
#ifdef DEBUG
|
||||
#define ATRACE(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define ATRACE(...)
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class DialServer
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Dial Server ctor
|
||||
*
|
||||
* @param[in] location dd.xml LOCATION header
|
||||
* @param[in] appsUrl Parsed out Application URL
|
||||
* @param[in] location dd.xml LOCATION header
|
||||
* empty string to find any server
|
||||
*
|
||||
*/
|
||||
DialServer( string location, string appsUrl, string dd_xml ) :
|
||||
m_location(location),
|
||||
m_appsUrl(appsUrl),
|
||||
found(true),
|
||||
m_ddxml(dd_xml)
|
||||
{}
|
||||
|
||||
~DialServer() { ATRACE("%s\n", __FUNCTION__); }
|
||||
|
||||
/**
|
||||
* Get the DIAL Server location
|
||||
*
|
||||
* @return Location of the server (http://<IP_ADDR>:<PORT>/dd.xml)
|
||||
*/
|
||||
string getLocation() { return m_location; }
|
||||
|
||||
/**
|
||||
* Get the DIAL Server IP address
|
||||
*
|
||||
* @return IP address of the server (X.X.X.X)
|
||||
*/
|
||||
string getIpAddress();
|
||||
|
||||
/**
|
||||
* Get the DIAL REST endpoint
|
||||
*
|
||||
* @return Location of the server (http://<IP_ADDR>:<PORT>/apps)
|
||||
*/
|
||||
string getAppsUrl() { return m_appsUrl; }
|
||||
|
||||
/**
|
||||
* Get the DIAL friendly name
|
||||
*
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool getFriendlyName( string& name );
|
||||
|
||||
/**
|
||||
* Get the DIAL UUID
|
||||
*
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool getUuid( string& uuid );
|
||||
|
||||
/**
|
||||
* Launch a DIAL application
|
||||
*
|
||||
* @param[in] application Name of the application to launch
|
||||
* @param[in] payload launch POST data
|
||||
* @param[out] responseHeaders Returns the HTTP response headers
|
||||
* @param[out] responseBody Returns the HTTP response body
|
||||
*
|
||||
* @return 0 if successful, !0 otherwise
|
||||
*/
|
||||
int launchApplication(
|
||||
string &application,
|
||||
string &payload,
|
||||
string &responseHeaders,
|
||||
string &responseBody );
|
||||
|
||||
/**
|
||||
* Get the status of a DIAL application
|
||||
*
|
||||
* @param[in] application Name of the application to query
|
||||
* @param[out] responseHeaders Returns the HTTP response headers
|
||||
* @param[out] responseBody Returns the HTTP response body
|
||||
*
|
||||
* @return 0 if successful, !0 otherwise
|
||||
*/
|
||||
int getStatus(
|
||||
string &application,
|
||||
string &responseHeaders,
|
||||
string &responseBody );
|
||||
|
||||
/**
|
||||
* Stop an application.
|
||||
*
|
||||
* @param[in] application Name of the application to stop
|
||||
* @param[out] responseHeaders Returns the HTTP response headers
|
||||
*
|
||||
* @return 0 if successful, !0 otherwise
|
||||
*/
|
||||
int stopApplication(
|
||||
string &application,
|
||||
string &responseHeaders );
|
||||
|
||||
|
||||
/** ********************* **/
|
||||
/** Convenience functions **/
|
||||
/** ********************* **/
|
||||
|
||||
/**
|
||||
* Extract a header from the response
|
||||
*
|
||||
* @param[in] responseHeaders Response headers
|
||||
* @param[in] header Header value to extract
|
||||
* @param[out] value Value of the header provided
|
||||
*
|
||||
* @return 0 if successful, !0 otherwise
|
||||
*/
|
||||
int getHttpResponseHeader(
|
||||
string &responseHeaders,
|
||||
string &header,
|
||||
string &value );
|
||||
|
||||
/**
|
||||
* Returns true if the server has been recently found
|
||||
*
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool isFound() { return found; }
|
||||
|
||||
/**
|
||||
* Sets the "found status" of this server
|
||||
*/
|
||||
void setFound(bool b) { found = b; }
|
||||
|
||||
private:
|
||||
int sendCommand( string &url, int command, string &payload,
|
||||
string &responseHeaders, string &responseBody );
|
||||
string m_location;
|
||||
string m_appsUrl;
|
||||
string m_ipAddr;
|
||||
bool found;
|
||||
string m_ddxml; // information about the device
|
||||
string m_stopEndPoint;
|
||||
};
|
||||
|
||||
#endif // DIALSERVER_H
|
||||
5
client/build.sh
Executable file
5
client/build.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
TARGET=/usr/local/i686-netflix-linux-gnu-4.2/bin/i686-netflix-linux-gnu- \
|
||||
LDFLAGS="-L/usr/local/i686-netflix-linux-gnu-4.2/netflix/lib \
|
||||
-Wl,-rpath,/usr/local/i686-netflix-linux-gnu-4.2/netflix/lib" \
|
||||
INCLUDES=-I/usr/local/i686-netflix-linux-gnu-4.2/netflix/include \
|
||||
make
|
||||
80
client/dialclient_input.txt
Normal file
80
client/dialclient_input.txt
Normal file
@@ -0,0 +1,80 @@
|
||||
# Input file for DIAL client
|
||||
|
||||
# List the applications defined.
|
||||
# Valid applications that should exist on your DIAL server implementation
|
||||
addApplication=Netflix
|
||||
addApplication=YouTube
|
||||
|
||||
# Error applications that should not exist on your DIAL server implementation
|
||||
addErrorApplication=netflix
|
||||
addErrorApplication=Netflix1
|
||||
|
||||
# Launch the Netflix application
|
||||
# HTTP response should be 201
|
||||
# HTTP response header should contain a Location header
|
||||
launch=Netflix httpresponse=201 httpresponse=Created httpresponseheader=Location
|
||||
sleep=6000
|
||||
|
||||
# Get the status of the application, it should be running
|
||||
status=Netflix httpresponse=200 resultbody=state resultbody=running
|
||||
|
||||
# Stop the Netflix application, wait for 1 second while it shuts down.
|
||||
stop=Netflix
|
||||
sleep=1000
|
||||
|
||||
# Call stop again and ensure the implementation returns the proper response
|
||||
stop=Netflix httpresponse=404
|
||||
|
||||
# Get the status of the Netflix application and ensure it returns stopped
|
||||
status=Netflix httpresponse=200 resultbody=state resultbody=stopped
|
||||
sleep=2000
|
||||
|
||||
# Launch the application with a parameter
|
||||
# NOTE: This test can not ensure that the parameter was taken properly
|
||||
launch=Netflix param="NETFLIX&WAS+HERE" httpresponse=201 httpresponse=Created httpresponseheader=Location
|
||||
sleep=6000
|
||||
|
||||
# Ensure the application is running
|
||||
status=Netflix httpresponse=200 resultbody=state resultbody=running
|
||||
|
||||
# This will launch Netflix with the same parameter. *Netflix should not relaunch*
|
||||
launch=Netflix param="NETFLIX&WAS*HERE" httpresponse=201 httpresponse=Created httpresponseheader=Location
|
||||
sleep=6000
|
||||
|
||||
# Ensure the application is running
|
||||
status=Netflix httpresponse=200 resultbody=state resultbody=running
|
||||
|
||||
# This will launch Netflix with a different parameter. *Netflix should not relaunch*
|
||||
launch=Netflix param="&&&%%%ThisShouldRelaunch%%%" httpresponse=201 httpresponse=Created httpresponseheader=Location
|
||||
sleep=6000
|
||||
|
||||
# Ensure the application is running
|
||||
status=Netflix httpresponse=200 resultbody=state resultbody=running
|
||||
|
||||
# Stop Netflix
|
||||
stop=Netflix
|
||||
sleep=1500
|
||||
|
||||
# Make sure netflix is reported as stopped
|
||||
status=Netflix httpresponse=200 resultbody=state resultbody=stopped
|
||||
|
||||
# Test a parameter with over 4096 bytes and ensure 413 is returned.
|
||||
launch=Netflix param="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" httpresponse=413
|
||||
|
||||
# Test for case sensitivity
|
||||
launch=netflix httpresponse=404
|
||||
sleep=200
|
||||
|
||||
# Ensure the server is checking the full application name
|
||||
launch=Netflix1 httpresponse=404
|
||||
sleep=200
|
||||
|
||||
# This executes correctly, but fails the XML
|
||||
status=Netflix1 httpresponse=404
|
||||
sleep=200
|
||||
|
||||
# Currently can't run this test, the code won't execute a stop without a stop URL.
|
||||
#stop=Netflix1 httpresponse=404
|
||||
|
||||
# Test ALL valid applications
|
||||
status=ALL httpresponse=200 resultbody=state resultbody=stopped
|
||||
298
client/main.cpp
Normal file
298
client/main.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Netflix, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NETFLIX, INC. AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NETFLIX OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include "DialDiscovery.h"
|
||||
#include "DialConformance.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static DialDiscovery* gpDiscovery;
|
||||
static bool gUseMenu = true;
|
||||
|
||||
// TODO: Make it possible to pass applications from the command line
|
||||
static vector<string> gAppList;
|
||||
static string gOutputFile;
|
||||
static string gInputFile;
|
||||
|
||||
// IP address of the DIAL server
|
||||
static string gIpAddress;
|
||||
|
||||
static void printServerList( vector<DialServer*> list )
|
||||
{
|
||||
int i;
|
||||
vector<DialServer*>::iterator it;
|
||||
for( i = 0, it = list.begin(); it < list.end(); it++, i++ )
|
||||
{
|
||||
string uuid, name;
|
||||
(*it)->getFriendlyName( name );
|
||||
(*it)->getUuid( uuid );
|
||||
printf("%Zu: Server IP[%s] UUID[%s] FriendlyName[%s] \n",
|
||||
i+1, (*it)->getIpAddress().c_str(),
|
||||
uuid.c_str(), name.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
static DialServer* getServerFromUser( vector<DialServer*> list )
|
||||
{
|
||||
DialServer* pServer;
|
||||
// show a list to the user
|
||||
if( list.size() > 1 )
|
||||
{
|
||||
char buf[80] = {0,};
|
||||
vector<DialServer*>::iterator it;
|
||||
|
||||
printf("Found Multiple servers\n");
|
||||
printServerList(list);
|
||||
printf("Enter server: ");
|
||||
scanf("%s", buf);
|
||||
unsigned int server = atoi(buf);
|
||||
assert( server > 0 && server <= list.size() );
|
||||
pServer = list[server-1];
|
||||
}
|
||||
else
|
||||
{
|
||||
pServer = list.front();
|
||||
}
|
||||
return pServer;
|
||||
}
|
||||
|
||||
static void runConformance()
|
||||
{
|
||||
vector<DialServer*> list;
|
||||
gpDiscovery->getServerList(list);
|
||||
|
||||
if( list.size() )
|
||||
{
|
||||
DialServer *pServer = NULL;
|
||||
if( !gIpAddress.empty() )
|
||||
{
|
||||
pServer = NULL;
|
||||
vector<DialServer*>::iterator it;
|
||||
for( it = list.begin(); it < list.end(); it ++ )
|
||||
{
|
||||
if( gIpAddress.compare((*it)->getIpAddress()) == 0 )
|
||||
{
|
||||
ATRACE("Found server %s in the list of servers\n",
|
||||
gIpAddress.c_str() );
|
||||
pServer = (*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pServer = getServerFromUser( list );
|
||||
}
|
||||
|
||||
if( pServer )
|
||||
{
|
||||
string name;
|
||||
bool serverExists = pServer->getFriendlyName(name);
|
||||
assert( serverExists );
|
||||
string uuid;
|
||||
pServer->getUuid( uuid );
|
||||
printf("\nRunning conformance against: IP[%s] UUID[%s] FriendlyName[%s] \n",
|
||||
pServer->getIpAddress().c_str(),
|
||||
uuid.c_str(), name.c_str() );
|
||||
DialConformance::instance()->run(
|
||||
pServer,
|
||||
gAppList,
|
||||
gInputFile,
|
||||
gOutputFile );
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("DIAL server not found\n");
|
||||
printf("%Zu available server(s): \n", list.size());
|
||||
printServerList(list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("No servers available\n");
|
||||
}
|
||||
}
|
||||
|
||||
int handleUser(DialDiscovery *pDial) {
|
||||
int processInput = 1;
|
||||
char buf[80];
|
||||
vector<DialServer*> list;
|
||||
|
||||
pDial->getServerList(list);
|
||||
if( list.size() == 0 )
|
||||
{
|
||||
printf("No servers available\n");
|
||||
return 1;
|
||||
}
|
||||
DialServer* pServer = getServerFromUser( list );
|
||||
|
||||
while(processInput)
|
||||
{
|
||||
string responseHeaders, responseBody, payload;
|
||||
string netflix = "Netflix";
|
||||
string youtube = "YouTube";
|
||||
|
||||
memset(buf, 0, 80);
|
||||
printf("0. List DIAL servers\n");
|
||||
printf("1. Launch Netflix\n");
|
||||
printf("2. Kill Netflix\n");
|
||||
printf("3. Netflix status\n");
|
||||
printf("4. Launch YouTube\n");
|
||||
printf("5. Kill YouTube\n");
|
||||
printf("6. YouTube status\n");
|
||||
printf("7. Run conformance tests\n");
|
||||
printf("8. QUIT\n");
|
||||
printf("Command (0:1:2:3:4:5:6:7:8): ");
|
||||
scanf("%s", buf);
|
||||
switch( atoi(buf) )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
printf("\n\n******** %Zu servers found ********\n\n", list.size());
|
||||
for( unsigned int i = 0; i < list.size(); i++ )
|
||||
{
|
||||
string name;
|
||||
list[i]->getFriendlyName(name);
|
||||
printf("Server %Zu: %s\n", i+1, name.c_str());
|
||||
}
|
||||
printf("\n*********************************\n\n");
|
||||
}break;
|
||||
case 1:
|
||||
printf("Launch Netflix\n");
|
||||
pServer->launchApplication( netflix, payload, responseHeaders, responseBody );
|
||||
break;
|
||||
case 2:
|
||||
printf("Kill Netflix\n");
|
||||
pServer->stopApplication( netflix, responseHeaders );
|
||||
break;
|
||||
case 3:
|
||||
printf("Netflix Status: \n");
|
||||
pServer->getStatus( netflix, responseHeaders, responseBody );
|
||||
printf("RESPONSE: \n%s\n", responseBody.c_str());
|
||||
break;
|
||||
case 4:
|
||||
printf("Launch YouTube\n");
|
||||
pServer->launchApplication( youtube, payload, responseHeaders, responseBody );
|
||||
break;
|
||||
case 5:
|
||||
printf("Kill YouTube\n");
|
||||
pServer->stopApplication( youtube, responseHeaders );
|
||||
break;
|
||||
case 6:
|
||||
printf("YouTube Status: \n");
|
||||
pServer->getStatus( youtube, responseHeaders, responseBody );
|
||||
break;
|
||||
case 7:
|
||||
runConformance();
|
||||
break;
|
||||
case 8:
|
||||
processInput = 0;
|
||||
break;
|
||||
default:
|
||||
printf("Invalid, try again\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char usage[] = "\n"
|
||||
" If no option is specified, the program will run a conformance test.\n"
|
||||
"\n"
|
||||
"usage: dialclient <option>\n"
|
||||
" Option Parameter Description\n"
|
||||
" -h none Usage menu\n"
|
||||
" -s filename (optional) Run conformance test. Use filename as\n"
|
||||
" the input, if provided\n"
|
||||
" -o filename Reporter output file (./report.html)\n"
|
||||
" -a ip_address IP address of DIAL server (used for conformance\n"
|
||||
" testing)\n"
|
||||
"\n";
|
||||
|
||||
inline void notSupported( string s )
|
||||
{
|
||||
printf( "%s not supported", s.c_str() );
|
||||
printf( "%s\n", usage );
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int parseArgs( int argc, char* argv[] )
|
||||
{
|
||||
for( int i = 1; i < argc; i++ )
|
||||
{
|
||||
string input(argv[i]);
|
||||
if( input[0] != '-' ) notSupported(input);
|
||||
switch(input[1])
|
||||
{
|
||||
case 's':
|
||||
gUseMenu = false;
|
||||
if( argv[i+1] != NULL && argv[i+1][0] != '-' )
|
||||
{
|
||||
//filename provided
|
||||
gInputFile = argv[++i];
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
gOutputFile = argv[++i];
|
||||
break;
|
||||
case 'a':
|
||||
gIpAddress = argv[++i];
|
||||
break;
|
||||
case 'h':
|
||||
printf("%s", usage);
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
notSupported(input);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
parseArgs(argc, argv);
|
||||
|
||||
gpDiscovery = DialDiscovery::create();
|
||||
gpDiscovery->init();
|
||||
DialConformance::create();
|
||||
|
||||
// Sleep for 2 seconds to allow DIAL servers to response to MSEARCH.
|
||||
sleep(2);
|
||||
|
||||
if ( gUseMenu )
|
||||
{
|
||||
return handleUser(gpDiscovery);
|
||||
}
|
||||
|
||||
// not using the menu, just run the conformance test.
|
||||
runConformance();
|
||||
return 0;
|
||||
}
|
||||
|
||||
14
client/makefile
Normal file
14
client/makefile
Normal file
@@ -0,0 +1,14 @@
|
||||
CC=$(TARGET)g++
|
||||
|
||||
.PHONY: clean
|
||||
.DEFAULT_GOAL=dialclient
|
||||
|
||||
includes = $(wildcard *.h)
|
||||
OBJS := main.cpp DialServer.cpp DialDiscovery.cpp DialConformance.cpp DialClientInput.cpp
|
||||
|
||||
# You may not need all these libraries. This example uses a build of curl that needs crypto, ssl, cares, and zlib
|
||||
dialclient: $(OBJS) ${includes}
|
||||
$(CC) -Wall -Werror -g $(OBJS) $(INCLUDES) $(LDFLAGS) -ldl -lpthread -lcurl -lz -lcrypto -lssl -lcares -m32 -o dialclient
|
||||
|
||||
clean:
|
||||
rm -f *.o dialclient
|
||||
Reference in New Issue
Block a user