Example #1
0
/*
    For a given httpRequest
    1) If AlwaysNetwork, return
    2) If we have a cache entry for this url populate headers so the server can return 304
    3) Calculate if response_is_fresh and if so send the cache and set loadedFromCache to true
 */
void QNetworkAccessHttpBackend::validateCache(QHttpNetworkRequest &httpRequest, bool &loadedFromCache)
{
    QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
        (QNetworkRequest::CacheLoadControl)request().attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
    if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
        // forced reload from the network
        // tell any caching proxy servers to reload too
        httpRequest.setHeaderField("Cache-Control", "no-cache");
        httpRequest.setHeaderField("Pragma", "no-cache");
        return;
    }

    QAbstractNetworkCache *nc = networkCache();
    if (!nc)
        return;                 // no local cache

    QNetworkCacheMetaData metaData = nc->metaData(url());
    if (!metaData.isValid())
        return;                 // not in cache

    if (!metaData.saveToDisk())
        return;

    QNetworkHeadersPrivate cacheHeaders;
    QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
    cacheHeaders.setAllRawHeaders(metaData.rawHeaders());

    it = cacheHeaders.findRawHeader("etag");
    if (it != cacheHeaders.rawHeaders.constEnd())
        httpRequest.setHeaderField("If-None-Match", it->second);

    QDateTime lastModified = metaData.lastModified();
    if (lastModified.isValid())
        httpRequest.setHeaderField("If-Modified-Since", QNetworkHeadersPrivate::toHttpDate(lastModified));

    if (CacheLoadControlAttribute == QNetworkRequest::PreferNetwork) {
        it = cacheHeaders.findRawHeader("Cache-Control");
        if (it != cacheHeaders.rawHeaders.constEnd()) {
            QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
            if (cacheControl.contains("must-revalidate"))
                return;
        }
    }

    QDateTime currentDateTime = QDateTime::currentDateTime();
    QDateTime expirationDate = metaData.expirationDate();

#if 0
    /*
     * age_value
     *      is the value of Age: header received by the cache with
     *              this response.
     * date_value
     *      is the value of the origin server's Date: header
     * request_time
     *      is the (local) time when the cache made the request
     *              that resulted in this cached response
     * response_time
     *      is the (local) time when the cache received the
     *              response
     * now
     *      is the current (local) time
     */
    int age_value = 0;
    it = cacheHeaders.findRawHeader("age");
    if (it != cacheHeaders.rawHeaders.constEnd())
        age_value = it->second.toInt();

    QDateTime dateHeader;
    int date_value = 0;
    it = cacheHeaders.findRawHeader("date");
    if (it != cacheHeaders.rawHeaders.constEnd()) {
        dateHeader = QNetworkHeadersPrivate::fromHttpDate(it->second);
        date_value = dateHeader.toTime_t();
    }

    int now = currentDateTime.toUTC().toTime_t();
    int request_time = now;
    int response_time = now;

    // Algorithm from RFC 2616 section 13.2.3
    int apparent_age = qMax(0, response_time - date_value);
    int corrected_received_age = qMax(apparent_age, age_value);
    int response_delay = response_time - request_time;
    int corrected_initial_age = corrected_received_age + response_delay;
    int resident_time = now - response_time;
    int current_age   = corrected_initial_age + resident_time;

    // RFC 2616 13.2.4 Expiration Calculations
    if (!expirationDate.isValid()) {
        if (lastModified.isValid()) {
            int diff = currentDateTime.secsTo(lastModified);
            expirationDate = lastModified;
            expirationDate.addSecs(diff / 10);
            if (httpRequest.headerField("Warning").isEmpty()) {
                QDateTime dt;
                dt.setTime_t(current_age);
                if (dt.daysTo(currentDateTime) > 1)
                    httpRequest.setHeaderField("Warning", "113");
            }
        }
    }

    // the cache-saving code below sets the expirationDate with date+max_age
    // if "max-age" is present, or to Expires otherwise
    int freshness_lifetime = dateHeader.secsTo(expirationDate);
    bool response_is_fresh = (freshness_lifetime > current_age);
#else
    bool response_is_fresh = currentDateTime.secsTo(expirationDate) >= 0;
#endif

    if (!response_is_fresh)
        return;

    loadedFromCache = true;
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
    qDebug() << "response_is_fresh" << CacheLoadControlAttribute;
#endif
    if (!sendCacheContents(metaData))
        loadedFromCache = false;
}
Example #2
0
/** \brief Downloads a QNetworkRequest via the QNetworkAccessManager
 *  \param dlInfo   MythDownloadInfo information for download
 */
