Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add eTag (entity Tag) support to Services API to help cache data on c…

…lient.

New Q_CLASSINFO property option "transient" used to tell the serializer
not to hash a property since its value changes every request (i.e. AsOf)
  • Loading branch information...
commit 3c0d1593a7b43590dca194038e046aa70cefc78b 1 parent aa59013
@dblain dblain authored
Showing with 164 additions and 78 deletions.
  1. +25 −0 mythtv/libs/libmythservicecontracts/datacontracthelper.h
  2. +3 −5 mythtv/libs/libmythservicecontracts/datacontracts/artworkInfoList.h
  3. +2 −4 mythtv/libs/libmythservicecontracts/datacontracts/captureCardList.h
  4. +3 −4 mythtv/libs/libmythservicecontracts/datacontracts/channelInfoList.h
  5. +2 −4 mythtv/libs/libmythservicecontracts/datacontracts/encoderList.h
  6. +2 −4 mythtv/libs/libmythservicecontracts/datacontracts/lineup.h
  7. +2 −4 mythtv/libs/libmythservicecontracts/datacontracts/liveStreamInfoList.h
  8. +2 −4 mythtv/libs/libmythservicecontracts/datacontracts/logMessageList.h
  9. +2 −4 mythtv/libs/libmythservicecontracts/datacontracts/programAndChannel.h
  10. +3 −11 mythtv/libs/libmythservicecontracts/datacontracts/programGuide.h
  11. +3 −5 mythtv/libs/libmythservicecontracts/datacontracts/programList.h
  12. +3 −4 mythtv/libs/libmythservicecontracts/datacontracts/recRuleList.h
  13. +2 −4 mythtv/libs/libmythservicecontracts/datacontracts/settingList.h
  14. +2 −4 mythtv/libs/libmythservicecontracts/datacontracts/storageGroupDirList.h
  15. +3 −4 mythtv/libs/libmythservicecontracts/datacontracts/videoLookupInfoList.h
  16. +3 −4 mythtv/libs/libmythservicecontracts/datacontracts/videoMetadataInfoList.h
  17. +3 −4 mythtv/libs/libmythservicecontracts/datacontracts/videoMultiplexList.h
  18. +3 −4 mythtv/libs/libmythservicecontracts/datacontracts/videoSourceList.h
  19. +32 −0 mythtv/libs/libmythupnp/httprequest.cpp
  20. +1 −0  mythtv/libs/libmythupnp/httprequest.h
  21. +55 −0 mythtv/libs/libmythupnp/serializers/serializer.cpp
  22. +8 −1 mythtv/libs/libmythupnp/serializers/serializer.h
