]> git.sur5r.net Git - minitube/blob - lib/http/src/cachedhttp.cpp
New upstream version 3.8
[minitube] / lib / http / src / cachedhttp.cpp
1 #include "cachedhttp.h"
2 #include "localcache.h"
3 #include <QtNetwork>
4
5 CachedHttpReply::CachedHttpReply(const QByteArray &body, const QUrl &url, bool autoSignals)
6     : bytes(body), requestUrl(url) {
7     if (autoSignals) QTimer::singleShot(0, this, SLOT(emitSignals()));
8 }
9
10 QByteArray CachedHttpReply::body() const {
11     return bytes;
12 }
13
14 void CachedHttpReply::emitSignals() {
15     emit data(body());
16     emit finished(*this);
17     deleteLater();
18 }
19
20 WrappedHttpReply::WrappedHttpReply(CachedHttp &cachedHttp,
21                                    LocalCache *cache,
22                                    const QByteArray &key,
23                                    HttpReply *httpReply)
24     : HttpReply(httpReply), cachedHttp(cachedHttp), cache(cache), key(key), httpReply(httpReply) {
25     connect(httpReply, SIGNAL(finished(HttpReply)), SLOT(originFinished(HttpReply)));
26 }
27
28 void WrappedHttpReply::originFinished(const HttpReply &reply) {
29     qDebug() << reply.statusCode() << reply.url();
30     bool success = reply.isSuccessful();
31     if (!success) {
32         // Fallback to stale cached data on HTTP error
33         const QByteArray value = cache->possiblyStaleValue(key);
34         if (!value.isNull()) {
35             qDebug() << "Using stale cache value" << reply.url();
36             emit data(value);
37             auto replyFromCache = new CachedHttpReply(value, reply.url(), false);
38             emit finished(*replyFromCache);
39             replyFromCache->deleteLater();
40             return;
41         }
42     }
43
44     bool doCache = success;
45     if (doCache) {
46         const auto &validators = cachedHttp.getValidators();
47         if (!validators.isEmpty()) {
48             const QByteArray &mime = reply.header("Content-Type");
49             auto i = validators.constFind(mime);
50             if (i != validators.constEnd()) {
51                 auto validator = i.value();
52                 doCache = validator(reply);
53             } else {
54                 i = validators.constFind("*");
55                 if (i != validators.constEnd()) {
56                     auto validator = i.value();
57                     doCache = validator(reply);
58                 }
59             }
60         }
61     }
62
63     if (doCache)
64         cache->insert(key, reply.body());
65     else
66         qDebug() << "Not caching" << reply.statusCode() << reply.url();
67
68     if (success) {
69         emit data(reply.body());
70     } else {
71         emit error(reply.reasonPhrase());
72     }
73     emit finished(reply);
74 }
75
76 CachedHttp::CachedHttp(Http &http, const char *name)
77     : http(http), cache(LocalCache::instance(name)), cachePostRequests(false) {}
78
79 void CachedHttp::setMaxSeconds(uint seconds) {
80     cache->setMaxSeconds(seconds);
81 }
82
83 void CachedHttp::setMaxSize(uint maxSize) {
84     cache->setMaxSize(maxSize);
85 }
86
87 HttpReply *CachedHttp::request(const HttpRequest &req) {
88     bool cacheable = req.operation == QNetworkAccessManager::GetOperation ||
89                      (cachePostRequests && req.operation == QNetworkAccessManager::PostOperation);
90     if (!cacheable) {
91         qDebug() << "Not cacheable" << req.url;
92         return http.request(req);
93     }
94     const QByteArray key = requestHash(req);
95     const QByteArray value = cache->value(key);
96     if (!value.isNull()) {
97         qDebug() << "HIT" << key << req.url;
98         return new CachedHttpReply(value, req.url);
99     }
100     // qDebug() << "MISS" << key << req.url;
101     return new WrappedHttpReply(*this, cache, key, http.request(req));
102 }
103
104 QByteArray CachedHttp::requestHash(const HttpRequest &req) {
105     const char sep = '|';
106
107     QByteArray s;
108     if (ignoreHostname) {
109         s = (req.url.scheme() + sep + req.url.path() + sep + req.url.query()).toUtf8();
110     } else {
111         s = req.url.toEncoded();
112     }
113     s += sep + req.body + sep + QByteArray::number(req.offset);
114     if (req.operation == QNetworkAccessManager::PostOperation) {
115         s.append(sep);
116         s.append("POST");
117     }
118     return LocalCache::hash(s);
119 }