diff options
Diffstat (limited to 'url_handler/handler.cpp')
-rw-r--r-- | url_handler/handler.cpp | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/url_handler/handler.cpp b/url_handler/handler.cpp new file mode 100644 index 0000000..b202d5b --- /dev/null +++ b/url_handler/handler.cpp @@ -0,0 +1,146 @@ +#include <fstream> +#include <filesystem> +#include <unordered_map> +#include <mutex> +#include <thread> +#include <memory> +#include <cstdlib> +#include "handler.h" + +using namespace std; + + +static string shell_escape(const string &str) { + string res; + res.reserve(str.size() + 16); + res += '\''; + + for (char c : str) { + if (c == '\'') res += "'\"'\"'"; + else res += c; + } + + res += '\''; + return res; +} + +static string read_file(const string &fname) { + ifstream f(fname); + if (!f) return {}; + + string contents; + char buffer[4096]; + while (true) { + f.read(buffer, sizeof buffer); + contents.insert(contents.size(), buffer, f.gcount()); + if (!f) break; + } + + return contents; +} + +static string download_url(const string &url) { + string tempdir_name = "/tmp/tmp.url_handler.XXXXXX"; + if (mkdtemp(tempdir_name.data()) == nullptr) { + perror("mkdtemp"); + return {}; + } + + string tempfile_name = tempdir_name + "/file"; + + string cmd = "curl -sL " + shell_escape(url) + " >" + shell_escape(tempfile_name); + // fprintf(stderr, "cmd = %s\n", cmd.data()); + + system(cmd.data()); + + string response = read_file(tempfile_name); + filesystem::remove_all(tempdir_name); + + fprintf(stderr, "Received response for url '%s'\n", url.data()); + + return response; +} + +struct State { + string url; + + thread download_thread; + + // mutex protects 'response' and 'response_present'; the download thread + // sets 'response_present' to true and simultaneously 'response' to the + // right value. After 'response_present' can be observed to be true by the + // main thread, the download thread doesn't touch anything anymore. + mutex res_mt; + bool response_present = false; + string response; + + // owned by host thread + size_t rescur = 0; +}; + +static unordered_map<uint32_t, unique_ptr<State>> state_map; + +uint16_t handle_icmp(uint32_t source_addr, const array<uint8_t, 16> &payload) { + auto state_it = state_map.find(source_addr); + if (state_it == state_map.end()) { + state_it = state_map.emplace(source_addr, make_unique<State>()).first; + } + State &state = *state_it->second; + + switch (payload[0]) { + case 100: // url fragment + // fprintf(stderr, "msg(%u): url fragment\n", source_addr); + { + lock_guard<mutex> guard(state.res_mt); + if (state.response_present) return 0; + } + for (size_t i = 1; i < 16; i++) { + if (payload[i] == '\0') { + fprintf(stderr, "URL received: <%s>\n", state.url.data()); + state.download_thread = thread([&state]() { + string response = download_url(state.url); + + { + lock_guard<mutex> guard(state.res_mt); + state.response = move(response); + state.response_present = true; + } + + // fprintf(stderr, "[dlth] response received: <%s>\n", state.response.data()); + }); + break; + } + state.url.push_back(payload[i]); + } + return 1; + + case 101: { // query whether response is ready + // fprintf(stderr, "msg(%u): ready query\n", source_addr); + lock_guard<mutex> guard(state.res_mt); + return state.response_present ? 1 : 0; + } + + case 102: { // get response + // fprintf(stderr, "msg(%u): get_response\n", source_addr); + { + lock_guard<mutex> guard(state.res_mt); + if (!state.response_present) return 0; + } + + uint16_t retval = 0; + for (size_t i = 0; i < 2 && state.rescur < state.response.size(); i++) { + retval |= (uint16_t)state.response[state.rescur] << (8 * i); + state.rescur++; + } + + if (state.rescur == state.response.size()) { + state.download_thread.join(); // should already have exited + state_map.erase(state_it); + } + // fprintf(stderr, " -> retval %016X\n", (unsigned)retval); + return retval; + } + } + + return 0; +} |