#include "common.hh" #include "pathutil.hh" #include "strutil.hh" #include "transport.hh" #include namespace { class NoContentInput : public Transport::Input { public: Return fill(Buffer*, size_t) override { return Return::END; } void wait_once(std::shared_ptr, std::function callback) override { assert(false); callback(); } }; class NoContentResponse : public Transport::Response { public: explicit NoContentResponse(std::unique_ptr response) : response_(std::move(response)) { } uint16_t code() const override { return response_->code(); } std::vector> const& headers() const override { return response_->headers(); } std::unique_ptr open_content() override { return std::make_unique(); } void add_header(std::string name, std::string value) override { response_->add_header(std::move(name), std::move(value)); } private: std::unique_ptr response_; }; void copy_headers(Transport::Response const* src, Transport::Response* dst, std::string_view name) { for (auto const& header_pair : src->headers()) { if (header_pair.first == name) { dst->add_header(std::string(header_pair.first), std::string(header_pair.second)); } } } std::optional get_first_header( Transport::Response const* resp, std::string_view name) { for (auto const& header_pair : resp->headers()) { if (header_pair.first == name) { return header_pair.second; } } return std::nullopt; } class DefaultHandler : public Transport::Handler { public: DefaultHandler(std::shared_ptr logger, Transport::Handler* handler) : logger_(logger), handler_(handler) {} std::unique_ptr request( Transport* transport, Transport::Request const* request) override { bool include_content; if (request->method() == "GET") { include_content = true; } else if (request->method() == "HEAD") { include_content = false; } else { return transport->create_data(405, ""); } auto clean_path = path::cleanup(request->path()); if (clean_path != request->path()) { auto response = transport->create_redirect(clean_path, false); return response; } auto response = handler_->request(transport, request); if (!response) return nullptr; bool not_modified = false; { auto values = request->header_all("if-none-match"); auto etag = get_first_header(response.get(), "ETag"); bool match = false; for (auto const& value : values) { if (value == "*") { match = true; break; } if (!etag) continue; if (str::starts_with(value, "W/") || str::starts_with(value, "w/")) { if (value.compare(2, value.size() - 2, etag.value()) == 0) { match = true; break; } } else { if (value == etag.value()) { match = true; break; } } } if (match) not_modified = true; } // TODO: Add If-Modified-Since? Not useful right now tho as we have // no content that returns Date: headers. if (not_modified) { auto new_response = transport->create_data(304, ""); copy_headers(response.get(), new_response.get(), "Cache-Control"); copy_headers(response.get(), new_response.get(), "Content-Location"); copy_headers(response.get(), new_response.get(), "Date"); copy_headers(response.get(), new_response.get(), "ETag"); copy_headers(response.get(), new_response.get(), "Expires"); copy_headers(response.get(), new_response.get(), "Vary"); return new_response; } return include_content ? std::move(response) : std::make_unique(std::move(response)); } private: std::shared_ptr logger_; Transport::Handler* const handler_; }; } // namespace void Transport::Response::open_content_async( std::shared_ptr, std::function)> callback) { assert(false); callback(open_content()); } std::unique_ptr Transport::create_ok_data( std::string data) { if (data.empty()) return create_data(204, std::move(data)); return create_data(200, std::move(data)); } std::unique_ptr Transport::create_ok_file( std::filesystem::path path) { return create_file(200, std::move(path)); } std::unique_ptr Transport::create_ok_exif_thumbnail( std::filesystem::path path) { return create_exif_thumbnail(200, std::move(path)); } std::unique_ptr Transport::create_not_found() { return create_data(404, std::string()); } std::unique_ptr Transport::create_redirect( std::string location, bool temporary) { auto ret = create_data(temporary ? 302 : 301, std::string()); ret->add_header("Location", std::move(location)); return ret; } std::unique_ptr Transport::create_default_handler( std::shared_ptr logger, Handler* handler) { return std::make_unique(logger, handler); }