void handle_request()

in src/visualization/server/server.cpp [113:336]


void handle_request(
    http::request<Body, http::basic_fields<Allocator>>&& req,
    Send&& send,
    const WebServer::plot_map& plots,
    const WebServer::table_vector& tables) {
    // Returns a bad request response
    auto const bad_request =
    [&req](boost::beast::string_view why)
    {
        http::response<http::string_body> res{http::status::bad_request, req.version()};
        res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
        res.set(http::field::content_type, "text/html");
        res.keep_alive(req.keep_alive());
        res.body() = why.to_string();
        res.prepare_payload();
        return res;
    };

    // Returns a not found response
    auto const not_found =
    [&req](boost::beast::string_view target)
    {
        http::response<http::string_body> res{http::status::not_found, req.version()};
        res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
        res.set(http::field::content_type, "text/html");
        res.keep_alive(req.keep_alive());
        res.body() = "The resource '" + target.to_string() + "' was not found.";
        res.prepare_payload();
        return res;
    };

    // Returns a server error response
    auto const server_error =
    [&req, &send](boost::beast::string_view what)
    {
        http::response<http::string_body> res{http::status::internal_server_error, req.version()};
        res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
        res.set(http::field::content_type, "text/html");
        res.keep_alive(req.keep_alive());
        res.body() = "An error occurred: '" + what.to_string() + "'";
        res.prepare_payload();
        return send(std::move(res));
    };

    // Make sure we can handle the method=
    if( req.method() != http::verb::get &&
        req.method() != http::verb::post &&
        req.method() != http::verb::head)
        return send(bad_request("Unknown HTTP-method"));

    auto const respond =
    [&req, &send, &server_error](const std::string& body, const std::string& content_type) {
        if(req.method() == http::verb::head) {
            http::response<http::empty_body> res{http::status::ok, req.version()};
            res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
            res.set(http::field::content_type, content_type);
            res.content_length(body.size());
            res.keep_alive(req.keep_alive());
            return send(std::move(res));
        } else if(req.method() == http::verb::get || req.method() == http::verb::post) {
            http::response<http::string_body> res{
                std::piecewise_construct,
                std::make_tuple(body),
                std::make_tuple(http::status::ok, req.version())};
            res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
            res.set(http::field::content_type, content_type);
            res.content_length(body.size());
            res.keep_alive(req.keep_alive());
            return send(std::move(res));
        } else {
            return server_error("Unexpected request method. Expected either HEAD or GET.");
        }
    };


    try {
        auto req_target = req.target();
        if (req_target.find("/spec/plot/") == 0) {
            const size_t spec_url_length = sizeof("/spec/plot/") - 1;
            std::string plot_id = req_target.substr(spec_url_length).to_string();
            if (plots.find(plot_id) == plots.end()) {
                return server_error("Expected plot " + plot_id + " was not found");
            }
            const Plot& plot = plots.at(plot_id);

            // For now, force light mode, until we have dark mode
            // support for all visualizations and the web app itself.
            std::string plot_spec = plot.get_spec(tc_plot_color_light);
            std::stringstream ss;
            ss << "{\"type\":\"vega\",\"data\":";
            ss << plot_spec;
            ss << "}";
            return respond(ss.str(), "application/json");
        }

        if (req_target.find("/spec/table/") == 0) {
            const size_t spec_url_length = sizeof("/spec/table/") - 1;
            std::string table_id = req_target.substr(spec_url_length).to_string();
            size_t idx = std::stoul(table_id);
            if (idx >= tables.size()) {
                return server_error("Expected table " + table_id + " was not found");
            }
            const WebServer::table& container = tables[idx];
            const std::shared_ptr<turi::unity_sframe>& table = container.sf;
            std::stringstream ss;
            ss << "{\"type\":\"table\",\"data\":";
            ss << table_spec(table, container.title, table_id);
            ss << "}";
            return respond(ss.str(), "application/json");
        }

        if (req_target.find("/data/plot/") == 0) {
            const size_t data_url_length = sizeof("/data/plot/") - 1;
            std::string plot_id = req_target.substr(data_url_length).to_string();
            if (plots.find(plot_id) == plots.end()) {
                return server_error("Expected plot " + plot_id + " was not found");
            }
            const Plot& plot = plots.at(plot_id);
            std::stringstream plot_data;
            plot_data << "{\"data_spec\":";
            plot_data << plot.get_next_data();
            plot_data << "}";
            return respond(plot_data.str(), "application/json");
        }

        if (req_target.find("/data/table/") == 0) {
            const size_t data_url_length = sizeof("/data/table/") - 1;
            std::string table_id = req_target.substr(data_url_length).to_string();

            std::string image_row, image_column;
            size_t image_url_pos = table_id.find("/image/");
            if (image_url_pos != std::string::npos) {
                // Found image URL request
                DASSERT_EQ(req.method(), http::verb::get);

                // chop off everything after /image/
                std::string image_requested = table_id.substr(image_url_pos + sizeof("/image/") - 1);

                // chop off everything before /image/
                table_id = table_id.substr(0, image_url_pos);
                size_t idx = std::stoul(table_id);
                if (idx >= tables.size()) {
                    return server_error("Expected table " + table_id + " was not found");
                }
                const WebServer::table& container = tables[idx];
                const std::shared_ptr<turi::unity_sframe>& table = container.sf;

                // extract row and column
                size_t slash_pos = image_requested.find("/");
                if (slash_pos == std::string::npos) {
                    return server_error("Could not parse image URL requested: " + req_target.to_string());
                }
                std::string row = image_requested.substr(0, slash_pos);
                size_t row_index = std::stoul(row);
                std::string column_name = image_requested.substr(slash_pos + 1);

                // find the image and return it as PNG data
                turi::gl_sarray gl_sa(table->select_column(column_name));
                turi::flex_image value = gl_sa[row_index].get<turi::flex_image>();
                std::string png_data = image_png_data(value, 200 /* resized_height */);
                return respond(png_data, "image/png");
            }

            size_t idx = std::stoul(table_id);
            if (idx >= tables.size()) {
                return server_error("Expected table " + table_id + " was not found");
            }
            const WebServer::table& container = tables[idx];
            const std::shared_ptr<turi::unity_sframe>& table = container.sf;

            // Read request type out of message body
            using boost::property_tree::ptree;
            ptree pt;
            std::string body = req.body();
            std::stringstream ss(body);
            read_json(ss, pt);
            std::string type = pt.get<std::string>("type");
            if (type == "rows") {
                size_t start = pt.get<size_t>("start");
                size_t end = pt.get<size_t>("end");

                return respond(table_data(table, container.reader.get(), start, end), "application/json");
            } else if (type == "accordion") {
                size_t row_index = pt.get<size_t>("index");
                std::string column_name = pt.get<std::string>("column");
                return respond(table_accordion(table, column_name, row_index), "application/json");
            } else {
                log_and_throw("Unexpected data table request type: " + type);
            }
        }

        // try to match a static file
        // note: server-side API caller should set static URL directory prior to any HTTP requests!
        DASSERT_NE(VISUALIZATION_WEB_SERVER_ROOT_DIRECTORY, "");
        std::string possible_file_path = VISUALIZATION_WEB_SERVER_ROOT_DIRECTORY + req_target.to_string();
        size_t query_pos = possible_file_path.rfind("?");
        if (query_pos != std::string::npos) {
            possible_file_path = possible_file_path.substr(0, query_pos);
        }
        auto possible_file_status = turi::fileio::get_file_status(possible_file_path);
        if (possible_file_status.first == turi::fileio::file_status::REGULAR_FILE) {
            // we can serve a file from this path
            turi::general_ifstream stream(possible_file_path);
            std::string file_contents{ std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>() };
            std::string type = mime_type(possible_file_path).to_string();
            return respond(file_contents, type);
        }

        // did not match any expected URL
        logstream(LOG_ERROR)
            << "WebServer: unrecognized destination requested in URL:  "
            << req_target << ". Err:" << possible_file_status.second
            << std::endl;

        return send(not_found(req_target));

    } catch (const std::exception& e) {
        return server_error(e.what());
    } catch (const std::string& s) {
        return server_error(s);
    } catch (...) {
        return server_error("Unknown exception");
    }
};