diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2014-08-25 16:56:56 +0200 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2014-09-24 16:12:03 +0200 |
commit | b94635f70430c3659cd57596e49649d376284473 (patch) | |
tree | 8d3957bf7c26c8fffeb7f961e0826ab625d5052a | |
parent | 28446bed24689b2bac7c8bfbd37741a7fa4fa6be (diff) | |
download | qtlocation-mapboxgl-b94635f70430c3659cd57596e49649d376284473.tar.gz |
parse cache-control and last-modified headers
-rw-r--r-- | common/curl_request.cpp | 31 | ||||
-rw-r--r-- | common/foundation_request.mm | 4 | ||||
-rw-r--r-- | include/mbgl/platform/platform.hpp | 11 | ||||
-rw-r--r-- | include/mbgl/platform/response.hpp | 29 | ||||
-rw-r--r-- | linux/mapboxgl-app.gyp | 2 | ||||
-rw-r--r-- | mapboxgl.gyp | 4 | ||||
-rw-r--r-- | src/platform/response.cpp | 26 | ||||
-rw-r--r-- | src/util/filesource.cpp | 29 |
8 files changed, 121 insertions, 15 deletions
diff --git a/common/curl_request.cpp b/common/curl_request.cpp index 416ed90cd1..0957481226 100644 --- a/common/curl_request.cpp +++ b/common/curl_request.cpp @@ -286,6 +286,35 @@ size_t curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp) { return size * nmemb; } +// Compares the beginning of the (non-zero-terminated!) data buffer with the (zero-terminated!) +// header string. If the data buffer contains the header string at the beginning, it returns +// the length of the header string == begin of the value, otherwise it returns npos. +// The comparison of the header is ASCII-case-insensitive. +size_t header_matches(const char *header, const char *buffer, size_t length) { + const size_t header_length = strlen(header); + if (length < header_length) return std::string::npos; + size_t i = 0; + while (i < length && i < header_length && std::tolower(buffer[i]) == header[i]) { + i++; + } + return i == header_length ? i : std::string::npos; +} + +size_t curl_header_cb(char *buffer, size_t size, size_t nmemb, void *userp) { + const size_t length = size * nmemb; + + size_t begin = std::string::npos; + if ((begin = header_matches("last-modified: ", buffer, length)) != std::string::npos) { + const std::string value { buffer + begin, length - begin - 2 /* remove \r\n */ }; + static_cast<Response *>(userp)->setLastModified(value.c_str()); + } else if ((begin = header_matches("cache-control: ", buffer, length)) != std::string::npos) { + const std::string value { buffer + begin, length - begin - 2 /* remove \r\n */ }; + static_cast<Response *>(userp)->setCacheControl(value.c_str()); + } + + return length; +} + // This callback is called in the request event loop (on the request thread). // It initializes newly queued up download requests and adds them to the CURL // multi handle. @@ -315,6 +344,8 @@ void async_add_cb(uv_async_t * /*async*/) { curl_easy_setopt(handle, CURLOPT_URL, (*req)->url.c_str()); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curl_write_cb); curl_easy_setopt(handle, CURLOPT_WRITEDATA, &(*req)->res->body); + curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, curl_header_cb); + curl_easy_setopt(handle, CURLOPT_HEADERDATA, (*req)->res.get()); curl_easy_setopt(handle, CURLOPT_ACCEPT_ENCODING, "gzip, deflate"); curl_easy_setopt(handle, CURLOPT_SHARE, curl_share); curl_multi_add_handle(curl_multi, handle); diff --git a/common/foundation_request.mm b/common/foundation_request.mm index b7eafdb96c..478dadf4d9 100644 --- a/common/foundation_request.mm +++ b/common/foundation_request.mm @@ -91,8 +91,12 @@ mbgl::platform::request_http(const std::string &url, } if (!error && [response isKindOfClass:[NSHTTPURLResponse class]]) { + NSDictionary *headers = [(NSHTTPURLResponse *)response allHeaderFields]; (*req_ptr)->res->code = [(NSHTTPURLResponse *)response statusCode]; (*req_ptr)->res->body = {(const char *)[data bytes], [data length]}; + (*req_ptr)->res->setCacheControl([[headers objectForKey:@"Cache-Control"] UTF8String]); + (*req_ptr)->res->setLastModified([[headers objectForKey:@"Last-Modified"] UTF8String]); + } else { (*req_ptr)->res->error_message = [[error localizedDescription] UTF8String]; } diff --git a/include/mbgl/platform/platform.hpp b/include/mbgl/platform/platform.hpp index 22405a4cfd..e08cac5312 100644 --- a/include/mbgl/platform/platform.hpp +++ b/include/mbgl/platform/platform.hpp @@ -1,10 +1,11 @@ #ifndef MBGL_PLATFORM_PLATFORM #define MBGL_PLATFORM_PLATFORM +#include <mbgl/platform/response.hpp> + #include <mbgl/util/uv.hpp> #include <memory> -#include <functional> #include <string> namespace mbgl { @@ -12,14 +13,6 @@ namespace platform { class Request; -struct Response { - Response(std::function<void(Response *)> callback) : callback(callback) {} - int16_t code = -1; - std::string body; - std::string error_message; - std::function<void(Response *)> callback; -}; - // Makes an HTTP request of a URL, preferrably on a background thread, and calls a function with the // results in the original thread (which runs the libuv loop). // If the loop pointer is NULL, the callback function will be called on an arbitrary thread. diff --git a/include/mbgl/platform/response.hpp b/include/mbgl/platform/response.hpp new file mode 100644 index 0000000000..345a2ee3df --- /dev/null +++ b/include/mbgl/platform/response.hpp @@ -0,0 +1,29 @@ +#ifndef MBGL_PLATFORM_RESPONSE +#define MBGL_PLATFORM_RESPONSE + +#include <string> +#include <functional> +#include <ctime> + +#include <mbgl/util/noncopyable.hpp> + +namespace mbgl { +namespace platform { + +struct Response : private util::noncopyable { + Response(std::function<void(Response *)> callback) : callback(callback) {} + int16_t code = -1; + std::string body; + std::string error_message; + std::time_t modified; + std::time_t expires; + std::function<void(Response *)> callback; + + void setCacheControl(const char *value); + void setLastModified(const char *value); +}; + +} +} + +#endif diff --git a/linux/mapboxgl-app.gyp b/linux/mapboxgl-app.gyp index e19768caf9..2a76fea1d6 100644 --- a/linux/mapboxgl-app.gyp +++ b/linux/mapboxgl-app.gyp @@ -32,7 +32,6 @@ ], 'OTHER_LDFLAGS': [ '<@(glfw3_libraries)', - '<@(curl_libraries)', ], } }, @@ -46,7 +45,6 @@ 'link_settings': { 'libraries': [ '<@(glfw3_libraries)', - '<@(curl_libraries)', '-lboost_regex' ], }, diff --git a/mapboxgl.gyp b/mapboxgl.gyp index 49c4150aa1..6d8bd3f265 100644 --- a/mapboxgl.gyp +++ b/mapboxgl.gyp @@ -139,6 +139,7 @@ 'OTHER_CPLUSPLUSFLAGS':[ '<@(png_cflags)', '<@(uv_cflags)', + '<@(curl_cflags)', '<@(sqlite3_cflags)', '-I<(boost_root)/include', ], @@ -149,6 +150,7 @@ }, { 'cflags': [ '<@(png_cflags)', + '<@(curl_cflags)', '<@(sqlite3_cflags)', '-I<(boost_root)/include', ], @@ -177,12 +179,14 @@ 'OTHER_LDFLAGS': [ '<@(png_libraries)', '<@(uv_libraries)', + '<@(curl_libraries)', ] } }, { 'libraries': [ '<@(png_libraries)', '<@(uv_libraries)', + '<@(curl_libraries)', ] }] ] diff --git a/src/platform/response.cpp b/src/platform/response.cpp new file mode 100644 index 0000000000..f2117c8894 --- /dev/null +++ b/src/platform/response.cpp @@ -0,0 +1,26 @@ +#include <mbgl/platform/response.hpp> +#include <curl/curl.h> + +#include <cstdio> + +namespace mbgl { +namespace platform { + + +void Response::setCacheControl(const char *value) { + int seconds = 0; + // TODO: cache-control may contain other information as well: + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9 + if (std::sscanf(value, "max-age=%u", &seconds) == 1) { + if (std::time(&expires) != -1) { + expires += seconds; + } + } +} + +void Response::setLastModified(const char *value) { + modified = curl_getdate(value, nullptr); +} + +} +} diff --git a/src/util/filesource.cpp b/src/util/filesource.cpp index d626092bce..8c14f6e4e0 100644 --- a/src/util/filesource.cpp +++ b/src/util/filesource.cpp @@ -32,6 +32,7 @@ void FileSource::createSchema() { "`url` TEXT PRIMARY KEY NOT NULL," "`code` INTEGER NOT NULL," "`type` INTEGER NOT NULL," + "`modified` INTEGER," "`expires` INTEGER," "`data` BLOB," "`compressed` INTEGER NOT NULL DEFAULT 0" @@ -143,7 +144,7 @@ void FileSource::saveFile(ResourceType type, const std::string &url, platform::R sqlite3_stmt *stmt = nullptr; int err; - err = sqlite3_prepare_v2(db, "REPLACE INTO `http_cache` (`url`, `code`, `type`, `data`, `compressed`) VALUES(?, ?, ?, ?, ?)", -1, &stmt, nullptr); + err = sqlite3_prepare_v2(db, "REPLACE INTO `http_cache` (`url`, `code`, `type`, `modified`, `expires`, `data`, `compressed`) VALUES(?, ?, ?, ?, ?, ?, ?)", -1, &stmt, nullptr); if (err != SQLITE_OK) { Log::Warning(Event::Database, "%s: %s", sqlite3_errstr(err), sqlite3_errmsg(db)); return; @@ -179,15 +180,35 @@ void FileSource::saveFile(ResourceType type, const std::string &url, platform::R return; } + err = sqlite3_bind_int(stmt, 4, res->modified); + if (err != SQLITE_OK) { + Log::Warning(Event::Database, "%s: %s", sqlite3_errstr(err), sqlite3_errmsg(db)); + err = sqlite3_finalize(stmt); + if (err != SQLITE_OK) { + Log::Warning(Event::Database, "%s: %s", sqlite3_errstr(err), sqlite3_errmsg(db)); + } + return; + } + + err = sqlite3_bind_int(stmt, 5, res->expires); + if (err != SQLITE_OK) { + Log::Warning(Event::Database, "%s: %s", sqlite3_errstr(err), sqlite3_errmsg(db)); + err = sqlite3_finalize(stmt); + if (err != SQLITE_OK) { + Log::Warning(Event::Database, "%s: %s", sqlite3_errstr(err), sqlite3_errmsg(db)); + } + return; + } + bool compressed = false; switch (type) { case ResourceType::Image: - err = sqlite3_bind_blob(stmt, 4, res->body.data(), res->body.size(), SQLITE_STATIC); + err = sqlite3_bind_blob(stmt, 6, res->body.data(), res->body.size(), SQLITE_STATIC); break; default: const std::string *data = new std::string(std::move(util::compress(res->body))); compressed = true; - err = sqlite3_bind_blob(stmt, 4, data->data(), data->size(), [](void *p) { delete (std::string *)p; }); + err = sqlite3_bind_blob(stmt, 6, data->data(), data->size(), [](void *p) { delete (std::string *)p; }); break; } if (err != SQLITE_OK) { @@ -199,7 +220,7 @@ void FileSource::saveFile(ResourceType type, const std::string &url, platform::R return; } - err = sqlite3_bind_int(stmt, 5, compressed); + err = sqlite3_bind_int(stmt, 7, compressed); if (err != SQLITE_OK) { Log::Warning(Event::Database, "%s: %s", sqlite3_errstr(err), sqlite3_errmsg(db)); err = sqlite3_finalize(stmt); |