View
25 mythtv/libs/libmythservicecontracts/datacontracthelper.h
@@ -9,9 +9,34 @@
//////////////////////////////////////////////////////////////////////////////
//
// * Copy Constructors (needed for Q_PROPERTY) don't do Deep Copies yet.
+//
// * DECLARE_METATYPE not working if this header is not included... need
// to find solution since Serializer classes doesn't include this header.
//
+// * Q_CLASSINFO is used to add metadata options to individual properties
+// the the Qt Metadata system doesn't account for.
+// It can be used in each data contract class. The format is as follows:
+//
+// Q_CLASSINFO( "<PropName>", "<option>=<value>;<option>=<value" );
+//
+// Valid options/values are:
+//
+// type=<containedType> - used by collections to know what type of
+// object is stored. (QVariantMap, QVariantList)
+// name=<name> - used for QVariantMap & QVariantList to hint
+// to the serializer what name to use for each
+// object in collection (child element in XML).
+// transient=true - If present, this property will not be used
+// when calculating the SHA1 hash used for ETag
+// http header.
+//
+//
+// * DESIGNABLE in Q_PROPERTY is used to indicate if it should be Serialized
+// (can specify a propery to support runtime logic)
+//
+// * Q_CLASSINFO( "defaultProp", "<propname>" ) is used to indicate the
+// default property (used for node text in XML)
+//
//////////////////////////////////////////////////////////////////////////////
#ifndef DATACONTRACTHELPER_H_
View
8 mythtv/libs/libmythservicecontracts/datacontracts/artworkInfoList.h
@@ -27,11 +27,9 @@ class SERVICE_PUBLIC ArtworkInfoList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
-
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
+
Q_CLASSINFO( "ArtworkInfos", "type=DTC::ArtworkInfo");
Q_PROPERTY( QVariantList ArtworkInfos READ ArtworkInfos DESIGNABLE true )
View
6 mythtv/libs/libmythservicecontracts/datacontracts/captureCardList.h
@@ -26,10 +26,8 @@ class SERVICE_PUBLIC CaptureCardList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do this by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "CaptureCards", "type=DTC::CaptureCard");
View
7 mythtv/libs/libmythservicecontracts/datacontracts/channelInfoList.h
@@ -16,12 +16,11 @@ class SERVICE_PUBLIC ChannelInfoList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "ChannelInfos", "type=DTC::ChannelInfo");
+ Q_CLASSINFO( "AsOf" , "transient=true" );
Q_PROPERTY( int StartIndex READ StartIndex WRITE setStartIndex )
Q_PROPERTY( int Count READ Count WRITE setCount )
View
6 mythtv/libs/libmythservicecontracts/datacontracts/encoderList.h
@@ -26,10 +26,8 @@ class SERVICE_PUBLIC EncoderList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "Encoders", "type=DTC::Encoder");
View
6 mythtv/libs/libmythservicecontracts/datacontracts/lineup.h
@@ -76,10 +76,8 @@ class SERVICE_PUBLIC LineupList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "Lineups", "type=DTC::Lineup");
View
6 mythtv/libs/libmythservicecontracts/datacontracts/liveStreamInfoList.h
@@ -16,10 +16,8 @@ class SERVICE_PUBLIC LiveStreamInfoList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "LiveStreamInfos", "type=DTC::LiveStreamInfo");
View
6 mythtv/libs/libmythservicecontracts/datacontracts/logMessageList.h
@@ -17,10 +17,8 @@ class SERVICE_PUBLIC LogMessageList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "HostNames", "type=DTC::LabelValue");
Q_CLASSINFO( "Applications", "type=DTC::LabelValue");
View
6 mythtv/libs/libmythservicecontracts/datacontracts/programAndChannel.h
@@ -31,10 +31,8 @@ class SERVICE_PUBLIC ChannelInfo : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.06" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "Programs", "type=DTC::Program");
View
14 mythtv/libs/libmythservicecontracts/datacontracts/programGuide.h
@@ -27,13 +27,7 @@ namespace DTC
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//
-// -=>NOTE: DESIGNABLE - is used to indicate if should be Serialized
-// (can specify a propery to support runtime logic)
-//
//
-// Q_CLASSINFO( "defaultProp", "<propname>" ) -
-// is used to indicate the default property
-// (used for node text in XML)
//
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
@@ -43,13 +37,11 @@ class SERVICE_PUBLIC ProgramGuide : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "Channels", "type=DTC::ChannelInfo");
-
+ Q_CLASSINFO( "AsOf" , "transient=true" );
Q_PROPERTY( QDateTime StartTime READ StartTime WRITE setStartTime )
Q_PROPERTY( QDateTime EndTime READ EndTime WRITE setEndTime )
View
8 mythtv/libs/libmythservicecontracts/datacontracts/programList.h
@@ -28,13 +28,11 @@ class SERVICE_PUBLIC ProgramList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
-
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "Programs", "type=DTC::Program");
+ Q_CLASSINFO( "AsOf" , "transient=true" );
Q_PROPERTY( int StartIndex READ StartIndex WRITE setStartIndex )
Q_PROPERTY( int Count READ Count WRITE setCount )
View
7 mythtv/libs/libmythservicecontracts/datacontracts/recRuleList.h
@@ -17,12 +17,11 @@ class SERVICE_PUBLIC RecRuleList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "RecRules", "type=DTC::RecRule");
+ Q_CLASSINFO( "AsOf" , "transient=true" );
Q_PROPERTY( int StartIndex READ StartIndex WRITE setStartIndex )
Q_PROPERTY( int Count READ Count WRITE setCount )
View
6 mythtv/libs/libmythservicecontracts/datacontracts/settingList.h
@@ -25,10 +25,8 @@ class SERVICE_PUBLIC SettingList : public QObject
Q_OBJECT
Q_CLASSINFO( "version" , "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "Settings", "type=QString;name=String");
View
6 mythtv/libs/libmythservicecontracts/datacontracts/storageGroupDirList.h
@@ -16,10 +16,8 @@ class SERVICE_PUBLIC StorageGroupDirList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "StorageGroupDirs", "type=DTC::StorageGroupDir");
View
7 mythtv/libs/libmythservicecontracts/datacontracts/videoLookupInfoList.h
@@ -26,12 +26,11 @@ class SERVICE_PUBLIC VideoLookupList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "VideoLookups", "type=DTC::VideoLookup");
+ Q_CLASSINFO( "AsOf" , "transient=true" );
Q_PROPERTY( int Count READ Count WRITE setCount )
Q_PROPERTY( QDateTime AsOf READ AsOf WRITE setAsOf )
View
7 mythtv/libs/libmythservicecontracts/datacontracts/videoMetadataInfoList.h
@@ -26,12 +26,11 @@ class SERVICE_PUBLIC VideoMetadataInfoList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.01" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "VideoMetadataInfos", "type=DTC::VideoMetadataInfo");
+ Q_CLASSINFO( "AsOf" , "transient=true" );
Q_PROPERTY( int StartIndex READ StartIndex WRITE setStartIndex )
Q_PROPERTY( int Count READ Count WRITE setCount )
View
7 mythtv/libs/libmythservicecontracts/datacontracts/videoMultiplexList.h
@@ -17,12 +17,11 @@ class SERVICE_PUBLIC VideoMultiplexList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "VideoMultiplexes", "type=DTC::VideoMultiplex");
+ Q_CLASSINFO( "AsOf" , "transient=true" );
Q_PROPERTY( int StartIndex READ StartIndex WRITE setStartIndex )
Q_PROPERTY( int Count READ Count WRITE setCount )
View
7 mythtv/libs/libmythservicecontracts/datacontracts/videoSourceList.h
@@ -17,12 +17,11 @@ class SERVICE_PUBLIC VideoSourceList : public QObject
Q_OBJECT
Q_CLASSINFO( "version", "1.0" );
- // We need to know the type that will ultimately be contained in
- // any QVariantList or QVariantMap. We do his by specifying
- // A Q_CLASSINFO entry with "<PropName>_type" as the key
- // and the type name as the value
+ // Q_CLASSINFO Used to augment Metadata for properties.
+ // See datacontracthelper.h for details
Q_CLASSINFO( "VideoSources", "type=DTC::VideoSource");
+ Q_CLASSINFO( "AsOf" , "transient=true" );
Q_PROPERTY( QDateTime AsOf READ AsOf WRITE setAsOf )
Q_PROPERTY( QString Version READ Version WRITE setVersion )
View
32 mythtv/libs/libmythupnp/httprequest.cpp
@@ -246,11 +246,31 @@ long HTTPRequest::SendResponse( void )
#endif
// ----------------------------------------------------------------------
+ // Check for ETag match...
+ // ----------------------------------------------------------------------
+
+ QString sETag = GetHeaderValue( "If-None-Match", "" );
+
+ if ( !sETag.isEmpty() && sETag == m_mapRespHeaders[ "ETag" ] )
+ {
+ LOG(VB_UPNP, LOG_INFO,
+ QString("HTTPRequest::SendResponse(%1) - Cached")
+ .arg(sETag));
+
+ m_nResponseStatus = 304;
+
+ // no content can be returned.
+ m_response.buffer().clear();
+ }
+
+ // ----------------------------------------------------------------------
// Write out Header.
// ----------------------------------------------------------------------
+
const QByteArray &buffer = m_response.buffer();
QString rHeader = BuildHeader( buffer.length() );
+
QByteArray sHeader = rHeader.toUtf8();
nBytes = WriteBlockDirect( sHeader.constData(), sHeader.length() );
@@ -720,6 +740,7 @@ QString HTTPRequest::GetResponseStatus( void )
case 201: return( "201 Created" );
case 202: return( "202 Accepted" );
case 206: return( "206 Partial Content" );
+ case 304: return( "304 Not Modified" );
case 400: return( "400 Bad Request" );
case 401: return( "401 Unauthorized" );
case 403: return( "403 Forbidden" );
@@ -1444,6 +1465,17 @@ QString HTTPRequest::Encode(const QString &sIn)
//
/////////////////////////////////////////////////////////////////////////////
+QString HTTPRequest::GetETagHash(const QByteArray &data)
+{
+ QByteArray hash = QCryptographicHash::hash( data.data(), QCryptographicHash::Sha1);
+
+ return ("\"" + hash.toHex() + "\"");
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+/////////////////////////////////////////////////////////////////////////////
+
bool HTTPRequest::IsUrlProtected( const QString &sBaseUrl )
{
QString sProtected = UPnp::GetConfiguration()->GetValue( "HTTP/Protected/Urls", "/setup;/Config" );
View
1  mythtv/libs/libmythupnp/httprequest.h
@@ -195,6 +195,7 @@ class UPNP_PUBLIC HTTPRequest
static QString TestMimeType ( const QString &sFileName );
static long GetParameters ( QString sParams, QStringMap &mapParams );
static QString Encode ( const QString &sIn );
+ static QString GetETagHash ( const QByteArray &data );
// ------------------------------------------------------------------
View
55 mythtv/libs/libmythupnp/serializers/serializer.cpp
@@ -23,6 +23,9 @@ void Serializer::AddHeaders( QStringMap &headers )
{
headers[ "Cache-Control" ] = "no-cache=\"Ext\", "
"max-age = 5000";
+
+ headers[ "ETag" ] = "\"" + m_hash.result().toHex() + "\"";
+
}
//////////////////////////////////////////////////////////////////////////////
@@ -48,11 +51,14 @@ void Serializer::Serialize( const QObject *pObject, const QString &_sName )
// ---------------------------------------------------------------
+ m_hash.reset();
+
BeginSerialize( sName );
SerializeObject( pObject, sName );
EndSerialize();
+
}
//////////////////////////////////////////////////////////////////////////////
@@ -66,6 +72,8 @@ void Serializer::Serialize( const QVariant &vValue, const QString &_sName )
if (sName.at(0) == 'Q')
sName = sName.mid( 1 );
+ m_hash.reset();
+
BeginSerialize( sName );
AddProperty( sName, vValue, NULL, NULL );
@@ -79,6 +87,8 @@ void Serializer::Serialize( const QVariant &vValue, const QString &_sName )
void Serializer::SerializeObject( const QObject *pObject, const QString &sName )
{
+ m_hash.addData( sName.toUtf8() );
+
BeginObject( sName, pObject );
SerializeObjectProperties( pObject );
@@ -109,12 +119,57 @@ void Serializer::SerializeObjectProperties( const QObject *pObject )
if ( sPropName.compare( "objectName" ) == 0)
continue;
+
+ bool bHash = false;
+
+ if (ReadPropertyMetadata( pObject,
+ sPropName,
+ "transient").toLower() != "true" )
+ {
+ bHash = true;
+ m_hash.addData( sPropName.toUtf8() );
+ }
QVariant value( pObject->property( pszPropName ) );
+ if (bHash && !value.canConvert< QObject* >())
+ {
+ m_hash.addData( value.toString().toUtf8() );
+ }
+
AddProperty( sPropName, value, pMetaObject, &metaProperty );
}
}
}
}
+/////////////////////////////////////////////////////////////////////////////
+//
+/////////////////////////////////////////////////////////////////////////////
+
+QString Serializer::ReadPropertyMetadata( const QObject *pObject,
+ QString sPropName,
+ QString sKey )
+{
+ const QMetaObject *pMeta = pObject->metaObject();
+
+ int nIdx = pMeta->indexOfClassInfo( sPropName.toUtf8() );
+
+ if (nIdx >=0)
+ {
+ QString sMetadata = pMeta->classInfo( nIdx ).value();
+ QStringList sOptions = sMetadata.split( ';' );
+
+ QString sFullKey = sKey + "=";
+
+ for (int nIdx = 0; nIdx < sOptions.size(); ++nIdx)
+ {
+ if (sOptions.at( nIdx ).startsWith( sFullKey ))
+ return sOptions.at( nIdx ).mid( sFullKey.length() );
+ }
+ }
+
+ return QString();
+}
+
+
View
9 mythtv/libs/libmythupnp/serializers/serializer.h
@@ -18,6 +18,7 @@
#include <QList>
#include <QMetaType>
+#include <QCryptographicHash.h>
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@@ -31,6 +32,8 @@ class UPNP_PUBLIC Serializer
{
protected:
+ QCryptographicHash m_hash;
+
virtual void BeginSerialize( QString &sName ) {}
virtual void EndSerialize () {}
@@ -47,6 +50,10 @@ class UPNP_PUBLIC Serializer
void SerializeObject ( const QObject *pObject, const QString &sName );
void SerializeObjectProperties( const QObject *pObject );
+ QString ReadPropertyMetadata ( const QObject *pObject,
+ QString sPropName,
+ QString sKey );
+
public:
virtual void Serialize( const QObject *pObject, const QString &_sName = QString() );
@@ -60,7 +67,7 @@ class UPNP_PUBLIC Serializer
virtual void AddHeaders ( QStringMap &headers );
- Serializer()
+ Serializer() : m_hash( QCryptographicHash::Sha1 )
{
qRegisterMetaType< QList<QObject*> >("QList<QObject*>");
}
Please sign in to comment.
Something went wrong with that request. Please try again.