summaryrefslogtreecommitdiff
path: root/src/mbgl/tile/tile_loader_impl.hpp
blob: 598ec32c10c5712afbc7e03db744f340c98928c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#pragma once

#include <mbgl/tile/tile_loader.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/renderer/tile_parameters.hpp>
#include <mbgl/util/tileset.hpp>

#include <cassert>

namespace mbgl {

template <typename T>
TileLoader<T>::TileLoader(T& tile_,
                          const OverscaledTileID& id,
                          const TileParameters& parameters,
                          const Tileset& tileset)
    : tile(tile_),
      necessity(Necessity::Optional),
      resource(Resource::tile(
        tileset.tiles.at(0),
        parameters.pixelRatio,
        id.canonical.x,
        id.canonical.y,
        id.canonical.z,
        tileset.scheme)),
      fileSource(parameters.fileSource) {
    assert(!request);
    if (fileSource.supportsOptionalRequests()) {
        // When supported, the first request is always optional, even if the TileLoader
        // is marked as required. That way, we can let the first optional request continue
        // to load when the TileLoader is later changed from required to optional. If we
        // started out with a required request, we'd have to cancel everything, including the
        // initial optional part of the request.
        loadOptional();
    } else {
        // When the FileSource doesn't support optional requests, we do nothing until the
        // data is definitely required.
        if (necessity == Necessity::Required) {
            loadRequired();
        } else {
            // We're using this field to check whether the pending request is optional or required.
            resource.necessity = Resource::Optional;
        }
    }
}

template <typename T>
TileLoader<T>::~TileLoader() = default;

template <typename T>
void TileLoader<T>::loadOptional() {
    assert(!request);

    resource.necessity = Resource::Optional;
    request = fileSource.request(resource, [this](Response res) {
        request.reset();

        tile.setTriedOptional();

        if (res.error && res.error->reason == Response::Error::Reason::NotFound) {
            // When the optional request could not be satisfied, don't treat it as an error.
            // Instead, we make sure that the next request knows that there has been an optional
            // request before by setting one of the prior* fields.
            resource.priorModified = res.modified;
            resource.priorExpires = Timestamp{ Seconds::zero() };
            resource.priorEtag = res.etag;
            resource.priorData = res.data;
        } else {
            loadedData(res);
        }

        if (necessity == Necessity::Required) {
            loadRequired();
        }
    });
}

template <typename T>
void TileLoader<T>::makeRequired() {
    if (!request) {
        loadRequired();
    }
}

template <typename T>
void TileLoader<T>::makeOptional() {
    if (resource.necessity == Resource::Required && request) {
        // Abort a potential HTTP request.
        request.reset();
    }
}

template <typename T>
void TileLoader<T>::loadedData(const Response& res) {
    if (res.error && res.error->reason != Response::Error::Reason::NotFound) {
        tile.setError(std::make_exception_ptr(std::runtime_error(res.error->message)));
    } else if (res.notModified) {
        resource.priorExpires = res.expires;
        // Do not notify the tile; when we get this message, it already has the current
        // version of the data.
    } else {
        resource.priorModified = res.modified;
        resource.priorExpires = res.expires;
        resource.priorEtag = res.etag;
        tile.setData(res.noContent ? nullptr : res.data, res.modified, res.expires);
    }
}

template <typename T>
void TileLoader<T>::loadRequired() {
    assert(!request);

    resource.necessity = Resource::Required;
    request = fileSource.request(resource, [this](Response res) { loadedData(res); });
}

} // namespace mbgl