summaryrefslogtreecommitdiff
path: root/src/static_files.cc
blob: 7125ee0b7631f1f2c27b0d26a1d35e6a342c547d (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
#include "common.hh"

#include "files_finder.hh"
#include "hasher.hh"
#include "mime_types.hh"
#include "send_file.hh"
#include "static_files.hh"
#include "weak_ptr.hh"

#include <optional>

namespace {

class StaticFilesImpl : public StaticFiles, public FilesFinder::Delegate {
public:
  StaticFilesImpl(
      std::shared_ptr<Logger> logger,
      std::shared_ptr<TaskRunner> runner,
      std::shared_ptr<SendFile> send_file,
      std::filesystem::path path,
      size_t threads)
    : root_(std::move(path)), send_file_(std::move(send_file)),
      weak_ptr_owner_(this) {
    finder_ = FilesFinder::create(logger, runner, root_, this, threads);
    hasher_ = Hasher::create(logger, runner, threads);
  }

  std::unique_ptr<Transport::Response> request(
      Transport* transport, std::string_view path) override {
    auto it = files_.find(std::string(path));
    if (it == files_.end())
      return nullptr;
    auto resp = send_file_->create_ok_file(transport, it->second.path_,
                                           it->first, it->second.etag_,
                                           it->second.size_);
    if (!it->second.mime_type_.empty())
      resp->add_header("Content-Type", it->second.mime_type_);
    return resp;
  }

  void file(std::filesystem::path path) override {
    std::string name("/");
    name.append(path.filename());
    auto parent = path.parent_path();
    while (true) {
      if (parent == root_ || !parent.has_parent_path())
        break;
      name.insert(0, parent.filename());
      name.insert(0, "/");
      parent = parent.parent_path();
    }
    files_[name].path_ = path;
    if (path.has_extension())
      files_[name].mime_type_ = mime_types::from_extension(
          std::string(path.extension()).substr(1));
    hasher_->hash(path, std::bind(&StaticFilesImpl::weak_hashed,
                                  weak_ptr_owner_.get(), name,
                                  std::placeholders::_1,
                                  std::placeholders::_2));
  }

  void done() override {
    finder_.reset();
  }

private:
  struct File {
    std::filesystem::path path_;
    std::string mime_type_;
    std::string etag_;
    std::optional<uint64_t> size_;
  };

  static void weak_hashed(std::shared_ptr<WeakPtr<StaticFilesImpl>> weak_ptr,
                          std::string const& name,
                          std::string hash,
                          uint64_t size) {
    auto* ptr = weak_ptr->get();
    if (ptr)
      ptr->hashed(name, std::move(hash), size);
  }

  void hashed(std::string const& name, std::string hash, uint64_t size) {
    if (hash.empty())
      return;

    hash.insert(0, "\"");
    hash.push_back('"');
    files_[name].etag_ = std::move(hash);
    files_[name].size_ = size;
  }

  std::filesystem::path root_;
  std::shared_ptr<SendFile> send_file_;
  std::unordered_map<std::string, File> files_;
  std::unique_ptr<FilesFinder> finder_;
  std::unique_ptr<Hasher> hasher_;

  WeakPtrOwner<StaticFilesImpl> weak_ptr_owner_;
};

}  // namespace

std::unique_ptr<StaticFiles> StaticFiles::create(
    std::shared_ptr<Logger> logger,
    std::shared_ptr<TaskRunner> runner,
    std::shared_ptr<SendFile> send_file,
    std::filesystem::path path,
    size_t threads) {
  return std::make_unique<StaticFilesImpl>(
      std::move(logger), std::move(runner), std::move(send_file),
      std::move(path), threads);
}