Skip to content

Commit

Permalink
Add eTag (entity Tag) support to Services API to help cache data on c…
Browse files Browse the repository at this point in the history
…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
dblain committed Mar 23, 2012
1 parent aa59013 commit 3c0d159
Show file tree
Hide file tree
Showing 22 changed files with 164 additions and 78 deletions.
25 changes: 25 additions & 0 deletions mythtv/libs/libmythservicecontracts/datacontracthelper.h
Expand Up @@ -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_
Expand Down
Expand Up @@ -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 )
Expand Down
Expand Up @@ -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");

Expand Down
Expand Up @@ -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 )
Expand Down
Expand Up @@ -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");

Expand Down
6 changes: 2 additions & 4 deletions mythtv/libs/libmythservicecontracts/datacontracts/lineup.h
Expand Up @@ -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");

Expand Down
Expand Up @@ -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");

Expand Down
Expand Up @@ -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");
Expand Down
Expand Up @@ -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");

Expand Down
14 changes: 3 additions & 11 deletions mythtv/libs/libmythservicecontracts/datacontracts/programGuide.h
Expand Up @@ -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)
//
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
Expand All @@ -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 )
Expand Down
Expand Up @@ -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 )
Expand Down
Expand Up @@ -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 )
Expand Down
Expand Up @@ -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");

Expand Down
Expand Up @@ -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");

Expand Down
Expand Up @@ -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 )
Expand Down
Expand Up @@ -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 )
Expand Down
Expand Up @@ -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 )
Expand Down
Expand Up @@ -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 )
Expand Down
32 changes: 32 additions & 0 deletions mythtv/libs/libmythupnp/httprequest.cpp
Expand Up @@ -245,12 +245,32 @@ long HTTPRequest::SendResponse( void )
setsockopt( getSocketHandle(), SOL_TCP, TCP_CORK, &g_on, sizeof( g_on ));
#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() );

Expand Down Expand Up @@ -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" );
Expand Down Expand Up @@ -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" );
Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythupnp/httprequest.h
Expand Up @@ -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 );

// ------------------------------------------------------------------

Expand Down

0 comments on commit 3c0d159

Please sign in to comment.