1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-01-18 23:17:59 +01:00

Do a native (Qt) request if the 'Referer' header was supplied

This commit is contained in:
Uwe L. Korn 2014-06-06 00:32:32 +01:00
parent 4e9a088af4
commit 655e63f261
3 changed files with 137 additions and 16 deletions

View File

@ -277,6 +277,35 @@ Tomahawk.syncRequest = function (url, extraHeaders, options) {
}
};
/**
* Internal counter used to identify asyncRequest callback from native code.
*/
Tomahawk.asyncRequestIdCounter = 0;
/**
* Internal map used to map asyncRequestIds to the respective javascript
* callback functions.
*/
Tomahawk.asyncRequestCallbacks = {};
/**
* Pass the natively retrived reply back to the javascript callback
* and augment the fake XMLHttpRequest object.
*
* Internal use only!
*/
Tomahawk.nativeAsyncRequestDone = function (reqId, xhr) {
// Check we have a matching callback stored.
if (!Tomahawk.asyncRequestCallbacks.hasOwnProperty(reqId)) {
return;
}
// Call the real callback
Tomahawk.asyncRequestCallbacks[reqId](xhr);
// Callback are only used once.
delete Tomahawk.asyncRequestCallbacks[reqId];
};
/**
* Possible options:
* - method: The HTTP request method (default: GET)
@ -289,26 +318,40 @@ Tomahawk.asyncRequest = function (url, callback, extraHeaders, options) {
// unpack options
var opt = options || {};
var method = opt.method || 'GET';
var doNativeRequest = false;
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open(method, url, true, opt.username, opt.password);
if (extraHeaders) {
for (var headerName in extraHeaders) {
xmlHttpRequest.setRequestHeader(headerName, extraHeaders[headerName]);
}
if (extraHeaders && (extraHeaders.hasOwnProperty("Referer") || extraHeaders.hasOwnProperty("referer"))) {
doNativeRequest = true;
}
xmlHttpRequest.onreadystatechange = function () {
if (xmlHttpRequest.readyState == 4 && xmlHttpRequest.status == 200) {
callback.call(window, xmlHttpRequest);
} else if (xmlHttpRequest.readyState === 4) {
Tomahawk.log("Failed to do " + method + " request: to: " + url);
Tomahawk.log("Status Code was: " + xmlHttpRequest.status);
if (opt.hasOwnProperty('errorHandler')) {
opt.errorHandler.call(window, xmlHttpRequest);
if (doNativeRequest) {
// Assign a request Id to the callback so we can use it when we are
// returning from the native call.
var reqId = Tomahawk.asyncRequestIdCounter;
Tomahawk.asyncRequestIdCounter++;
Tomahawk.asyncRequestCallbacks[reqId] = callback;
Tomahawk.nativeAsyncRequest(reqId, url, extraHeaders, options);
} else {
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open(method, url, true, opt.username, opt.password);
if (extraHeaders) {
for (var headerName in extraHeaders) {
xmlHttpRequest.setRequestHeader(headerName, extraHeaders[headerName]);
}
}
};
xmlHttpRequest.send(opt.data || null);
xmlHttpRequest.onreadystatechange = function () {
if (xmlHttpRequest.readyState == 4 && xmlHttpRequest.status == 200) {
callback.call(window, xmlHttpRequest);
} else if (xmlHttpRequest.readyState === 4) {
Tomahawk.log("Failed to do " + method + " request: to: " + url);
Tomahawk.log("Status Code was: " + xmlHttpRequest.status);
if (opt.hasOwnProperty('errorHandler')) {
opt.errorHandler.call(window, xmlHttpRequest);
}
}
};
xmlHttpRequest.send(opt.data || null);
}
};
Tomahawk.sha256 = Tomahawk.sha256 || CryptoJS.SHA256;

View File

@ -28,6 +28,7 @@
#include "resolvers/ScriptEngine.h"
#include "network/Servent.h"
#include "utils/Closure.h"
#include "utils/Json.h"
#include "utils/NetworkAccessManager.h"
#include "utils/NetworkReply.h"
#include "utils/Logger.h"
@ -487,6 +488,69 @@ JSResolverHelper::reportStreamUrl( const QString& qid,
}
void
JSResolverHelper::nativeAsyncRequest( const int requestId, const QString& url,
const QVariantMap& headers,
const QVariantMap& options )
{
QNetworkRequest req( url );
foreach ( const QString& key , headers.keys() ) {
req.setRawHeader( key.toLatin1(), headers[key].toString().toLatin1() );
}
if ( options.contains( "username" ) && options.contains( "password" ) )
{
// If we have sufficient authentication data, we will send
// username+password as HTTP Basic Auth
QString credentials = QString( "Basic %1" )
.arg( QString( QString("%1:%2")
.arg( options["username"].toString() )
.arg( options["password"].toString() )
.toLatin1().toBase64() )
);
req.setRawHeader( "Authorization", credentials.toLatin1() );
}
NetworkReply* reply = NULL;
if ( options.contains( "method") && options["method"].toString().toUpper() == "POST" ) {
QByteArray data;
if ( options.contains( "data" ) ) {
data = options["data"].toString().toLatin1();
}
reply = new NetworkReply( Tomahawk::Utils::nam()->post( req, data ) );
} else if ( options.contains( "method") && options["method"].toString().toUpper() == "HEAD" ) {
reply = new NetworkReply( Tomahawk::Utils::nam()->head( req ) );
} else {
reply = new NetworkReply( Tomahawk::Utils::nam()->get( req ) );
}
NewClosure( reply , SIGNAL( finished() ), this, SLOT( nativeAsyncRequestDone( int, NetworkReply* ) ), requestId, reply );
}
void
JSResolverHelper::nativeAsyncRequestDone( int requestId, NetworkReply* reply )
{
QVariantMap map;
map["response"] = QString::fromUtf8( reply->reply()->readAll() );
map["responseText"] = map["response"];
map["responseType"] = QString(); // Default, indicates a string in map["response"]
map["readyState"] = 4;
map["status"] = reply->reply()->attribute( QNetworkRequest::HttpStatusCodeAttribute ).toInt();
map["statusText"] = QString("%1 %2").arg( map["status"].toString() )
.arg( reply->reply()->attribute( QNetworkRequest::HttpReasonPhraseAttribute ).toString() );
bool ok = false;
QString json = QString::fromUtf8( TomahawkUtils::toJson( map, &ok ) );
Q_ASSERT( ok );
QString javascript = QString( "Tomahawk.nativeAsyncRequestDone( %1, %2 );" )
.arg( QString::number( requestId ) )
.arg( json );
m_resolver->d_func()->engine->mainFrame()->evaluateJavaScript( javascript );
}
bool
JSResolverHelper::hasFuzzyIndex()
{

View File

@ -49,6 +49,19 @@ public:
Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl );
Q_INVOKABLE void reportStreamUrl( const QString& qid, const QString& streamUrl, const QVariantMap& headers );
/**
* Native handler for asynchronous HTTP requests.
*
* This handler shall only be used if we cannot achieve the request with
* XMLHttpRequest as that would be more efficient.
* Use cases are:
* * Referer header: Stripped on MacOS and the specification says it
* should be stripped
*/
Q_INVOKABLE void nativeAsyncRequest( int requestId, const QString& url,
const QVariantMap& headers,
const QVariantMap& options );
/**
* Clucene indices for JS resolvers
**/
@ -89,6 +102,7 @@ private slots:
void gotStreamUrl( IODeviceCallback callback, NetworkReply* reply );
void tracksAdded( const QList<Tomahawk::query_ptr>& tracks, const Tomahawk::ModelMode, const Tomahawk::collection_ptr& collection );
void pltemplateTracksLoadedForUrl( const QString& url, const Tomahawk::playlisttemplate_ptr& pltemplate );
void nativeAsyncRequestDone( int requestId, NetworkReply* reply );
private:
Tomahawk::query_ptr parseTrack( const QVariantMap& track );