From ea0832e4ad57f6e15b0e5288d2bfb821f8985256 Mon Sep 17 00:00:00 2001 From: Fabrice Reix Date: Sat, 22 Jan 2022 20:19:15 +0100 Subject: [PATCH] Add square brackets in key-string --- integration/tests/form_params.curl | 4 +- integration/tests/form_params.html | 4 +- integration/tests/form_params.hurl | 4 +- integration/tests/form_params.json | 2 +- integration/tests/form_params.py | 2 + packages/hurl/tests/libcurl.rs | 10 +++- packages/hurl_core/src/format/html.rs | 2 +- packages/hurl_core/src/parser/string.rs | 62 +++++++++++++++++++++++-- 8 files changed, 80 insertions(+), 10 deletions(-) diff --git a/integration/tests/form_params.curl b/integration/tests/form_params.curl index 1df4792a5a2..1be0c760634 100644 --- a/integration/tests/form_params.curl +++ b/integration/tests/form_params.curl @@ -1,2 +1,2 @@ -curl 'http://localhost:8000/form-params' --data 'param1=value1' --data 'param2=' --data 'param3=a%3Db' --data 'param4=a%253db' -curl 'http://localhost:8000/form-params' -H 'Content-Type: application/x-www-form-urlencoded' --data 'param1=value1¶m2=¶m3=a%3db¶m4=a%253db' +curl 'http://localhost:8000/form-params' --data 'param1=value1' --data 'param2=' --data 'param3=a%3Db' --data 'param4=a%253db' --data 'values[0]=0' --data 'values[1]=1' +curl 'http://localhost:8000/form-params' -H 'Content-Type: application/x-www-form-urlencoded' --data 'param1=value1¶m2=¶m3=a%3db¶m4=a%253db&values[0]=0&values[1]=1' diff --git a/integration/tests/form_params.html b/integration/tests/form_params.html index 2dec0e562dc..f9964371410 100644 --- a/integration/tests/form_params.html +++ b/integration/tests/form_params.html @@ -4,13 +4,15 @@ param2: param3: a=b param4: a%3db +values\u{5b}0\u{5d}: 0 +values[1]: 1 HTTP/1.0 200 # same version as raw POST http://localhost:8000/form-params Content-Type: application/x-www-form-urlencoded -```param1=value1&param2=&param3=a%3db&param4=a%253db``` +```param1=value1&param2=&param3=a%3db&param4=a%253db&values[0]=0&values[1]=1``` HTTP/1.0 200 diff --git a/integration/tests/form_params.hurl b/integration/tests/form_params.hurl index b5beed0b925..28ee8ffcfa8 100644 --- a/integration/tests/form_params.hurl +++ b/integration/tests/form_params.hurl @@ -4,13 +4,15 @@ param1: value1 param2: param3: a=b param4: a%3db +values\u{5b}0\u{5d}: 0 +values[1]: 1 HTTP/1.0 200 # same version as raw POST http://localhost:8000/form-params Content-Type: application/x-www-form-urlencoded -```param1=value1¶m2=¶m3=a%3db¶m4=a%253db``` +```param1=value1¶m2=¶m3=a%3db¶m4=a%253db&values[0]=0&values[1]=1``` HTTP/1.0 200 diff --git a/integration/tests/form_params.json b/integration/tests/form_params.json index a25be72c198..1143348f99d 100644 --- a/integration/tests/form_params.json +++ b/integration/tests/form_params.json @@ -1 +1 @@ -{"entries":[{"request":{"method":"POST","url":"http://localhost:8000/form-params","form_params":[{"name":"param1","value":"value1"},{"name":"param2","value":""},{"name":"param3","value":"a=b"},{"name":"param4","value":"a%3db"}]},"response":{"version":"HTTP/1.0","status":200}},{"request":{"method":"POST","url":"http://localhost:8000/form-params","headers":[{"name":"Content-Type","value":"application/x-www-form-urlencoded"}],"body":{"type":"raw-string","value":"param1=value1¶m2=¶m3=a%3db¶m4=a%253db"}},"response":{"version":"HTTP/1.0","status":200}}]} +{"entries":[{"request":{"method":"POST","url":"http://localhost:8000/form-params","form_params":[{"name":"param1","value":"value1"},{"name":"param2","value":""},{"name":"param3","value":"a=b"},{"name":"param4","value":"a%3db"},{"name":"values[0]","value":"0"},{"name":"values[1]","value":"1"}]},"response":{"version":"HTTP/1.0","status":200}},{"request":{"method":"POST","url":"http://localhost:8000/form-params","headers":[{"name":"Content-Type","value":"application/x-www-form-urlencoded"}],"body":{"type":"raw-string","value":"param1=value1¶m2=¶m3=a%3db¶m4=a%253db&values[0]=0&values[1]=1"}},"response":{"version":"HTTP/1.0","status":200}}]} \ No newline at end of file diff --git a/integration/tests/form_params.py b/integration/tests/form_params.py index de4352581b7..a8ce10596fc 100644 --- a/integration/tests/form_params.py +++ b/integration/tests/form_params.py @@ -7,6 +7,8 @@ def form_params(): assert request.form['param2'] == '' assert request.form['param3'] == 'a=b' assert request.form['param4'] == 'a%3db' + assert request.form['values[0]'] == '0' + assert request.form['values[1]'] == '1' return '' diff --git a/packages/hurl/tests/libcurl.rs b/packages/hurl/tests/libcurl.rs index 2885a4ecc74..9648c1acea9 100644 --- a/packages/hurl/tests/libcurl.rs +++ b/packages/hurl/tests/libcurl.rs @@ -280,6 +280,14 @@ fn test_form_params() { name: "param4".to_string(), value: "a%3db".to_string(), }, + Param { + name: "values[0]".to_string(), + value: "0".to_string(), + }, + Param { + name: "values[1]".to_string(), + value: "1".to_string(), + }, ], multipart: vec![], cookies: vec![], @@ -288,7 +296,7 @@ fn test_form_params() { }; assert_eq!( client.curl_command_line(&request_spec), - "curl 'http://localhost:8000/form-params' --data 'param1=value1' --data 'param2=' --data 'param3=a%3Db' --data 'param4=a%253db'".to_string() + "curl 'http://localhost:8000/form-params' --data 'param1=value1' --data 'param2=' --data 'param3=a%3Db' --data 'param4=a%253db' --data 'values[0]=0' --data 'values[1]=1'".to_string() ); let (request, response) = client.execute(&request_spec).unwrap(); diff --git a/packages/hurl_core/src/format/html.rs b/packages/hurl_core/src/format/html.rs index 4486dcb1127..f2f3e6ff4d8 100644 --- a/packages/hurl_core/src/format/html.rs +++ b/packages/hurl_core/src/format/html.rs @@ -215,7 +215,7 @@ impl Htmlable for KeyValue { add_line_terminators(&mut buffer, self.line_terminators.clone()); buffer.push_str(""); buffer.push_str(self.space0.to_html().as_str()); - buffer.push_str(format!("{}", self.key.value).as_str()); + buffer.push_str(format!("{}", self.key.encoded).as_str()); buffer.push_str(self.space1.to_html().as_str()); buffer.push_str(":"); buffer.push_str(self.space2.to_html().as_str()); diff --git a/packages/hurl_core/src/parser/string.rs b/packages/hurl_core/src/parser/string.rs index c4dbfd9c3d3..af90cd4a43c 100644 --- a/packages/hurl_core/src/parser/string.rs +++ b/packages/hurl_core/src/parser/string.rs @@ -97,7 +97,13 @@ pub fn unquoted_string_key(reader: &mut Reader) -> ParseResult<'static, EncodedS match reader.read() { None => break, Some(c) => { - if c.is_alphanumeric() || c == '_' || c == '-' || c == '.' { + if c.is_alphanumeric() + || c == '_' + || c == '-' + || c == '.' + || c == '[' + || c == ']' + { value.push(c); encoded.push_str(reader.from(save.cursor).as_str()) } else { @@ -113,8 +119,8 @@ pub fn unquoted_string_key(reader: &mut Reader) -> ParseResult<'static, EncodedS } } - // check nonempty - if value.is_empty() { + // check nonempty/ starts with [ + if value.is_empty() || encoded.starts_with('[') { return Err(Error { pos: start, recoverable: true, @@ -446,6 +452,56 @@ mod tests { assert_eq!(reader.state.cursor, 15); } + #[test] + fn test_unquoted_key_with_square_bracket() { + let mut reader = Reader::init("values\\u{5b}0\\u{5d} :"); + assert_eq!( + unquoted_string_key(&mut reader).unwrap(), + EncodedString { + value: "values[0]".to_string(), + encoded: "values\\u{5b}0\\u{5d}".to_string(), + quotes: false, + source_info: SourceInfo::init(1, 1, 1, 20), + } + ); + assert_eq!(reader.state.cursor, 19); + + let mut reader = Reader::init("values[0] :"); + assert_eq!( + unquoted_string_key(&mut reader).unwrap(), + EncodedString { + value: "values[0]".to_string(), + encoded: "values[0]".to_string(), + quotes: false, + source_info: SourceInfo::init(1, 1, 1, 10), + } + ); + assert_eq!(reader.state.cursor, 9); + } + + #[test] + fn test_unquoted_keys_ignore_start_square_bracket() { + let mut reader = Reader::init("[0]:"); + let error = unquoted_string_key(&mut reader).err().unwrap(); + assert!(error.recoverable); + assert_eq!(reader.state.cursor, 3); + } + + #[test] + fn test_unquoted_keys_accept_start_escape_square_bracket() { + let mut reader = Reader::init("\\u{5b}0\\u{5d}"); + assert_eq!( + unquoted_string_key(&mut reader).unwrap(), + EncodedString { + value: "[0]".to_string(), + encoded: "\\u{5b}0\\u{5d}".to_string(), + quotes: false, + source_info: SourceInfo::init(1, 1, 1, 14), + } + ); + assert_eq!(reader.state.cursor, 13); + } + #[test] fn test_unquoted_key_error() { let mut reader = Reader::init("");