From 7bf3fd765fb6e97413d654eed786807eb25d342c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20D=C3=ADaz?= Date: Tue, 10 Oct 2023 12:48:47 +0200 Subject: [PATCH] feat: implement HttpHead as new RADType --- data_structures/src/chain/mod.rs | 10 ++++-- data_structures/src/proto/mod.rs | 2 ++ data_structures/src/serialization_helpers.rs | 4 +-- rad/src/lib.rs | 36 +++++++++++++++++--- schemas/witnet/witnet.proto | 3 +- 5 files changed, 45 insertions(+), 10 deletions(-) diff --git a/data_structures/src/chain/mod.rs b/data_structures/src/chain/mod.rs index 34910b8a5..757f6c46e 100644 --- a/data_structures/src/chain/mod.rs +++ b/data_structures/src/chain/mod.rs @@ -1645,11 +1645,14 @@ pub enum RADType { /// HTTP POST request #[serde(rename = "HTTP-POST")] HttpPost, + /// HTTP HEAD request + #[serde(rename = "HTTP-HEAD")] + HttpHead, } impl RADType { pub fn is_http(&self) -> bool { - matches!(self, RADType::HttpGet | RADType::HttpPost) + matches!(self, RADType::HttpGet | RADType::HttpPost | RADType::HttpHead) } } @@ -1701,7 +1704,7 @@ pub struct RADRetrieve { pub script: Vec, /// Body of a HTTP-POST request pub body: Vec, - /// Extra headers of a HTTP-GET or HTTP-POST request + /// Extra headers of a HTTP-GET, HTTP-POST or HTTP-HEAD request pub headers: Vec<(String, String)>, } @@ -1809,7 +1812,8 @@ impl RADRetrieve { &[Field::Kind, Field::Url, Field::Script], &[Field::Body, Field::Headers], ) - } + }, + RADType::HttpHead => check(&[Field::Kind, Field::Url, Field::Script], &[Field::Headers]) } } diff --git a/data_structures/src/proto/mod.rs b/data_structures/src/proto/mod.rs index 4d681d8b9..c1bb007ca 100644 --- a/data_structures/src/proto/mod.rs +++ b/data_structures/src/proto/mod.rs @@ -51,6 +51,7 @@ impl ProtobufConvert for chain::RADType { chain::RADType::HttpGet => witnet::DataRequestOutput_RADRequest_RADType::HttpGet, chain::RADType::Rng => witnet::DataRequestOutput_RADRequest_RADType::Rng, chain::RADType::HttpPost => witnet::DataRequestOutput_RADRequest_RADType::HttpPost, + chain::RADType::HttpHead => witnet::DataRequestOutput_RADRequest_RADType::HttpHead, } } @@ -60,6 +61,7 @@ impl ProtobufConvert for chain::RADType { witnet::DataRequestOutput_RADRequest_RADType::HttpGet => chain::RADType::HttpGet, witnet::DataRequestOutput_RADRequest_RADType::Rng => chain::RADType::Rng, witnet::DataRequestOutput_RADRequest_RADType::HttpPost => chain::RADType::HttpPost, + witnet::DataRequestOutput_RADRequest_RADType::HttpHead => chain::RADType::HttpHead, }) } } diff --git a/data_structures/src/serialization_helpers.rs b/data_structures/src/serialization_helpers.rs index 7f7d7d4e6..a8d59ee69 100644 --- a/data_structures/src/serialization_helpers.rs +++ b/data_structures/src/serialization_helpers.rs @@ -360,7 +360,7 @@ struct RADRetrieveSerializationHelperJson { /// Body of a HTTP-POST request #[serde(default, skip_serializing_if = "Vec::is_empty")] pub body: Vec, - /// Extra headers of a HTTP-GET or HTTP-POST request + /// Extra headers of a HTTP-GET, HTTP-HEAD or HTTP-POST request #[serde(default, skip_serializing_if = "Vec::is_empty")] pub headers: Vec<(String, String)>, } @@ -377,7 +377,7 @@ struct RADRetrieveSerializationHelperBincode { pub script: Vec, /// Body of a HTTP-POST request pub body: Vec, - /// Extra headers of a HTTP-GET or HTTP-POST request + /// Extra headers of a HTTP-GET, HTTP-HEAD or HTTP-POST request pub headers: Vec<(String, String)>, } diff --git a/rad/src/lib.rs b/rad/src/lib.rs index 64c826780..056323721 100644 --- a/rad/src/lib.rs +++ b/rad/src/lib.rs @@ -25,10 +25,11 @@ use crate::{ create_radon_script_from_filters_and_reducer, execute_radon_script, unpack_radon_script, RadonScriptExecutionSettings, }, - types::{array::RadonArray, bytes::RadonBytes, string::RadonString, RadonTypes}, + types::{array::RadonArray, bytes::RadonBytes, string::RadonString, map::RadonMap, RadonTypes}, user_agents::UserAgent, }; use core::convert::From; +use std::collections::BTreeMap; use witnet_net::client::http::{WitnetHttpBody, WitnetHttpRequest}; pub mod conditions; @@ -173,6 +174,25 @@ fn string_response_with_data_report( execute_radon_script(input, &radon_script, context, settings) } +/// Handle HTTP-HEAD response with data, and return a `RadonReport`. +fn headers_response_with_data_report( + retrieve: &RADRetrieve, + response: &str, + context: &mut ReportContext, + settings: RadonScriptExecutionSettings, +) -> Result> { + let headers: BTreeMap = response.split("\r\n").map(|line| { + let parts: Vec<&str> = line.split(":").map(|part| part.trim()).collect(); + // todo: check there are two parts, and two parts only + // todo: make sure that values from repeated keys get appended within a RadonArray + (String::from(parts[0]), RadonTypes::from(RadonString::from(parts[1]))) + }).collect(); + let input = RadonTypes::from(RadonMap::from(headers)); + let radon_script = unpack_radon_script(&retrieve.script)?; + + execute_radon_script(input, &radon_script, context, settings) +} + /// Handle Rng response with data report fn rng_response_with_data_report( response: &str, @@ -196,7 +216,10 @@ pub fn run_retrieval_with_data_report( RADType::Rng => rng_response_with_data_report(response, context), RADType::HttpPost => { string_response_with_data_report(retrieve, response, context, settings) - } + }, + RADType::HttpHead => { + headers_response_with_data_report(retrieve, response, context, settings) + }, _ => Err(RadError::UnknownRetrieval), } } @@ -214,7 +237,7 @@ pub fn run_retrieval_with_data( .map(RadonReport::into_inner) } -/// Handle generic HTTP (GET/POST) response +/// Handle generic HTTP (GET/POST/HEAD) response async fn http_response( retrieve: &RADRetrieve, context: &mut ReportContext, @@ -258,7 +281,11 @@ async fn http_response( builder.method("POST").uri(&retrieve.url), WitnetHttpBody::from(retrieve.body.clone()), ) - } + }, + RADType::HttpHead => ( + builder.method("HEAD").uri(&retrieve.url), + WitnetHttpBody::empty(), + ), _ => panic!( "Called http_response with invalid retrieval kind {:?}", retrieve.kind @@ -357,6 +384,7 @@ pub async fn run_retrieval_report( RADType::HttpGet => http_response(retrieve, context, settings, client).await, RADType::Rng => rng_response(context, settings).await, RADType::HttpPost => http_response(retrieve, context, settings, client).await, + RADType::HttpHead => http_response(retrieve, context, settings, client).await, _ => Err(RadError::UnknownRetrieval), } } diff --git a/schemas/witnet/witnet.proto b/schemas/witnet/witnet.proto index 64b1b04e0..842009744 100644 --- a/schemas/witnet/witnet.proto +++ b/schemas/witnet/witnet.proto @@ -121,6 +121,7 @@ message DataRequestOutput { HttpGet = 1; Rng = 2; HttpPost = 3; + HttpHead = 4; } message RADFilter { uint32 op = 1; @@ -133,7 +134,7 @@ message DataRequestOutput { bytes script = 3; // Body of HTTP-POST request bytes body = 4; - // Extra headers for HTTP-GET and HTTP-POST requests + // Extra headers for HTTP-GET, HTTP-HEAD and HTTP-POST requests repeated StringPair headers = 5; } message RADAggregate {