diff --git a/include/bitcoin/node/protocols/protocol_explore.hpp b/include/bitcoin/node/protocols/protocol_explore.hpp index adb7a681a..835a3575d 100644 --- a/include/bitcoin/node/protocols/protocol_explore.hpp +++ b/include/bitcoin/node/protocols/protocol_explore.hpp @@ -123,10 +123,10 @@ class BCN_API protocol_explore /// Senders. void send_json(const network::http::request& request, boost::json::value&& model) NOEXCEPT; - void send_data(const network::http::request& request, - system::data_chunk&& data) NOEXCEPT; void send_text(const network::http::request& request, std::string&& hexidecimal) NOEXCEPT; + void send_data(const network::http::request& request, + system::data_chunk&& bytes) NOEXCEPT; /// Receivers. void handle_receive_get(const code& ec, diff --git a/src/protocols/protocol_explore.cpp b/src/protocols/protocol_explore.cpp index 6f6f101fb..81dd09f39 100644 --- a/src/protocols/protocol_explore.cpp +++ b/src/protocols/protocol_explore.cpp @@ -30,7 +30,7 @@ using namespace std::placeholders; using namespace network::http; using namespace system; -const auto binary = mime_type::application_octet_stream; +const auto data = mime_type::application_octet_stream; const auto json = mime_type::application_json; const auto text = mime_type::text_plain; @@ -38,6 +38,10 @@ BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) // Handle get method. // ---------------------------------------------------------------------------- +// TODO: performance timing header. +// TODO: formatted error responses. +// TODO: priority sort and dispatch. +// TODO: URI path parse (see API doc). void protocol_explore::handle_receive_get(const code& ec, const method::get& request) NOEXCEPT @@ -75,67 +79,131 @@ void protocol_explore::handle_receive_get(const code& ec, return; } - /////////////////////////////////////////////////////////////////////////// - // TODO: formatted error responses. - // TODO: priority sort and dispatch. - // TODO: URI path parse (see API doc). - // TODO: performance timing header. - /////////////////////////////////////////////////////////////////////////// + if (const auto parts = split(uri.path(), "/"); + !parts.empty() && parts.size() != two) + { + send_bad_target(*request); + return; + } + else + { + const auto hd = parts.front() == "header" || parts.front() == "hd"; + const auto bk = parts.front() == "block" || parts.front() == "bk"; + const auto tx = parts.front() == "transaction" || parts.front() == "tx"; + if (!hd && !bk && !tx) + { + send_bad_target(*request); + return; + } + + auto params = uri.decode_query(); + const auto format = params["format"]; + const auto accepts = to_mime_types((*request)[field::accept]); + const auto is_json = contains(accepts, json) || format == "json"; + const auto is_text = contains(accepts, text) || format == "text"; + const auto is_data = contains(accepts, data) || format == "data"; + const auto wit = params["witness"] != "false"; + const auto hex = parts.back(); - const auto get_tx = [&](const auto& path) NOEXCEPT - { hash_digest hash{}; - if (!decode_hash(hash, path)) + if ((is_json || is_text || is_data) && !decode_hash(hash, hex)) { send_bad_target(*request); - return chain::transaction::cptr{}; + return; } const auto& query = archive(); - return query.get_transaction(query.to_tx(hash), true); - }; - auto params = uri.decode_query(); - const auto hex = trim_copy(uri.path(), { "/" }); - const auto accepts = to_mime_types((*request)[field::accept]); - - if (contains(accepts, json) || params["format"] == "json") - { - const auto tx = get_tx(hex); - if (!tx) + if (is_json) { + if (hd) + { + if (const auto ptr = query.get_header(query.to_header(hash))) + { + send_json(*request, value_from(*ptr)); + return; + } + } + else if (bk) + { + if (const auto ptr = query.get_block(query.to_header(hash), wit)) + { + send_json(*request, value_from(*ptr)); + return; + } + } + else + { + if (const auto ptr = query.get_transaction(query.to_tx(hash), wit)) + { + send_json(*request, value_from(*ptr)); + return; + } + } + send_not_found(*request); return; } - - send_json(*request, value_from(*tx)); - return; - } - - if (contains(accepts, binary) || params["format"] == "binary") - { - const auto tx = get_tx(hex); - if (!tx) + else if (is_text) { + if (hd) + { + if (const auto ptr = query.get_header(query.to_header(hash))) + { + send_text(*request, encode_base16(ptr->to_data())); + return; + } + } + else if (bk) + { + if (const auto ptr = query.get_block(query.to_header(hash), wit)) + { + send_text(*request, encode_base16(ptr->to_data(wit))); + return; + } + } + else + { + if (const auto ptr = query.get_transaction(query.to_tx(hash), wit)) + { + send_text(*request, encode_base16(ptr->to_data(wit))); + return; + } + } + send_not_found(*request); return; } - - send_data(*request, tx->to_data(true)); - return; - } - - if (contains(accepts, text) || params["format"] == "text") - { - const auto tx = get_tx(hex); - if (!tx) + else if (is_data) { + if (hd) + { + if (const auto ptr = query.get_header(query.to_header(hash))) + { + send_data(*request, ptr->to_data()); + return; + } + } + else if (bk) + { + if (const auto ptr = query.get_block(query.to_header(hash), wit)) + { + send_data(*request, ptr->to_data(wit)); + return; + } + } + else + { + if (const auto ptr = query.get_transaction(query.to_tx(hash), wit)) + { + send_data(*request, ptr->to_data(wit)); + return; + } + } + send_not_found(*request); return; } - - send_text(*request, encode_base16(tx->to_data(true))); - return; } // Default to html (single page site). @@ -179,31 +247,31 @@ void protocol_explore::send_json(const request& request, response response{ status::ok, request.version() }; add_common_headers(response, request); response.set(field::content_type, from_mime_type(json)); - response.body() = { std::move(model) }; + response.body() = { std::move(model), std::make_shared(10 * 1024 * 1024) }; response.prepare_payload(); SEND(std::move(response), handle_complete, _1, error::success); } -void protocol_explore::send_data(const request& request, - data_chunk&& data) NOEXCEPT +void protocol_explore::send_text(const request& request, + std::string&& hexidecimal) NOEXCEPT { BC_ASSERT_MSG(stranded(), "strand"); - data_response response{ status::ok, request.version() }; + string_response response{ status::ok, request.version() }; add_common_headers(response, request); - response.set(field::content_type, from_mime_type(binary)); - response.body() = std::move(data); + response.set(field::content_type, from_mime_type(text)); + response.body() = std::move(hexidecimal); response.prepare_payload(); SEND(std::move(response), handle_complete, _1, error::success); } -void protocol_explore::send_text(const request& request, - std::string&& hexidecimal) NOEXCEPT +void protocol_explore::send_data(const request& request, + data_chunk&& bytes) NOEXCEPT { BC_ASSERT_MSG(stranded(), "strand"); - string_response response{ status::ok, request.version() }; + data_response response{ status::ok, request.version() }; add_common_headers(response, request); - response.set(field::content_type, from_mime_type(text)); - response.body() = std::move(hexidecimal); + response.set(field::content_type, from_mime_type(data)); + response.body() = std::move(bytes); response.prepare_payload(); SEND(std::move(response), handle_complete, _1, error::success); }