void MythDownloadManager::downloadQNetworkRequest(MythDownloadInfo *dlInfo)
{
    if (!dlInfo)
        return;

    static const char dateFormat[] = "ddd, dd MMM yyyy hh:mm:ss 'GMT'";
    QUrl qurl(dlInfo->m_url);
    QNetworkRequest request;

    if (dlInfo->m_request)
    {
        request = *dlInfo->m_request;
        delete dlInfo->m_request;
        dlInfo->m_request = NULL;
    }
    else
        request.setUrl(qurl);

    if (!dlInfo->m_reload)
    {
        // Prefer the in-cache item if one exists and it is less than 5 minutes
        // old and it will not expire in the next 10 seconds
        QDateTime now = MythDate::current();

        // Handle redirects, we want the metadata of the file headers
        QString redirectLoc;
        int limit = 0;
        while (!(redirectLoc = getHeader(qurl, "Location")).isNull())
        {
            if (limit == CACHE_REDIRECTION_LIMIT)
            {
                LOG(VB_GENERAL, LOG_WARNING, QString("Cache Redirection limit "
                                                     "reached for %1")
                                                    .arg(qurl.toString()));
                return;
            }
            qurl.setUrl(redirectLoc);
            limit++;
        }

        LOG(VB_NETWORK, LOG_DEBUG, QString("Checking cache for %1")
                                                    .arg(qurl.toString()));

        m_infoLock->lock();
        QNetworkCacheMetaData urlData = m_manager->cache()->metaData(qurl);
        m_infoLock->unlock();
        if ((urlData.isValid()) &&
            ((!urlData.expirationDate().isValid()) ||
             (QDateTime(urlData.expirationDate().toUTC()).secsTo(now) < 10)))
        {
            QString dateString = getHeader(urlData, "Date");

            if (!dateString.isNull())
            {
                QDateTime loadDate =
                    MythDate::fromString(dateString, dateFormat);
                loadDate.setTimeSpec(Qt::UTC);
                if (loadDate.secsTo(now) <= 720)
                {
                    dlInfo->m_preferCache = true;
                    LOG(VB_NETWORK, LOG_DEBUG, QString("Prefering cache for %1")
                                                    .arg(qurl.toString()));
                }
            }
        }
    }

    if (dlInfo->m_preferCache)
        request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
                             QNetworkRequest::PreferCache);

    request.setRawHeader("User-Agent",
                         "MythTV v" MYTH_BINARY_VERSION " MythDownloadManager");

    if (dlInfo->m_headers)
    {
        QHash<QByteArray, QByteArray>::const_iterator it =
            dlInfo->m_headers->constBegin();
        for ( ; it != dlInfo->m_headers->constEnd(); ++it )
        {
            if (!it.key().isEmpty() && !it.value().isEmpty())
            {
                request.setRawHeader(it.key(), it.value());
            }
        }
    }

    switch (dlInfo->m_requestType)
    {
        case kRequestPost :
            dlInfo->m_reply = m_manager->post(request, *dlInfo->m_data);
            break;
        case kRequestHead :
            dlInfo->m_reply = m_manager->head(request);
            break;
        case kRequestGet :
        default:
            dlInfo->m_reply = m_manager->get(request);
            break;
    }

    m_downloadReplies[dlInfo->m_reply] = dlInfo;

    if (dlInfo->m_authCallback)
    {
        connect(m_manager, SIGNAL(authenticationRequired(QNetworkReply *,
                                                         QAuthenticator *)),
                this, SLOT(authCallback(QNetworkReply *, QAuthenticator *)));
    }
void tst_QNetworkCacheMetaData::qnetworkcachemetadata()
{
    QNetworkCacheMetaData data;
    QCOMPARE(data.expirationDate(), QDateTime());
    QCOMPARE(data.isValid(), false);
    QCOMPARE(data.lastModified(), QDateTime());
    QCOMPARE(data.operator!=(QNetworkCacheMetaData()), false);
    QNetworkCacheMetaData metaData;
    QCOMPARE(data.operator=(metaData), QNetworkCacheMetaData());
    QCOMPARE(data.operator==(QNetworkCacheMetaData()), true);
    QCOMPARE(data.rawHeaders(), QNetworkCacheMetaData::RawHeaderList());
    QCOMPARE(data.saveToDisk(), true);
    QCOMPARE(data.url(), QUrl());
    data.setExpirationDate(QDateTime());
    data.setLastModified(QDateTime());
    data.setRawHeaders(QNetworkCacheMetaData::RawHeaderList());
    data.setSaveToDisk(false);
    data.setUrl(QUrl());
}