diff --git a/integration/report/html/index.html b/integration/report/html/index.html index 3ed0102d6..fe218eab0 100644 --- a/integration/report/html/index.html +++ b/integration/report/html/index.html @@ -1,2 +1,2 @@ -Hurl Report

Hurl Report

Thu, 10 Jun 2021 15:24:27 +0200
total: 78 (100%)
failure: 33 (42.3%)
success: 45 (57.7%)
filenamestatusduration
tests/assert_base64.hurlsuccess0.005s
tests/assert_header.hurlsuccess0.009s
tests/assert_json.hurlsuccess0.023s
tests/assert_match.hurlsuccess0.021s
tests/assert_regex.hurlsuccess0.009s
tests/assert_status_code.hurlsuccess0.01s
tests/assert_xpath.hurlsuccess0.004s
tests/basic_authentication.hurlsuccess0.005s
tests/bom.hurlsuccess0.004s
tests/bytes.hurlsuccess0.004s
tests/capture_and_assert.hurlsuccess0.005s
tests/captures.hurlsuccess0.017s
tests/color.hurlsuccess0s
tests/compressed.hurlsuccess0.019s
tests/cookie_file.hurlsuccess0.004s
tests/cookies.hurlsuccess0.036s
tests/cookie_storage.hurlsuccess0.007s
tests/delete.hurlsuccess0.004s
tests/empty.hurlsuccess0s
tests/encoding.hurlsuccess0.01s
tests/error_assert_base64.hurlfailure0.004s
tests/error_assert_content_encoding.hurlfailure0.006s
tests/error_assert_decompress.hurlfailure0.003s
tests/error_assert_file.hurlfailure0.004s
tests/error_assert_header_not_found.hurlfailure0.003s
tests/error_assert_header_value.hurlfailure0.005s
tests/error_assert_http_version.hurlfailure0.003s
tests/error_assert_invalid_predicate_type.hurlfailure0.006s
tests/error_assert_match_utf8.hurlfailure0.004s
tests/error_assert_query_cookie.hurlfailure0.012s
tests/error_assert_query_invalid_regex.hurlfailure0.004s
tests/error_assert_query_invalid_xpath.hurlfailure0.006s
tests/error_assert_status.hurlfailure0.005s
tests/error_assert_template_variable_not_found.hurlfailure0.005s
tests/error_assert_value_error.hurlfailure0.014s
tests/error_assert_variable.hurlfailure0.007s
tests/error_assert_xpath.hurlfailure0.005s
tests/error_body_json.hurlfailure0s
tests/error_connect_timeout.hurlfailure1.002s
tests/error_file_read_access.hurlfailure0s
tests/error_http_connection.hurlfailure0.034s
tests/error_invalid_jsonpath.hurlfailure0.012s
tests/error_invalid_url.hurlfailure0s
tests/error_invalid_xml.hurlfailure0.005s
tests/error_max_redirect.hurlfailure0.046s
tests/error_multipart_form_data.hurlfailure0s
tests/error_predicate.hurlfailure0.021s
tests/error_query_header_not_found.hurlfailure0.01s
tests/error_query_invalid_json.hurlfailure0.013s
tests/error_query_invalid_utf8.hurlfailure0.006s
tests/error_template_variable_not_found.hurlfailure0s
tests/error_template_variable_not_renderable.hurlfailure0.005s
tests/error_timeout.hurlfailure1.002s
tests/expect.hurlsuccess0.006s
tests/follow_redirect.hurlsuccess0.011s
tests/form_params.hurlsuccess0.015s
tests/headers.hurlsuccess0.028s
tests/hello.hurlsuccess0.041s
tests/include.hurlsuccess0.013s
tests/large.hurlsuccess7.459s
tests/multipart_form_data.hurlsuccess0.006s
tests/no_entry.hurlsuccess0.001s
tests/output.hurlsuccess0.012s
tests/patch.hurlsuccess0.004s
tests/post_base64.hurlsuccess0.005s
tests/post_bytes.hurlsuccess0.004s
tests/post_file.hurlsuccess0.004s
tests/post_json.hurlsuccess0.027s
tests/post_multilines.hurlsuccess0.009s
tests/post_xml.hurlsuccess0.006s
tests/predicates-string.hurlsuccess0.009s
tests/proxy.hurlsuccess0.021s
tests/put.hurlsuccess0.008s
tests/querystring_params.hurlsuccess0.019s
tests/redirect.hurlsuccess0.007s
tests/user_in_url.hurlsuccess0.006s
tests/utf8.hurlsuccess0.009s
tests/variables.hurlsuccess0.007s
\ No newline at end of file +Hurl Report

Hurl Report

Tue, 15 Jun 2021 21:19:59 +0200
total: 78 (100%)
failure: 33 (42.3%)
success: 45 (57.7%)
filenamestatusduration
tests/assert_base64.hurlsuccess0.004s
tests/assert_header.hurlsuccess0.006s
tests/assert_json.hurlsuccess0.026s
tests/assert_match.hurlsuccess0.019s
tests/assert_regex.hurlsuccess0.007s
tests/assert_status_code.hurlsuccess0.014s
tests/assert_xpath.hurlsuccess0.006s
tests/basic_authentication.hurlsuccess0.003s
tests/bom.hurlsuccess0.008s
tests/bytes.hurlsuccess0.005s
tests/capture_and_assert.hurlsuccess0.007s
tests/captures.hurlsuccess0.012s
tests/color.hurlsuccess0s
tests/compressed.hurlsuccess0.016s
tests/cookie_file.hurlsuccess0.007s
tests/cookies.hurlsuccess0.028s
tests/cookie_storage.hurlsuccess0.009s
tests/delete.hurlsuccess0.011s
tests/empty.hurlsuccess0s
tests/encoding.hurlsuccess0.007s
tests/error_assert_base64.hurlfailure0.004s
tests/error_assert_content_encoding.hurlfailure0.004s
tests/error_assert_decompress.hurlfailure0.015s
tests/error_assert_file.hurlfailure0.007s
tests/error_assert_header_not_found.hurlfailure0.01s
tests/error_assert_header_value.hurlfailure0.007s
tests/error_assert_http_version.hurlfailure0.005s
tests/error_assert_invalid_predicate_type.hurlfailure0.004s
tests/error_assert_match_utf8.hurlfailure0.007s
tests/error_assert_query_cookie.hurlfailure0.007s
tests/error_assert_query_invalid_regex.hurlfailure0.01s
tests/error_assert_query_invalid_xpath.hurlfailure0.004s
tests/error_assert_status.hurlfailure0.006s
tests/error_assert_template_variable_not_found.hurlfailure0.01s
tests/error_assert_value_error.hurlfailure0.013s
tests/error_assert_variable.hurlfailure0.007s
tests/error_assert_xpath.hurlfailure0.009s
tests/error_body_json.hurlfailure0s
tests/error_connect_timeout.hurlfailure1.004s
tests/error_file_read_access.hurlfailure0s
tests/error_http_connection.hurlfailure8.117s
tests/error_invalid_jsonpath.hurlfailure0.005s
tests/error_invalid_url.hurlfailure0s
tests/error_invalid_xml.hurlfailure0.01s
tests/error_max_redirect.hurlfailure0.021s
tests/error_multipart_form_data.hurlfailure0.004s
tests/error_predicate.hurlfailure0.019s
tests/error_query_header_not_found.hurlfailure0.005s
tests/error_query_invalid_json.hurlfailure0.004s
tests/error_query_invalid_utf8.hurlfailure0.007s
tests/error_template_variable_not_found.hurlfailure0s
tests/error_template_variable_not_renderable.hurlfailure0.007s
tests/error_timeout.hurlfailure1.002s
tests/expect.hurlsuccess0.005s
tests/follow_redirect.hurlsuccess0.008s
tests/form_params.hurlsuccess0.013s
tests/headers.hurlsuccess0.026s
tests/hello.hurlsuccess0.007s
tests/include.hurlsuccess0.01s
tests/large.hurlsuccess7.52s
tests/multipart_form_data.hurlsuccess0.011s
tests/no_entry.hurlsuccess0s
tests/output.hurlsuccess0.014s
tests/patch.hurlsuccess0.007s
tests/post_base64.hurlsuccess0.004s
tests/post_bytes.hurlsuccess0.004s
tests/post_file.hurlsuccess0.004s
tests/post_json.hurlsuccess0.028s
tests/post_multilines.hurlsuccess0.013s
tests/post_xml.hurlsuccess0.006s
tests/predicates-string.hurlsuccess0.01s
tests/proxy.hurlsuccess0.038s
tests/put.hurlsuccess0.004s
tests/querystring_params.hurlsuccess0.018s
tests/redirect.hurlsuccess0.009s
tests/user_in_url.hurlsuccess0.006s
tests/utf8.hurlsuccess0.004s
tests/variables.hurlsuccess0.005s
\ No newline at end of file diff --git a/integration/report/html/tests/error_predicate.hurl.html b/integration/report/html/tests/error_predicate.hurl.html index be1076586..7c8f7ae5c 100644 --- a/integration/report/html/tests/error_predicate.hurl.html +++ b/integration/report/html/tests/error_predicate.hurl.html @@ -56,5 +56,5 @@ span.line:before { color: dimgray; } -
GET http://localhost:8000/predicate/error/type
HTTP/1.0 200[Asserts]jsonpath "$.status" equals "true"#jsonpath "$.count" equals "0"jsonpath "$.count" equals 0jsonpath "$.message" equals 0jsonpath "$.empty" equals 0jsonpath "$.number" equals 1.1jsonpath "$.message" startsWith "hi"jsonpath "$.message" contains "hi"jsonpath "$.message" matches "hi"jsonpath "$.message" equals 1jsonpath "$.toto" existsjsonpath "$.message" not exists
+
GET http://localhost:8000/predicate/error/type
HTTP/1.0 200[Asserts]jsonpath "$.status" equals "true"#jsonpath "$.count" equals "0"jsonpath "$.count" equals 0jsonpath "$.message" equals 0jsonpath "$.empty" equals 0jsonpath "$.number" equals 1.1jsonpath "$.message" startsWith "hi"jsonpath "$.message" contains "hi"jsonpath "$.message" matches "hi"jsonpath "$.message" equals 1jsonpath "$.toto" existsjsonpath "$.message" not existsjsonpath "$.list" equals 2
\ No newline at end of file diff --git a/integration/report/tests.json b/integration/report/tests.json index a187c92a6..8f4da2dcc 100644 --- a/integration/report/tests.json +++ b/integration/report/tests.json @@ -6,11 +6,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-base64", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -31,7 +42,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -41,7 +52,7 @@ } ], "success": true, - "time": 5, + "time": 4, "cookies": [] }, { @@ -51,11 +62,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-header", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -112,7 +134,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -122,7 +144,7 @@ } ], "success": true, - "time": 9, + "time": 6, "cookies": [] }, { @@ -132,11 +154,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-json", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -157,7 +190,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -169,11 +202,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-json/index", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -194,7 +238,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -206,11 +250,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-json", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -231,7 +286,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -243,11 +298,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-json/list", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -268,7 +334,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -278,7 +344,7 @@ } ], "success": true, - "time": 23, + "time": 26, "cookies": [] }, { @@ -288,11 +354,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-match", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -313,7 +390,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -323,7 +400,7 @@ } ], "success": true, - "time": 21, + "time": 19, "cookies": [] }, { @@ -333,11 +410,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-regex", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -358,7 +446,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -368,7 +456,7 @@ } ], "success": true, - "time": 9, + "time": 7, "cookies": [] }, { @@ -378,11 +466,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-status-code", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -403,7 +502,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -415,11 +514,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-status-code", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -440,7 +550,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -452,11 +562,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-status-code", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -477,7 +598,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -487,7 +608,7 @@ } ], "success": true, - "time": 10, + "time": 14, "cookies": [] }, { @@ -497,11 +618,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-xpath", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -522,7 +654,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -532,7 +664,7 @@ } ], "success": true, - "time": 4, + "time": 6, "cookies": [] }, { @@ -542,11 +674,26 @@ "request": { "method": "GET", "url": "http://localhost:8000/basic-authentication", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Authorization", + "value": "Basic Ym9iOnNlY3JldA==" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -567,7 +714,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -577,7 +724,7 @@ } ], "success": true, - "time": 5, + "time": 3, "cookies": [] }, { @@ -587,11 +734,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/utf8_bom", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -612,7 +770,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -622,7 +780,7 @@ } ], "success": true, - "time": 4, + "time": 8, "cookies": [] }, { @@ -632,11 +790,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/bytes", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -657,7 +826,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -667,7 +836,7 @@ } ], "success": true, - "time": 4, + "time": 5, "cookies": [] }, { @@ -677,11 +846,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/capture-and-assert", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -702,7 +882,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -712,7 +892,7 @@ } ], "success": true, - "time": 5, + "time": 7, "cookies": [] }, { @@ -722,11 +902,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/captures", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -755,7 +946,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -766,7 +957,22 @@ { "request": { "method": "GET", - "url": "http://localhost:8000/captures-check", + "url": "http://localhost:8000/captures-check?param1=value1¶m2=Bob", + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [], "queryString": [ { "name": "param1", @@ -776,11 +982,7 @@ "name": "param2", "value": "Bob" } - ], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" + ] }, "response": { "httpVersion": "HTTP/1.0", @@ -801,7 +1003,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -813,11 +1015,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/captures-json", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -838,7 +1051,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -848,7 +1061,7 @@ } ], "success": true, - "time": 17, + "time": 12, "cookies": [] }, { @@ -865,11 +1078,26 @@ "request": { "method": "GET", "url": "http://localhost:8000/compressed/none", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Accept-Encoding", + "value": "gzip, deflate, br" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -890,7 +1118,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -902,11 +1130,26 @@ "request": { "method": "GET", "url": "http://localhost:8000/compressed/gzip", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Accept-Encoding", + "value": "gzip, deflate, br" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -931,7 +1174,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -943,11 +1186,26 @@ "request": { "method": "GET", "url": "http://localhost:8000/compressed/zlib", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Accept-Encoding", + "value": "gzip, deflate, br" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -972,7 +1230,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -984,11 +1242,26 @@ "request": { "method": "GET", "url": "http://localhost:8000/compressed/brotli", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Accept-Encoding", + "value": "gzip, deflate, br" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1013,7 +1286,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1025,11 +1298,26 @@ "request": { "method": "GET", "url": "http://localhost:8000/compressed/brotli_identity", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Accept-Encoding", + "value": "gzip, deflate, br" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1054,7 +1342,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1064,7 +1352,7 @@ } ], "success": true, - "time": 19, + "time": 16, "cookies": [] }, { @@ -1074,11 +1362,31 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookie_file", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Cookie", + "value": "cookie1=valueA" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [ + { + "name": "cookie1", + "value": "valueA" + } + ], + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1099,7 +1407,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1109,7 +1417,7 @@ } ], "success": true, - "time": 4, + "time": 7, "cookies": [] }, { @@ -1119,16 +1427,31 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookies/set-request-cookie1-valueA", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Cookie", + "value": "cookie1=valueA" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [ { "name": "cookie1", "value": "valueA" } ], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1149,7 +1472,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1161,11 +1484,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookies/assert-that-cookie1-is-not-in-session", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1186,7 +1520,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1198,8 +1532,24 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookies/set-multiple-request-cookies", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Cookie", + "value": "user1=Bob; user2=Bill" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [ { "name": "user1", @@ -1210,8 +1560,7 @@ "value": "Bill" } ], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1232,7 +1581,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1244,11 +1593,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookies/set-session-cookie2-valueA", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1279,7 +1639,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1291,11 +1651,31 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookies/assert-that-cookie2-is-valueA", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Cookie", + "value": "cookie2=valueA" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [ + { + "name": "cookie2", + "value": "valueA" + } + ], + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1316,7 +1696,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1328,16 +1708,35 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookies/assert-that-cookie2-is-valueA-and-valueB", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Cookie", + "value": "cookie2=valueA; cookie2=valueB" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [ + { + "name": "cookie2", + "value": "valueA" + }, { "name": "cookie2", "value": "valueB" } ], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1358,7 +1757,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1370,11 +1769,31 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookies/delete-cookie2", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Cookie", + "value": "cookie2=valueA" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [ + { + "name": "cookie2", + "value": "valueA" + } + ], + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1383,7 +1802,7 @@ { "name": "cookie2", "value": "", - "expires": "Thu, 10 Jun 2021 13:24:11 GMT", + "expires": "Tue, 15 Jun 2021 19:19:33 GMT", "max_age": 0, "path": "/" } @@ -1395,7 +1814,7 @@ }, { "name": "Set-Cookie", - "value": "cookie2=; Expires=Thu, 10 Jun 2021 13:24:11 GMT; Max-Age=0; Path=/" + "value": "cookie2=; Expires=Tue, 15 Jun 2021 19:19:33 GMT; Max-Age=0; Path=/" }, { "name": "Server", @@ -1407,7 +1826,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1419,11 +1838,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookies/assert-that-cookie2-is-not-in-session", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1444,7 +1874,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1456,11 +1886,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookies/set", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1519,7 +1960,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1529,7 +1970,7 @@ } ], "success": true, - "time": 36, + "time": 28, "cookies": [] }, { @@ -1539,11 +1980,31 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookie-storage/assert-that-cookie1-is-valueA", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Cookie", + "value": "cookie1=valueA" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [ + { + "name": "cookie1", + "value": "valueA" + } + ], + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1564,7 +2025,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1576,11 +2037,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/cookie-storage/assert-that-cookie1-is-not-in-session", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1601,7 +2073,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1611,7 +2083,7 @@ } ], "success": true, - "time": 7, + "time": 9, "cookies": [] }, { @@ -1621,11 +2093,22 @@ "request": { "method": "DELETE", "url": "http://localhost:8000/delete", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1646,7 +2129,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1656,7 +2139,7 @@ } ], "success": true, - "time": 4, + "time": 11, "cookies": [] }, { @@ -1673,11 +2156,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/encoding/utf8", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1698,7 +2192,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1710,11 +2204,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/encoding/latin1", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1735,7 +2240,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1745,7 +2250,7 @@ } ], "success": true, - "time": 10, + "time": 7, "cookies": [] }, { @@ -1755,11 +2260,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/assert-base64", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1780,7 +2296,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:33 GMT" } ] }, @@ -1800,11 +2316,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error/content-encoding", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1829,7 +2356,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -1839,7 +2366,7 @@ } ], "success": false, - "time": 6, + "time": 4, "cookies": [] }, { @@ -1849,11 +2376,26 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert-decompress", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Accept-Encoding", + "value": "gzip, deflate, br" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1878,7 +2420,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -1888,7 +2430,7 @@ } ], "success": false, - "time": 3, + "time": 15, "cookies": [] }, { @@ -1898,11 +2440,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert-file", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1923,7 +2476,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -1933,7 +2486,7 @@ } ], "success": false, - "time": 4, + "time": 7, "cookies": [] }, { @@ -1943,11 +2496,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert-header-not-found", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -1968,7 +2532,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:11 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -1978,7 +2542,7 @@ } ], "success": false, - "time": 3, + "time": 10, "cookies": [] }, { @@ -1988,11 +2552,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert-header-value", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2013,7 +2588,63 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" + } + ] + }, + "captures": [], + "asserts": [], + "time": 0 + } + ], + "success": false, + "time": 7, + "cookies": [] + }, + { + "filename": "tests/error_assert_http_version.hurl", + "entries": [ + { + "request": { + "method": "GET", + "url": "http://localhost:8000/error-assert/http-version", + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [], + "queryString": [] + }, + "response": { + "httpVersion": "HTTP/1.0", + "status": 200, + "cookies": [], + "headers": [ + { + "name": "Content-Type", + "value": "text/html; charset=utf-8" + }, + { + "name": "Content-Length", + "value": "0" + }, + { + "name": "Server", + "value": "Flask Server" + }, + { + "name": "Date", + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -2026,51 +2657,6 @@ "time": 5, "cookies": [] }, - { - "filename": "tests/error_assert_http_version.hurl", - "entries": [ - { - "request": { - "method": "GET", - "url": "http://localhost:8000/error-assert/http-version", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" - }, - "response": { - "httpVersion": "HTTP/1.0", - "status": 200, - "cookies": [], - "headers": [ - { - "name": "Content-Type", - "value": "text/html; charset=utf-8" - }, - { - "name": "Content-Length", - "value": "0" - }, - { - "name": "Server", - "value": "Flask Server" - }, - { - "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" - } - ] - }, - "captures": [], - "asserts": [], - "time": 0 - } - ], - "success": false, - "time": 3, - "cookies": [] - }, { "filename": "tests/error_assert_invalid_predicate_type.hurl", "entries": [ @@ -2078,11 +2664,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert-invalid-predicate-type", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2103,7 +2700,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -2113,7 +2710,7 @@ } ], "success": false, - "time": 6, + "time": 4, "cookies": [] }, { @@ -2123,11 +2720,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert/match-utf8", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2148,7 +2756,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -2158,7 +2766,7 @@ } ], "success": false, - "time": 4, + "time": 7, "cookies": [] }, { @@ -2168,11 +2776,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert-query-cookie", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2213,7 +2832,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -2223,7 +2842,7 @@ } ], "success": false, - "time": 12, + "time": 7, "cookies": [] }, { @@ -2233,11 +2852,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert-query-invalid-regex", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2258,7 +2888,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -2268,7 +2898,7 @@ } ], "success": false, - "time": 4, + "time": 10, "cookies": [] }, { @@ -2278,11 +2908,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/utf8", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2303,7 +2944,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -2313,7 +2954,7 @@ } ], "success": false, - "time": 6, + "time": 4, "cookies": [] }, { @@ -2323,11 +2964,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/not_found", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2348,7 +3000,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -2358,7 +3010,7 @@ } ], "success": false, - "time": 5, + "time": 6, "cookies": [] }, { @@ -2368,11 +3020,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert-template-variable-not-found", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2393,7 +3056,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -2403,7 +3066,7 @@ } ], "success": false, - "time": 5, + "time": 10, "cookies": [] }, { @@ -2413,11 +3076,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert-value", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2438,7 +3112,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -2448,7 +3122,7 @@ } ], "success": false, - "time": 14, + "time": 13, "cookies": [] }, { @@ -2458,11 +3132,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert-variable", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2483,7 +3168,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -2503,11 +3188,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-assert-xpath", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2528,7 +3224,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:12 GMT" + "value": "Tue, 15 Jun 2021 19:19:34 GMT" } ] }, @@ -2538,7 +3234,7 @@ } ], "success": false, - "time": 5, + "time": 9, "cookies": [] }, { @@ -2558,22 +3254,13 @@ "filename": "tests/error_connect_timeout.hurl", "entries": [ { - "request": { - "method": "GET", - "url": "http://10.0.0.0", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" - }, "captures": [], "asserts": [], "time": 0 } ], "success": false, - "time": 1002, + "time": 1004, "cookies": [] }, { @@ -2593,22 +3280,13 @@ "filename": "tests/error_http_connection.hurl", "entries": [ { - "request": { - "method": "GET", - "url": "http://unknown", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" - }, "captures": [], "asserts": [], "time": 0 } ], "success": false, - "time": 34, + "time": 8117, "cookies": [] }, { @@ -2618,11 +3296,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-invalid-jsonpath", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2643,7 +3332,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:13 GMT" + "value": "Tue, 15 Jun 2021 19:19:44 GMT" } ] }, @@ -2653,22 +3342,13 @@ } ], "success": false, - "time": 12, + "time": 5, "cookies": [] }, { "filename": "tests/error_invalid_url.hurl", "entries": [ { - "request": { - "method": "GET", - "url": "???", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" - }, "captures": [], "asserts": [], "time": 0 @@ -2685,11 +3365,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-invalid-xml", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2710,132 +3401,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:14 GMT" - } - ] - }, - "captures": [], - "asserts": [], - "time": 0 - } - ], - "success": false, - "time": 5, - "cookies": [] - }, - { - "filename": "tests/error_max_redirect.hurl", - "entries": [ - { - "request": { - "method": "GET", - "url": "http://localhost:8000/redirect/7", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" - }, - "captures": [], - "asserts": [], - "time": 0 - } - ], - "success": false, - "time": 46, - "cookies": [] - }, - { - "filename": "tests/error_multipart_form_data.hurl", - "entries": [ - { - "captures": [], - "asserts": [], - "time": 0 - } - ], - "success": false, - "time": 0, - "cookies": [] - }, - { - "filename": "tests/error_predicate.hurl", - "entries": [ - { - "request": { - "method": "GET", - "url": "http://localhost:8000/predicate/error/type", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" - }, - "response": { - "httpVersion": "HTTP/1.0", - "status": 200, - "cookies": [], - "headers": [ - { - "name": "Content-Type", - "value": "text/html; charset=utf-8" - }, - { - "name": "Content-Length", - "value": "74" - }, - { - "name": "Server", - "value": "Flask Server" - }, - { - "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:14 GMT" - } - ] - }, - "captures": [], - "asserts": [], - "time": 0 - } - ], - "success": false, - "time": 21, - "cookies": [] - }, - { - "filename": "tests/error_query_header_not_found.hurl", - "entries": [ - { - "request": { - "method": "GET", - "url": "http://localhost:8000/error-query-header-not-found", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" - }, - "response": { - "httpVersion": "HTTP/1.0", - "status": 200, - "cookies": [], - "headers": [ - { - "name": "Content-Type", - "value": "text/html; charset=utf-8" - }, - { - "name": "Content-Length", - "value": "12" - }, - { - "name": "Server", - "value": "Flask Server" - }, - { - "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:14 GMT" + "value": "Tue, 15 Jun 2021 19:19:44 GMT" } ] }, @@ -2849,17 +3415,110 @@ "cookies": [] }, { - "filename": "tests/error_query_invalid_json.hurl", + "filename": "tests/error_max_redirect.hurl", + "entries": [ + { + "captures": [], + "asserts": [], + "time": 0 + } + ], + "success": false, + "time": 21, + "cookies": [] + }, + { + "filename": "tests/error_multipart_form_data.hurl", + "entries": [ + { + "captures": [], + "asserts": [], + "time": 0 + } + ], + "success": false, + "time": 4, + "cookies": [] + }, + { + "filename": "tests/error_predicate.hurl", "entries": [ { "request": { "method": "GET", - "url": "http://localhost:8000/error-query-invalid-json", - "queryString": [], - "headers": [], + "url": "http://localhost:8000/predicate/error/type", + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] + }, + "response": { + "httpVersion": "HTTP/1.0", + "status": 200, + "cookies": [], + "headers": [ + { + "name": "Content-Type", + "value": "text/html; charset=utf-8" + }, + { + "name": "Content-Length", + "value": "126" + }, + { + "name": "Server", + "value": "Flask Server" + }, + { + "name": "Date", + "value": "Tue, 15 Jun 2021 19:19:44 GMT" + } + ] + }, + "captures": [], + "asserts": [], + "time": 0 + } + ], + "success": false, + "time": 19, + "cookies": [] + }, + { + "filename": "tests/error_query_header_not_found.hurl", + "entries": [ + { + "request": { + "method": "GET", + "url": "http://localhost:8000/error-query-header-not-found", + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [], + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2880,7 +3539,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:14 GMT" + "value": "Tue, 15 Jun 2021 19:19:44 GMT" } ] }, @@ -2890,7 +3549,63 @@ } ], "success": false, - "time": 13, + "time": 5, + "cookies": [] + }, + { + "filename": "tests/error_query_invalid_json.hurl", + "entries": [ + { + "request": { + "method": "GET", + "url": "http://localhost:8000/error-query-invalid-json", + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [], + "queryString": [] + }, + "response": { + "httpVersion": "HTTP/1.0", + "status": 200, + "cookies": [], + "headers": [ + { + "name": "Content-Type", + "value": "text/html; charset=utf-8" + }, + { + "name": "Content-Length", + "value": "12" + }, + { + "name": "Server", + "value": "Flask Server" + }, + { + "name": "Date", + "value": "Tue, 15 Jun 2021 19:19:44 GMT" + } + ] + }, + "captures": [], + "asserts": [], + "time": 0 + } + ], + "success": false, + "time": 4, "cookies": [] }, { @@ -2900,11 +3615,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/error-query-invalid-utf8", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2925,7 +3651,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:14 GMT" + "value": "Tue, 15 Jun 2021 19:19:44 GMT" } ] }, @@ -2935,7 +3661,7 @@ } ], "success": false, - "time": 6, + "time": 7, "cookies": [] }, { @@ -2958,11 +3684,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/get-list", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -2983,7 +3720,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:14 GMT" + "value": "Tue, 15 Jun 2021 19:19:44 GMT" } ] }, @@ -2998,22 +3735,13 @@ } ], "success": false, - "time": 5, + "time": 7, "cookies": [] }, { "filename": "tests/error_timeout.hurl", "entries": [ { - "request": { - "method": "GET", - "url": "http://localhost:8000/timeout", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" - }, "captures": [], "asserts": [], "time": 0 @@ -3030,16 +3758,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/expect", - "queryString": [], "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, { "name": "Expect", "value": "100-continue" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "4" } ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3060,7 +3802,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:15 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3070,7 +3812,7 @@ } ], "success": true, - "time": 6, + "time": 5, "cookies": [] }, { @@ -3080,11 +3822,126 @@ "request": { "method": "GET", "url": "http://localhost:8000/follow-redirect", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] + }, + "response": { + "httpVersion": "HTTP/1.0", + "status": 302, + "cookies": [], + "headers": [ + { + "name": "Content-Type", + "value": "text/html; charset=utf-8" + }, + { + "name": "Content-Length", + "value": "286" + }, + { + "name": "Location", + "value": "http://localhost:8000/following-redirect" + }, + { + "name": "Server", + "value": "Flask Server" + }, + { + "name": "Date", + "value": "Tue, 15 Jun 2021 19:19:46 GMT" + } + ] + }, + "captures": [], + "asserts": [], + "time": 0 + }, + { + "request": { + "method": "GET", + "url": "http://localhost:8000/following-redirect", + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [], + "queryString": [] + }, + "response": { + "httpVersion": "HTTP/1.0", + "status": 302, + "cookies": [], + "headers": [ + { + "name": "Content-Type", + "value": "text/html; charset=utf-8" + }, + { + "name": "Content-Length", + "value": "284" + }, + { + "name": "Location", + "value": "http://localhost:8000/followed-redirect" + }, + { + "name": "Server", + "value": "Flask Server" + }, + { + "name": "Date", + "value": "Tue, 15 Jun 2021 19:19:46 GMT" + } + ] + }, + "captures": [], + "asserts": [], + "time": 0 + }, + { + "request": { + "method": "GET", + "url": "http://localhost:8000/followed-redirect", + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [], + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3105,7 +3962,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:15 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3115,7 +3972,7 @@ } ], "success": true, - "time": 11, + "time": 8, "cookies": [] }, { @@ -3125,29 +3982,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/form-params", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "form": [ + "headers": [ { - "name": "param1", - "value": "value1" + "name": "Host", + "value": "localhost:8000" }, { - "name": "param2", - "value": "" + "name": "Accept", + "value": "*/*" }, { - "name": "param3", - "value": "a=b" + "name": "Content-Type", + "value": "application/x-www-form-urlencoded" }, { - "name": "param4", - "value": "a%3db" + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "49" } ], - "body": "" + "cookies": [], + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3168,7 +4026,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:16 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3180,16 +4038,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/form-params", - "queryString": [], "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, { "name": "Content-Type", "value": "application/x-www-form-urlencoded" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "49" } ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3210,7 +4082,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:16 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3220,7 +4092,7 @@ } ], "success": true, - "time": 15, + "time": 13, "cookies": [] }, { @@ -3230,11 +4102,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/default-headers", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3255,7 +4138,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:16 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3267,20 +4150,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/default-headers", - "queryString": [], "headers": [ - { - "name": "User-Agent", - "value": "hurl/1.0" - }, { "name": "Host", "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.0" } ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3301,7 +4186,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:16 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3313,20 +4198,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/default-headers", - "queryString": [], "headers": [ - { - "name": "User-Agent", - "value": "hurl/1.0" - }, { "name": "Host", "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.0" } ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3347,7 +4234,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:16 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3359,8 +4246,15 @@ "request": { "method": "GET", "url": "http://localhost:8000/custom-headers", - "queryString": [], "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, { "name": "Fruit", "value": "Raspberry" @@ -3380,11 +4274,14 @@ { "name": "Color", "value": "Green" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" } ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3405,7 +4302,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:16 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3417,16 +4314,26 @@ "request": { "method": "GET", "url": "http://localhost:8000/custom-headers-utf8", - "queryString": [], "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, { "name": "Beverage", "value": "café" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" } ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3447,7 +4354,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:16 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3459,16 +4366,26 @@ "request": { "method": "GET", "url": "http://localhost:8000/custom-headers-quote", - "queryString": [], "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, { "name": "Header1", "value": "'" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" } ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3489,7 +4406,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:16 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3501,11 +4418,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/response-headers", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3530,7 +4458,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:16 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3540,7 +4468,7 @@ } ], "success": true, - "time": 28, + "time": 26, "cookies": [] }, { @@ -3550,11 +4478,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/hello", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3575,7 +4514,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:16 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3587,11 +4526,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/hello", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3612,7 +4562,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:16 GMT" + "value": "Tue, 15 Jun 2021 19:19:46 GMT" } ] }, @@ -3622,7 +4572,7 @@ } ], "success": true, - "time": 41, + "time": 7, "cookies": [] }, { @@ -3632,11 +4582,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/include", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3667,7 +4628,7 @@ } ], "success": true, - "time": 13, + "time": 10, "cookies": [] }, { @@ -3677,11 +4638,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/large", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3702,7 +4674,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:18 GMT" + "value": "Tue, 15 Jun 2021 19:19:48 GMT" } ] }, @@ -3712,7 +4684,7 @@ } ], "success": true, - "time": 7459, + "time": 7520, "cookies": [] }, { @@ -3722,11 +4694,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/multipart-form-data", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "616" + }, + { + "name": "Content-Type", + "value": "multipart/form-data; boundary=------------------------8c0af88c4df99c9a" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3747,7 +4738,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:25 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -3757,14 +4748,14 @@ } ], "success": true, - "time": 6, + "time": 11, "cookies": [] }, { "filename": "tests/no_entry.hurl", "entries": [], "success": true, - "time": 1, + "time": 0, "cookies": [] }, { @@ -3774,11 +4765,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/output/endpoint1", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "17" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3811,11 +4821,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/output/endpoint2", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3846,7 +4867,7 @@ } ], "success": true, - "time": 12, + "time": 14, "cookies": [] }, { @@ -3856,12 +4877,15 @@ "request": { "method": "PATCH", "url": "http://localhost:8000/patch/file.txt", - "queryString": [], "headers": [ { "name": "Host", "value": "www.example.com" }, + { + "name": "Accept", + "value": "*/*" + }, { "name": "Content-Type", "value": "application/example" @@ -3869,11 +4893,14 @@ { "name": "If-Match", "value": "\"e0023aa4e\"" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" } ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3898,7 +4925,67 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" + } + ] + }, + "captures": [], + "asserts": [], + "time": 0 + } + ], + "success": true, + "time": 7, + "cookies": [] + }, + { + "filename": "tests/post_base64.hurl", + "entries": [ + { + "request": { + "method": "POST", + "url": "http://localhost:8000/post-base64", + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "12" + } + ], + "cookies": [], + "queryString": [] + }, + "response": { + "httpVersion": "HTTP/1.0", + "status": 200, + "cookies": [], + "headers": [ + { + "name": "Content-Type", + "value": "text/html; charset=utf-8" + }, + { + "name": "Content-Length", + "value": "0" + }, + { + "name": "Server", + "value": "Flask Server" + }, + { + "name": "Date", + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -3911,51 +4998,6 @@ "time": 4, "cookies": [] }, - { - "filename": "tests/post_base64.hurl", - "entries": [ - { - "request": { - "method": "POST", - "url": "http://localhost:8000/post-base64", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" - }, - "response": { - "httpVersion": "HTTP/1.0", - "status": 200, - "cookies": [], - "headers": [ - { - "name": "Content-Type", - "value": "text/html; charset=utf-8" - }, - { - "name": "Content-Length", - "value": "0" - }, - { - "name": "Server", - "value": "Flask Server" - }, - { - "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" - } - ] - }, - "captures": [], - "asserts": [], - "time": 0 - } - ], - "success": true, - "time": 5, - "cookies": [] - }, { "filename": "tests/post_bytes.hurl", "entries": [ @@ -3963,16 +5005,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-bytes", - "queryString": [], "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, { "name": "Content-Type", "value": "application/octet-stream" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "3" } ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -3993,7 +5049,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4013,11 +5069,26 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-file", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "12" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4038,7 +5109,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4058,11 +5129,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-json", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "82" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4083,7 +5173,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4095,11 +5185,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-json-array", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "7" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4120,7 +5229,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4132,11 +5241,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-json-string", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "7" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4157,7 +5285,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4169,11 +5297,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-json-number", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "3" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4194,7 +5341,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4206,11 +5353,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-json-numbers", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "101" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4231,7 +5397,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4243,11 +5409,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-json-boolean", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "4" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4268,7 +5453,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4280,11 +5465,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/get-name", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4305,7 +5501,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4317,11 +5513,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-json", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "82" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4342,7 +5557,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4352,7 +5567,7 @@ } ], "success": true, - "time": 27, + "time": 28, "cookies": [] }, { @@ -4362,11 +5577,26 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-multilines", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "24" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4387,7 +5617,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4399,11 +5629,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/get-bob-age", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4424,7 +5665,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4436,11 +5677,26 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-multilines", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "24" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4461,7 +5717,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4471,7 +5727,7 @@ } ], "success": true, - "time": 9, + "time": 13, "cookies": [] }, { @@ -4481,11 +5737,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-xml", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Content-Type", + "value": "application/xml" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "42" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4506,7 +5781,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4518,11 +5793,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/post-xml-no-prolog", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Content-Type", + "value": "application/xml" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "20" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4543,7 +5837,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4563,11 +5857,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/predicates-string", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4588,7 +5893,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4600,11 +5905,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/predicates-string-empty", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4625,7 +5941,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" } ] }, @@ -4635,7 +5951,7 @@ } ], "success": true, - "time": 9, + "time": 10, "cookies": [] }, { @@ -4645,11 +5961,26 @@ "request": { "method": "GET", "url": "http://localhost:8000/proxy", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Proxy-Connection", + "value": "Keep-Alive" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4670,7 +6001,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:58 GMT" }, { "name": "From-Proxy", @@ -4684,7 +6015,7 @@ } ], "success": true, - "time": 21, + "time": 38, "cookies": [] }, { @@ -4694,11 +6025,22 @@ "request": { "method": "PUT", "url": "http://localhost:8000/put", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4719,7 +6061,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:59 GMT" } ] }, @@ -4729,7 +6071,7 @@ } ], "success": true, - "time": 8, + "time": 4, "cookies": [] }, { @@ -4738,7 +6080,22 @@ { "request": { "method": "GET", - "url": "http://localhost:8000/querystring-params", + "url": "http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3Db¶m4=1%2C2%2C3", + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [], "queryString": [ { "name": "param1", @@ -4756,11 +6113,7 @@ "name": "param4", "value": "1,2,3" } - ], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" + ] }, "response": { "httpVersion": "HTTP/1.0", @@ -4781,7 +6134,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:59 GMT" } ] }, @@ -4793,44 +6146,26 @@ "request": { "method": "GET", "url": "http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3db¶m4=1,2,3", - "queryString": [], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" - }, - "response": { - "httpVersion": "HTTP/1.0", - "status": 200, - "cookies": [], "headers": [ { - "name": "Content-Type", - "value": "text/html; charset=utf-8" + "name": "Host", + "value": "localhost:8000" }, { - "name": "Content-Length", - "value": "0" + "name": "Accept", + "value": "*/*" }, { - "name": "Server", - "value": "Flask Server" - }, - { - "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "name": "User-Agent", + "value": "hurl/1.3.0" } - ] - }, - "captures": [], - "asserts": [], - "time": 0 - }, - { - "request": { - "method": "GET", - "url": "http://localhost:8000/querystring-params?param1=value1", + ], + "cookies": [], "queryString": [ + { + "name": "param1", + "value": "value1" + }, { "name": "param2", "value": "" @@ -4843,11 +6178,7 @@ "name": "param4", "value": "1,2,3" } - ], - "headers": [], - "cookies": [], - "multipartFormData": [], - "body": "" + ] }, "response": { "httpVersion": "HTTP/1.0", @@ -4868,7 +6199,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:59 GMT" } ] }, @@ -4879,17 +6210,40 @@ { "request": { "method": "GET", - "url": "http://localhost:8000/querystring-params-encoded?value1=/&value2=%2F", - "queryString": [ + "url": "http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3Db¶m4=1%2C2%2C3", + "headers": [ { - "name": "value3", - "value": "/" + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" } ], - "headers": [], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [ + { + "name": "param1", + "value": "value1" + }, + { + "name": "param2", + "value": "" + }, + { + "name": "param3", + "value": "a=b" + }, + { + "name": "param4", + "value": "1,2,3" + } + ] }, "response": { "httpVersion": "HTTP/1.0", @@ -4910,7 +6264,68 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:59 GMT" + } + ] + }, + "captures": [], + "asserts": [], + "time": 0 + }, + { + "request": { + "method": "GET", + "url": "http://localhost:8000/querystring-params-encoded?value1=/&value2=%2F&value3=%2F", + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], + "cookies": [], + "queryString": [ + { + "name": "value1", + "value": "/" + }, + { + "name": "value2", + "value": "/" + }, + { + "name": "value3", + "value": "/" + } + ] + }, + "response": { + "httpVersion": "HTTP/1.0", + "status": 200, + "cookies": [], + "headers": [ + { + "name": "Content-Type", + "value": "text/html; charset=utf-8" + }, + { + "name": "Content-Length", + "value": "0" + }, + { + "name": "Server", + "value": "Flask Server" + }, + { + "name": "Date", + "value": "Tue, 15 Jun 2021 19:19:59 GMT" } ] }, @@ -4920,7 +6335,7 @@ } ], "success": true, - "time": 19, + "time": 18, "cookies": [] }, { @@ -4930,11 +6345,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/redirect", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4959,7 +6385,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:59 GMT" } ] }, @@ -4971,11 +6397,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/redirected", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -4996,7 +6433,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:59 GMT" } ] }, @@ -5006,7 +6443,7 @@ } ], "success": true, - "time": 7, + "time": 9, "cookies": [] }, { @@ -5016,11 +6453,26 @@ "request": { "method": "GET", "url": "http://bob:secret@localhost:8000/basic-authentication", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Authorization", + "value": "Basic Ym9iOnNlY3JldA==" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -5041,7 +6493,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:26 GMT" + "value": "Tue, 15 Jun 2021 19:19:59 GMT" } ] }, @@ -5061,11 +6513,22 @@ "request": { "method": "GET", "url": "http://localhost:8000/utf8", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + } + ], "cookies": [], - "multipartFormData": [], - "body": "" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -5086,7 +6549,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:27 GMT" + "value": "Tue, 15 Jun 2021 19:19:59 GMT" } ] }, @@ -5096,7 +6559,7 @@ } ], "success": true, - "time": 9, + "time": 4, "cookies": [] }, { @@ -5106,11 +6569,30 @@ "request": { "method": "POST", "url": "http://localhost:8000/variables", - "queryString": [], - "headers": [], + "headers": [ + { + "name": "Host", + "value": "localhost:8000" + }, + { + "name": "Accept", + "value": "*/*" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "User-Agent", + "value": "hurl/1.3.0" + }, + { + "name": "Content-Length", + "value": "122" + } + ], "cookies": [], - "multipartFormData": [], - "body": "ewogICJuYW1lIjogIkplbm5pZmVyIiwKICAiYWdlIjogMzAsCiAgImhlaWdodCI6IDEuNzAwMDAwMDAwMDAwMDAwMDAwLAogICJmZW1hbGUiOiB0cnVlLAogICJpZCI6ICIxMjMiLAogICJhX251bGwiOiBudWxsCn0=" + "queryString": [] }, "response": { "httpVersion": "HTTP/1.0", @@ -5131,7 +6613,7 @@ }, { "name": "Date", - "value": "Thu, 10 Jun 2021 13:24:27 GMT" + "value": "Tue, 15 Jun 2021 19:19:59 GMT" } ] }, @@ -5149,11 +6631,11 @@ {}, {} ], - "time": 6 + "time": 4 } ], "success": true, - "time": 7, + "time": 5, "cookies": [] } ] \ No newline at end of file diff --git a/packages/hurl/src/http/client.rs b/packages/hurl/src/http/client.rs index 7b2644120..d7b744370 100644 --- a/packages/hurl/src/http/client.rs +++ b/packages/hurl/src/http/client.rs @@ -26,6 +26,7 @@ use std::time::Instant; use super::core::*; use super::options::ClientOptions; use super::request::*; +use super::request_spec::*; use super::response::*; use std::str::FromStr; use url::Url; @@ -51,7 +52,7 @@ pub struct Client { pub redirect_count: usize, // unfortunately, follow-location feature from libcurl can not be used // libcurl returns a single list of headers for the 2 responses - // hurl needs the return the headers only for the second (last) response) + // hurl needs to keep everything } impl Client { @@ -85,15 +86,50 @@ impl Client { /// /// Execute an http request /// - pub fn execute( + pub fn execute_with_redirect( &mut self, - request: &Request, - redirect_count: usize, - ) -> Result { + request: &RequestSpec, + ) -> Result, HttpError> { + let mut calls = vec![]; + + let mut request_spec = request.clone(); + self.redirect_count = 0; + loop { + let (request, response) = self.execute(&request_spec)?; + calls.push((request, response.clone())); + if let Some(url) = self.get_follow_location(response.clone()) { + request_spec = RequestSpec { + method: Method::Get, + url, + headers: vec![], + querystring: vec![], + form: vec![], + multipart: vec![], + cookies: vec![], + body: Body::Binary(vec![]), + content_type: None, + }; + + self.redirect_count += 1; + if let Some(max_redirect) = self.options.max_redirect { + if self.redirect_count > max_redirect { + return Err(HttpError::TooManyRedirect); + } + } + } else { + break; + } + } + Ok(calls) + } + + /// + /// Execute an http request + /// + pub fn execute(&mut self, request: &RequestSpec) -> Result<(Request, Response), HttpError> { // set handle attributes // that have not been set or reset - - self.handle.verbose(self.options.verbose).unwrap(); + self.handle.verbose(true).unwrap(); self.handle.ssl_verify_host(!self.options.insecure).unwrap(); self.handle.ssl_verify_peer(!self.options.insecure).unwrap(); if let Some(proxy) = self.options.proxy.clone() { @@ -107,7 +143,8 @@ impl Client { .connect_timeout(self.options.connect_timeout) .unwrap(); - self.set_url(&request.url, &request.querystring); + let url = self.generate_url(&request.url, &request.querystring); + self.handle.url(url.as_str()).unwrap(); self.set_method(&request.method); self.set_cookies(&request.cookies); @@ -120,23 +157,8 @@ impl Client { self.set_headers(&request); - self.handle - .debug_function(|info_type, data| match info_type { - // return all request headers (not one by one) - easy::InfoType::HeaderOut => { - let lines = split_lines(data); - for line in lines { - eprintln!("> {}", line); - } - } - easy::InfoType::HeaderIn => { - if let Some(s) = decode_header(data) { - eprint!("< {}", s); - } - } - _ => {} - }) - .unwrap(); + let verbose = self.options.verbose; + let mut request_headers: Vec
= vec![]; let start = Instant::now(); let mut status_lines = vec![]; @@ -149,7 +171,35 @@ impl Client { .read_function(|buf| Ok(data.read(buf).unwrap_or(0))) .unwrap(); } + transfer + .debug_function(|info_type, data| match info_type { + // return all request headers (not one by one) + easy::InfoType::HeaderOut => { + let mut lines = split_lines(data); + if verbose { + for line in lines.clone() { + eprintln!("> {}", line); + } + } + lines.pop().unwrap(); + lines.remove(0); // method/url + for line in lines { + if let Some(header) = Header::parse(line) { + request_headers.push(header); + } + } + } + easy::InfoType::HeaderIn => { + if let Some(s) = decode_header(data) { + if verbose { + eprint!("< {}", s); + } + } + } + _ => {} + }) + .unwrap(); transfer .header_function(|h| { if let Some(s) = decode_header(h) { @@ -196,46 +246,28 @@ impl Client { Some(status_line) => self.parse_response_version(status_line.clone())?, }; let headers = self.parse_response_headers(&headers); - - if let Some(url) = self.get_follow_location(headers.clone()) { - let request = Request { - method: Method::Get, - url, - headers: vec![], - querystring: vec![], - form: vec![], - multipart: vec![], - cookies: vec![], - body: Body::Binary(vec![]), - content_type: None, - }; - - let redirect_count = redirect_count + 1; - if let Some(max_redirect) = self.options.max_redirect { - if redirect_count > max_redirect { - return Err(HttpError::TooManyRedirect); - } - } - - return self.execute(&request, redirect_count); - } let duration = start.elapsed(); - self.redirect_count = redirect_count; self.handle.reset(); - Ok(Response { + let request = Request { + url, + method: (&request.method).to_string(), + headers: request_headers, + }; + let response = Response { version, status, headers, body, duration, - }) + }; + Ok((request, response)) } /// - /// set url + /// generate url /// - fn set_url(&mut self, url: &str, params: &[Param]) { + fn generate_url(&mut self, url: &str, params: &[Param]) -> String { let url = if params.is_empty() { url.to_string() } else { @@ -249,7 +281,7 @@ impl Client { let s = self.encode_params(params); format!("{}{}", url, s) }; - self.handle.url(url.as_str()).unwrap(); + url } /// @@ -272,7 +304,7 @@ impl Client { /// /// set request headers /// - fn set_headers(&mut self, request: &Request) { + fn set_headers(&mut self, request: &RequestSpec) { let mut list = easy::List::new(); for header in request.headers.clone() { @@ -426,17 +458,15 @@ impl Client { /// 2. a 3xx response code /// 3. a header Location /// - fn get_follow_location(&mut self, headers: Vec
) -> Option { + fn get_follow_location(&mut self, response: Response) -> Option { if !self.options.follow_location { return None; } - - let response_code = self.handle.response_code().unwrap(); + let response_code = response.status; if !(300..400).contains(&response_code) { return None; } - - let location = match get_header_values(headers, "Location".to_string()).get(0) { + let location = match get_header_values(response.headers, "Location".to_string()).get(0) { None => return None, Some(value) => value.clone(), }; @@ -490,7 +520,7 @@ impl Client { /// /// return curl command-line for the http request run by the client /// - pub fn curl_command_line(&mut self, http_request: &Request) -> String { + pub fn curl_command_line(&mut self, http_request: &RequestSpec) -> String { let mut arguments = vec!["curl".to_string()]; arguments.append(&mut http_request.curl_args(self.options.context_dir.clone())); @@ -514,7 +544,7 @@ impl Client { /// /// return cookies from both cookies from the cookie storage and the request /// -pub fn all_cookies(cookie_storage: Vec, request: &Request) -> Vec { +pub fn all_cookies(cookie_storage: Vec, request: &RequestSpec) -> Vec { let mut cookies = request.cookies.clone(); cookies.append( &mut cookie_storage diff --git a/packages/hurl/src/http/core.rs b/packages/hurl/src/http/core.rs index 874266b49..d5a88f8cf 100644 --- a/packages/hurl/src/http/core.rs +++ b/packages/hurl/src/http/core.rs @@ -37,6 +37,18 @@ pub struct Cookie { pub http_only: bool, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RequestCookie { + pub name: String, + pub value: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Param { + pub name: String, + pub value: String, +} + impl fmt::Display for Header { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}: {}", self.name, self.value) @@ -60,6 +72,18 @@ impl fmt::Display for Cookie { } } +impl fmt::Display for RequestCookie { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}={}", self.name, self.value) + } +} + +impl fmt::Display for Param { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}: {}", self.name, self.value) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct ParseCookieError {} diff --git a/packages/hurl/src/http/mod.rs b/packages/hurl/src/http/mod.rs index 39e0811b9..faa308c1d 100644 --- a/packages/hurl/src/http/mod.rs +++ b/packages/hurl/src/http/mod.rs @@ -17,11 +17,12 @@ */ pub use self::client::{Client, HttpError}; -pub use self::core::{Cookie, Header}; +pub use self::core::{Cookie, Header, Param, RequestCookie}; pub use self::options::ClientOptions; +pub use self::request::Request; #[cfg(test)] -pub use self::request::tests::*; -pub use self::request::{Body, FileParam, Method, MultipartParam, Param, Request, RequestCookie}; +pub use self::request_spec::tests::*; +pub use self::request_spec::{Body, FileParam, Method, MultipartParam, RequestSpec}; #[cfg(test)] pub use self::response::tests::*; pub use self::response::{Response, Version}; @@ -30,4 +31,5 @@ mod client; mod core; mod options; mod request; +mod request_spec; mod response; diff --git a/packages/hurl/src/http/request.rs b/packages/hurl/src/http/request.rs index c92cb6aa3..7748e9ba3 100644 --- a/packages/hurl/src/http/request.rs +++ b/packages/hurl/src/http/request.rs @@ -16,564 +16,170 @@ * */ -use core::fmt; - use super::core::*; +use url::Url; #[derive(Clone, Debug, PartialEq, Eq)] pub struct Request { - pub method: Method, pub url: String, + pub method: String, pub headers: Vec
, - pub querystring: Vec, - pub form: Vec, - pub multipart: Vec, - pub cookies: Vec, - pub body: Body, - pub content_type: Option, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Method { - Get, - Head, - Post, - Put, - Delete, - Connect, - Options, - Trace, - Patch, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Param { - pub name: String, - pub value: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum MultipartParam { - Param(Param), - FileParam(FileParam), -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct FileParam { - pub name: String, - pub filename: String, - pub data: Vec, - pub content_type: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct RequestCookie { - pub name: String, - pub value: String, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Body { - Text(String), - Binary(Vec), - File(Vec, String), -} - -impl Body { - pub fn bytes(&self) -> Vec { - match self { - Body::Text(s) => s.as_bytes().to_vec(), - Body::Binary(bs) => bs.clone(), - Body::File(bs, _) => bs.clone(), - } - } -} - -impl fmt::Display for Method { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let value = match self { - Method::Get => "GET", - Method::Head => "HEAD", - Method::Post => "POST", - Method::Put => "PUT", - Method::Delete => "DELETE", - Method::Connect => "CONNECT", - Method::Options => "OPTIONS", - Method::Trace => "TRACE", - Method::Patch => "PATCH", - }; - write!(f, "{}", value) - } -} - -impl fmt::Display for Param { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}: {}", self.name, self.value) - } -} - -impl fmt::Display for MultipartParam { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - MultipartParam::Param(param) => write!(f, "{}", param.to_string()), - MultipartParam::FileParam(param) => write!(f, "{}", param.to_string()), - } - } -} - -impl fmt::Display for FileParam { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}: file,{}; {}", - self.name, self.filename, self.content_type - ) - } -} - -impl fmt::Display for RequestCookie { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}={}", self.name, self.value) - } } impl Request { - /// - /// return request as curl arguments - /// It does not contain the requests cookies (they will be accessed from the client) - /// - pub fn curl_args(&self, context_dir: String) -> Vec { - let querystring = if self.querystring.is_empty() { - "".to_string() - } else { - let params = self - .querystring - .iter() - .map(|p| p.curl_arg_escape()) - .collect::>(); - params.join("&") - }; - let url = if querystring.as_str() == "" { - self.url.to_string() - } else if self.url.to_string().contains('?') { - format!("{}&{}", self.url.to_string(), querystring) - } else { - format!("{}?{}", self.url.to_string(), querystring) - }; - let mut arguments = vec![format!("'{}'", url)]; - - let data = - !self.multipart.is_empty() || !self.form.is_empty() || !self.body.bytes().is_empty(); - arguments.append(&mut self.method.curl_args(data)); - - for header in self.headers.clone() { - arguments.append(&mut header.curl_args()); + pub fn query_string_params(self) -> Vec { + let u = Url::parse(self.url.as_str()).expect("valid url"); + let mut params = vec![]; + for (name, value) in u.query_pairs() { + let param = Param { + name: name.to_string(), + value: value.to_string(), + }; + params.push(param); } + params + } - let has_explicit_content_type = self - .headers + pub fn cookies(self) -> Vec { + self.headers .iter() - .map(|h| h.name.clone()) - .any(|n| n.as_str() == "Content-Type"); - if !has_explicit_content_type { - if let Some(content_type) = self.content_type.clone() { - if content_type.as_str() != "application/x-www-form-urlencoded" - && content_type.as_str() != "multipart/form-data" - { - arguments.push("-H".to_string()); - arguments.push(format!("'Content-Type: {}'", content_type)); - } - } else if !self.body.bytes().is_empty() { - match self.body.clone() { - Body::Text(_) => { - arguments.push("-H".to_string()); - arguments.push("'Content-Type:'".to_string()) - } - Body::Binary(_) => { - arguments.push("-H".to_string()); - arguments.push("'Content-Type: application/octet-stream'".to_string()) - } - Body::File(_, _) => { - arguments.push("-H".to_string()); - arguments.push("'Content-Type:'".to_string()) - } - } - } - } - - for param in self.form.clone() { - arguments.push("--data".to_string()); - arguments.push(format!("'{}'", param.curl_arg_escape())); - } - for param in self.multipart.clone() { - arguments.push("-F".to_string()); - arguments.push(format!("'{}'", param.curl_arg(context_dir.clone()))); - } - - if !self.body.bytes().is_empty() { - arguments.push("--data".to_string()); - match self.body.clone() { - Body::Text(s) => { - let prefix = if s.contains('\n') { "$" } else { "" }; - arguments.push(format!("{}'{}'", prefix, s.replace("\n", "\\n"))) - } - Body::Binary(bytes) => arguments.push(format!("$'{}'", encode_bytes(bytes))), - Body::File(_, filename) => { - let prefix = if context_dir.as_str() == "." { - "".to_string() - } else { - format!("{}/", context_dir) - }; - arguments.push(format!("'@{}{}'", prefix, filename)) - } - } - } - - arguments + .filter(|h| h.name.as_str() == "Cookie") + .flat_map(|h| parse_cookies(h.value.as_str().trim())) + .collect() } } -fn encode_byte(b: u8) -> String { - format!("\\x{:02x}", b) +fn parse_cookies(s: &str) -> Vec { + s.split(';').map(|t| parse_cookie(t.trim())).collect() } -fn encode_bytes(b: Vec) -> String { - b.iter().map(|b| encode_byte(*b)).collect() -} - -impl Method { - pub fn curl_args(&self, data: bool) -> Vec { - match self { - Method::Get => { - if data { - vec!["-X".to_string(), "GET".to_string()] - } else { - vec![] - } - } - Method::Head => vec!["-X".to_string(), "HEAD".to_string()], - Method::Post => { - if data { - vec![] - } else { - vec!["-X".to_string(), "POST".to_string()] - } - } - Method::Put => vec!["-X".to_string(), "PUT".to_string()], - Method::Delete => vec!["-X".to_string(), "DELETE".to_string()], - Method::Connect => vec!["-X".to_string(), "CONNECT".to_string()], - Method::Options => vec!["-X".to_string(), "OPTIONS".to_string()], - Method::Trace => vec!["-X".to_string(), "TRACE".to_string()], - Method::Patch => vec!["-X".to_string(), "PATCH".to_string()], - } - } -} - -impl Header { - pub fn curl_args(&self) -> Vec { - let name = self.name.clone(); - let value = self.value.clone(); - vec![ - "-H".to_string(), - encode_value(format!("{}: {}", name, value)), - ] - } -} - -impl Param { - pub fn curl_arg_escape(&self) -> String { - let name = self.name.clone(); - let value = escape_url(self.value.clone()); - format!("{}={}", name, value) - } - - pub fn curl_arg(&self) -> String { - let name = self.name.clone(); - let value = self.value.clone(); - format!("{}={}", name, value) - } -} - -impl MultipartParam { - pub fn curl_arg(&self, context_dir: String) -> String { - match self { - MultipartParam::Param(param) => param.curl_arg(), - MultipartParam::FileParam(FileParam { - name, - filename, - content_type, - .. - }) => { - let prefix = if context_dir.as_str() == "." { - "".to_string() - } else { - format!("{}/", context_dir) - }; - let value = format!("@{}{};type={}", prefix, filename, content_type); - format!("{}={}", name, value) - } - } - } -} - -fn escape_single_quote(s: String) -> String { - s.chars() - .map(|c| { - if c == '\'' { - "\\'".to_string() - } else { - c.to_string() - } - }) - .collect::>() - .join("") -} - -fn escape_url(s: String) -> String { - percent_encoding::percent_encode(s.as_bytes(), percent_encoding::NON_ALPHANUMERIC).to_string() -} - -// special encoding for the shell -// $'...' -fn encode_value(s: String) -> String { - if s.contains('\'') { - let s = escape_single_quote(s); - format!("$'{}'", s) - } else { - format!("'{}'", s) +fn parse_cookie(s: &str) -> RequestCookie { + match s.find('=') { + Some(i) => RequestCookie { + name: s.split_at(i).0.to_string(), + value: s.split_at(i + 1).1.to_string(), + }, + None => RequestCookie { + name: s.to_string(), + value: "".to_string(), + }, } } #[cfg(test)] pub mod tests { use super::*; + use crate::http::RequestCookie; - pub fn hello_http_request() -> Request { + pub fn hello_request() -> Request { Request { - method: Method::Get, + method: "GET".to_string(), url: "http://localhost:8000/hello".to_string(), - querystring: vec![], - headers: vec![], - cookies: vec![], - body: Body::Binary(vec![]), - multipart: vec![], - form: vec![], - content_type: None, - } - } - - pub fn custom_http_request() -> Request { - Request { - method: Method::Get, - url: "http://localhost/custom".to_string(), - querystring: vec![], headers: vec![ Header { - name: String::from("User-Agent"), - value: String::from("iPhone"), + name: "Host".to_string(), + value: "localhost:8000".to_string(), }, Header { - name: String::from("Foo"), - value: String::from("Bar"), + name: "Accept".to_string(), + value: "*/*".to_string(), + }, + Header { + name: "User-Agent".to_string(), + value: "hurl/1.0".to_string(), }, ], - cookies: vec![ - RequestCookie { - name: String::from("theme"), - value: String::from("light"), - }, - RequestCookie { - name: String::from("sessionToken"), - value: String::from("abc123"), - }, - ], - body: Body::Binary(vec![]), - multipart: vec![], - form: vec![], - content_type: None, } } - pub fn query_http_request() -> Request { + pub fn query_string_request() -> Request { Request { - method: Method::Get, - url: "http://localhost:8000/querystring-params".to_string(), - querystring: vec![ - Param { - name: String::from("param1"), - value: String::from("value1"), - }, - Param { - name: String::from("param2"), - value: String::from("a b"), - }, - ], + method: "GET".to_string(), + url: "http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3Db¶m4=1%2C2%2C3".to_string(), headers: vec![], - cookies: vec![], - body: Body::Binary(vec![]), - multipart: vec![], - form: vec![], - content_type: None, } } - pub fn form_http_request() -> Request { + pub fn cookies_request() -> Request { Request { - method: Method::Post, - url: "http://localhost/form-params".to_string(), - querystring: vec![], + method: "GET".to_string(), + url: "http://localhost:8000/cookies".to_string(), headers: vec![Header { - name: String::from("Content-Type"), - value: String::from("application/x-www-form-urlencoded"), + name: "Cookie".to_string(), + value: "cookie1=value1; cookie2=value2".to_string(), }], - cookies: vec![], - body: Body::Binary(vec![]), - multipart: vec![], - form: vec![ - Param { - name: String::from("param1"), - value: String::from("value1"), - }, - Param { - name: String::from("param2"), - value: String::from("a b"), - }, - ], - content_type: Some("multipart/form-data".to_string()), } } #[test] - fn test_encode_byte() { - assert_eq!(encode_byte(1), "\\x01".to_string()); - assert_eq!(encode_byte(32), "\\x20".to_string()); + fn test_query_string() { + assert!(hello_request().query_string_params().is_empty()); + assert_eq!( + query_string_request().query_string_params(), + vec![ + Param { + name: "param1".to_string(), + value: "value1".to_string() + }, + Param { + name: "param2".to_string(), + value: "".to_string() + }, + Param { + name: "param3".to_string(), + value: "a=b".to_string() + }, + Param { + name: "param4".to_string(), + value: "1,2,3".to_string() + } + ] + ) } #[test] - fn method_curl_args() { - assert!(Method::Get.curl_args(false).is_empty()); + fn test_cookies() { + assert!(hello_request().cookies().is_empty()); assert_eq!( - Method::Get.curl_args(true), - vec!["-X".to_string(), "GET".to_string()] - ); - - assert_eq!( - Method::Post.curl_args(false), - vec!["-X".to_string(), "POST".to_string()] - ); - assert!(Method::Post.curl_args(true).is_empty()); - - assert_eq!( - Method::Put.curl_args(false), - vec!["-X".to_string(), "PUT".to_string()] - ); - assert_eq!( - Method::Put.curl_args(true), - vec!["-X".to_string(), "PUT".to_string()] - ); + cookies_request().cookies(), + vec![ + RequestCookie { + name: "cookie1".to_string(), + value: "value1".to_string() + }, + RequestCookie { + name: "cookie2".to_string(), + value: "value2".to_string() + } + ] + ) } #[test] - fn header_curl_args() { + fn test_parse_cookies() { assert_eq!( - Header { - name: "Host".to_string(), - value: "example.com".to_string() - } - .curl_args(), - vec!["-H".to_string(), "'Host: example.com'".to_string()] - ); - assert_eq!( - Header { - name: "If-Match".to_string(), - value: "\"e0023aa4e\"".to_string() - } - .curl_args(), - vec!["-H".to_string(), "'If-Match: \"e0023aa4e\"'".to_string()] - ); + parse_cookies("cookie1=value1; cookie2=value2"), + vec![ + RequestCookie { + name: "cookie1".to_string(), + value: "value1".to_string() + }, + RequestCookie { + name: "cookie2".to_string(), + value: "value2".to_string() + } + ] + ) } #[test] - fn param_curl_args() { + fn test_parse_cookie() { assert_eq!( - Param { - name: "param1".to_string(), + parse_cookie("cookie1=value1"), + RequestCookie { + name: "cookie1".to_string(), value: "value1".to_string() - } - .curl_arg(), - "param1=value1".to_string() - ); - assert_eq!( - Param { - name: "param2".to_string(), - value: "".to_string() - } - .curl_arg(), - "param2=".to_string() - ); - assert_eq!( - Param { - name: "param3".to_string(), - value: "a=b".to_string() - } - .curl_arg_escape(), - "param3=a%3Db".to_string() - ); - assert_eq!( - Param { - name: "param4".to_string(), - value: "1,2,3".to_string() - } - .curl_arg_escape(), - "param4=1%2C2%2C3".to_string() - ); - } - - #[test] - fn requests_curl_args() { - assert_eq!( - hello_http_request().curl_args(".".to_string()), - vec!["'http://localhost:8000/hello'".to_string()] - ); - assert_eq!( - custom_http_request().curl_args(".".to_string()), - vec![ - "'http://localhost/custom'".to_string(), - "-H".to_string(), - "'User-Agent: iPhone'".to_string(), - "-H".to_string(), - "'Foo: Bar'".to_string(), - ] - ); - assert_eq!( - query_http_request().curl_args(".".to_string()), - vec![ - "'http://localhost:8000/querystring-params?param1=value1¶m2=a%20b'".to_string() - ] - ); - assert_eq!( - form_http_request().curl_args(".".to_string()), - vec![ - "'http://localhost/form-params'".to_string(), - "-H".to_string(), - "'Content-Type: application/x-www-form-urlencoded'".to_string(), - "--data".to_string(), - "'param1=value1'".to_string(), - "--data".to_string(), - "'param2=a%20b'".to_string(), - ] - ); - } - - #[test] - fn test_encode_value() { - assert_eq!( - encode_value("Header1: x".to_string()), - "'Header1: x'".to_string() - ); - assert_eq!( - encode_value("Header1: '".to_string()), - "$'Header1: \\''".to_string() - ); + }, + ) } } diff --git a/packages/hurl/src/http/request_spec.rs b/packages/hurl/src/http/request_spec.rs new file mode 100644 index 000000000..8b194cc9d --- /dev/null +++ b/packages/hurl/src/http/request_spec.rs @@ -0,0 +1,555 @@ +/* + * hurl (https://hurl.dev) + * Copyright (C) 2020 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +use core::fmt; + +use super::core::*; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RequestSpec { + pub method: Method, + pub url: String, + pub headers: Vec
, + pub querystring: Vec, + pub form: Vec, + pub multipart: Vec, + pub cookies: Vec, + pub body: Body, + pub content_type: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Method { + Get, + Head, + Post, + Put, + Delete, + Connect, + Options, + Trace, + Patch, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum MultipartParam { + Param(Param), + FileParam(FileParam), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FileParam { + pub name: String, + pub filename: String, + pub data: Vec, + pub content_type: String, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Body { + Text(String), + Binary(Vec), + File(Vec, String), +} + +impl Body { + pub fn bytes(&self) -> Vec { + match self { + Body::Text(s) => s.as_bytes().to_vec(), + Body::Binary(bs) => bs.clone(), + Body::File(bs, _) => bs.clone(), + } + } +} + +impl fmt::Display for Method { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let value = match self { + Method::Get => "GET", + Method::Head => "HEAD", + Method::Post => "POST", + Method::Put => "PUT", + Method::Delete => "DELETE", + Method::Connect => "CONNECT", + Method::Options => "OPTIONS", + Method::Trace => "TRACE", + Method::Patch => "PATCH", + }; + write!(f, "{}", value) + } +} + +impl fmt::Display for MultipartParam { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + MultipartParam::Param(param) => write!(f, "{}", param.to_string()), + MultipartParam::FileParam(param) => write!(f, "{}", param.to_string()), + } + } +} + +impl fmt::Display for FileParam { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}: file,{}; {}", + self.name, self.filename, self.content_type + ) + } +} + +impl RequestSpec { + /// + /// return request as curl arguments + /// It does not contain the requests cookies (they will be accessed from the client) + /// + pub fn curl_args(&self, context_dir: String) -> Vec { + let querystring = if self.querystring.is_empty() { + "".to_string() + } else { + let params = self + .querystring + .iter() + .map(|p| p.curl_arg_escape()) + .collect::>(); + params.join("&") + }; + let url = if querystring.as_str() == "" { + self.url.to_string() + } else if self.url.to_string().contains('?') { + format!("{}&{}", self.url.to_string(), querystring) + } else { + format!("{}?{}", self.url.to_string(), querystring) + }; + let mut arguments = vec![format!("'{}'", url)]; + + let data = + !self.multipart.is_empty() || !self.form.is_empty() || !self.body.bytes().is_empty(); + arguments.append(&mut self.method.curl_args(data)); + + for header in self.headers.clone() { + arguments.append(&mut header.curl_args()); + } + + let has_explicit_content_type = self + .headers + .iter() + .map(|h| h.name.clone()) + .any(|n| n.as_str() == "Content-Type"); + if !has_explicit_content_type { + if let Some(content_type) = self.content_type.clone() { + if content_type.as_str() != "application/x-www-form-urlencoded" + && content_type.as_str() != "multipart/form-data" + { + arguments.push("-H".to_string()); + arguments.push(format!("'Content-Type: {}'", content_type)); + } + } else if !self.body.bytes().is_empty() { + match self.body.clone() { + Body::Text(_) => { + arguments.push("-H".to_string()); + arguments.push("'Content-Type:'".to_string()) + } + Body::Binary(_) => { + arguments.push("-H".to_string()); + arguments.push("'Content-Type: application/octet-stream'".to_string()) + } + Body::File(_, _) => { + arguments.push("-H".to_string()); + arguments.push("'Content-Type:'".to_string()) + } + } + } + } + + for param in self.form.clone() { + arguments.push("--data".to_string()); + arguments.push(format!("'{}'", param.curl_arg_escape())); + } + for param in self.multipart.clone() { + arguments.push("-F".to_string()); + arguments.push(format!("'{}'", param.curl_arg(context_dir.clone()))); + } + + if !self.body.bytes().is_empty() { + arguments.push("--data".to_string()); + match self.body.clone() { + Body::Text(s) => { + let prefix = if s.contains('\n') { "$" } else { "" }; + arguments.push(format!("{}'{}'", prefix, s.replace("\n", "\\n"))) + } + Body::Binary(bytes) => arguments.push(format!("$'{}'", encode_bytes(bytes))), + Body::File(_, filename) => { + let prefix = if context_dir.as_str() == "." { + "".to_string() + } else { + format!("{}/", context_dir) + }; + arguments.push(format!("'@{}{}'", prefix, filename)) + } + } + } + + arguments + } +} + +fn encode_byte(b: u8) -> String { + format!("\\x{:02x}", b) +} + +fn encode_bytes(b: Vec) -> String { + b.iter().map(|b| encode_byte(*b)).collect() +} + +impl Method { + pub fn curl_args(&self, data: bool) -> Vec { + match self { + Method::Get => { + if data { + vec!["-X".to_string(), "GET".to_string()] + } else { + vec![] + } + } + Method::Head => vec!["-X".to_string(), "HEAD".to_string()], + Method::Post => { + if data { + vec![] + } else { + vec!["-X".to_string(), "POST".to_string()] + } + } + Method::Put => vec!["-X".to_string(), "PUT".to_string()], + Method::Delete => vec!["-X".to_string(), "DELETE".to_string()], + Method::Connect => vec!["-X".to_string(), "CONNECT".to_string()], + Method::Options => vec!["-X".to_string(), "OPTIONS".to_string()], + Method::Trace => vec!["-X".to_string(), "TRACE".to_string()], + Method::Patch => vec!["-X".to_string(), "PATCH".to_string()], + } + } +} + +impl Header { + pub fn curl_args(&self) -> Vec { + let name = self.name.clone(); + let value = self.value.clone(); + vec![ + "-H".to_string(), + encode_value(format!("{}: {}", name, value)), + ] + } +} + +impl Param { + pub fn curl_arg_escape(&self) -> String { + let name = self.name.clone(); + let value = escape_url(self.value.clone()); + format!("{}={}", name, value) + } + + pub fn curl_arg(&self) -> String { + let name = self.name.clone(); + let value = self.value.clone(); + format!("{}={}", name, value) + } +} + +impl MultipartParam { + pub fn curl_arg(&self, context_dir: String) -> String { + match self { + MultipartParam::Param(param) => param.curl_arg(), + MultipartParam::FileParam(FileParam { + name, + filename, + content_type, + .. + }) => { + let prefix = if context_dir.as_str() == "." { + "".to_string() + } else { + format!("{}/", context_dir) + }; + let value = format!("@{}{};type={}", prefix, filename, content_type); + format!("{}={}", name, value) + } + } + } +} + +fn escape_single_quote(s: String) -> String { + s.chars() + .map(|c| { + if c == '\'' { + "\\'".to_string() + } else { + c.to_string() + } + }) + .collect::>() + .join("") +} + +fn escape_url(s: String) -> String { + percent_encoding::percent_encode(s.as_bytes(), percent_encoding::NON_ALPHANUMERIC).to_string() +} + +// special encoding for the shell +// $'...' +fn encode_value(s: String) -> String { + if s.contains('\'') { + let s = escape_single_quote(s); + format!("$'{}'", s) + } else { + format!("'{}'", s) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + pub fn hello_http_request() -> RequestSpec { + RequestSpec { + method: Method::Get, + url: "http://localhost:8000/hello".to_string(), + querystring: vec![], + headers: vec![], + cookies: vec![], + body: Body::Binary(vec![]), + multipart: vec![], + form: vec![], + content_type: None, + } + } + + pub fn custom_http_request() -> RequestSpec { + RequestSpec { + method: Method::Get, + url: "http://localhost/custom".to_string(), + querystring: vec![], + headers: vec![ + Header { + name: String::from("User-Agent"), + value: String::from("iPhone"), + }, + Header { + name: String::from("Foo"), + value: String::from("Bar"), + }, + ], + cookies: vec![ + RequestCookie { + name: String::from("theme"), + value: String::from("light"), + }, + RequestCookie { + name: String::from("sessionToken"), + value: String::from("abc123"), + }, + ], + body: Body::Binary(vec![]), + multipart: vec![], + form: vec![], + content_type: None, + } + } + + pub fn query_http_request() -> RequestSpec { + RequestSpec { + method: Method::Get, + url: "http://localhost:8000/querystring-params".to_string(), + querystring: vec![ + Param { + name: String::from("param1"), + value: String::from("value1"), + }, + Param { + name: String::from("param2"), + value: String::from("a b"), + }, + ], + headers: vec![], + cookies: vec![], + body: Body::Binary(vec![]), + multipart: vec![], + form: vec![], + content_type: None, + } + } + + pub fn form_http_request() -> RequestSpec { + RequestSpec { + method: Method::Post, + url: "http://localhost/form-params".to_string(), + querystring: vec![], + headers: vec![Header { + name: String::from("Content-Type"), + value: String::from("application/x-www-form-urlencoded"), + }], + cookies: vec![], + body: Body::Binary(vec![]), + multipart: vec![], + form: vec![ + Param { + name: String::from("param1"), + value: String::from("value1"), + }, + Param { + name: String::from("param2"), + value: String::from("a b"), + }, + ], + content_type: Some("multipart/form-data".to_string()), + } + } + + #[test] + fn test_encode_byte() { + assert_eq!(encode_byte(1), "\\x01".to_string()); + assert_eq!(encode_byte(32), "\\x20".to_string()); + } + + #[test] + fn method_curl_args() { + assert!(Method::Get.curl_args(false).is_empty()); + assert_eq!( + Method::Get.curl_args(true), + vec!["-X".to_string(), "GET".to_string()] + ); + + assert_eq!( + Method::Post.curl_args(false), + vec!["-X".to_string(), "POST".to_string()] + ); + assert!(Method::Post.curl_args(true).is_empty()); + + assert_eq!( + Method::Put.curl_args(false), + vec!["-X".to_string(), "PUT".to_string()] + ); + assert_eq!( + Method::Put.curl_args(true), + vec!["-X".to_string(), "PUT".to_string()] + ); + } + + #[test] + fn header_curl_args() { + assert_eq!( + Header { + name: "Host".to_string(), + value: "example.com".to_string() + } + .curl_args(), + vec!["-H".to_string(), "'Host: example.com'".to_string()] + ); + assert_eq!( + Header { + name: "If-Match".to_string(), + value: "\"e0023aa4e\"".to_string() + } + .curl_args(), + vec!["-H".to_string(), "'If-Match: \"e0023aa4e\"'".to_string()] + ); + } + + #[test] + fn param_curl_args() { + assert_eq!( + Param { + name: "param1".to_string(), + value: "value1".to_string() + } + .curl_arg(), + "param1=value1".to_string() + ); + assert_eq!( + Param { + name: "param2".to_string(), + value: "".to_string() + } + .curl_arg(), + "param2=".to_string() + ); + assert_eq!( + Param { + name: "param3".to_string(), + value: "a=b".to_string() + } + .curl_arg_escape(), + "param3=a%3Db".to_string() + ); + assert_eq!( + Param { + name: "param4".to_string(), + value: "1,2,3".to_string() + } + .curl_arg_escape(), + "param4=1%2C2%2C3".to_string() + ); + } + + #[test] + fn requests_curl_args() { + assert_eq!( + hello_http_request().curl_args(".".to_string()), + vec!["'http://localhost:8000/hello'".to_string()] + ); + assert_eq!( + custom_http_request().curl_args(".".to_string()), + vec![ + "'http://localhost/custom'".to_string(), + "-H".to_string(), + "'User-Agent: iPhone'".to_string(), + "-H".to_string(), + "'Foo: Bar'".to_string(), + ] + ); + assert_eq!( + query_http_request().curl_args(".".to_string()), + vec![ + "'http://localhost:8000/querystring-params?param1=value1¶m2=a%20b'".to_string() + ] + ); + assert_eq!( + form_http_request().curl_args(".".to_string()), + vec![ + "'http://localhost/form-params'".to_string(), + "-H".to_string(), + "'Content-Type: application/x-www-form-urlencoded'".to_string(), + "--data".to_string(), + "'param1=value1'".to_string(), + "--data".to_string(), + "'param2=a%20b'".to_string(), + ] + ); + } + + #[test] + fn test_encode_value() { + assert_eq!( + encode_value("Header1: x".to_string()), + "'Header1: x'".to_string() + ); + assert_eq!( + encode_value("Header1: '".to_string()), + "$'Header1: \\''".to_string() + ); + } +} diff --git a/packages/hurl/src/runner/entry.rs b/packages/hurl/src/runner/entry.rs index 81d7fc5f4..f90048c77 100644 --- a/packages/hurl/src/runner/entry.rs +++ b/packages/hurl/src/runner/entry.rs @@ -29,6 +29,7 @@ use super::value::Value; use crate::runner::request::{cookie_storage_clear, cookie_storage_set}; /// Run an entry with the hurl http client +/// Return one or more EntryResults (if following redirect) /// /// # Examples /// @@ -54,18 +55,18 @@ pub fn run( context_dir: String, log_verbose: &impl Fn(&str), log_error_message: &impl Fn(bool, &str), -) -> EntryResult { +) -> Vec { let http_request = match eval_request(entry.request.clone(), variables, context_dir.clone()) { Ok(r) => r, Err(error) => { - return EntryResult { + return vec![EntryResult { request: None, response: None, captures: vec![], asserts: vec![], errors: vec![error], time_in_ms: 0, - }; + }]; } }; @@ -106,8 +107,8 @@ pub fn run( .as_str(), ); - let http_response = match http_client.execute(&http_request, 0) { - Ok(response) => response, + let calls = match http_client.execute_with_redirect(&http_request) { + Ok(calls) => calls, Err(http_error) => { let runner_error = match http_error { HttpError::CouldNotResolveProxyName => RunnerError::CouldNotResolveProxyName, @@ -129,87 +130,100 @@ pub fn run( url: http_request.url.clone(), }, }; - return EntryResult { - request: Some(http_request.clone()), + return vec![EntryResult { + request: None, response: None, captures: vec![], asserts: vec![], errors: vec![Error { source_info: SourceInfo { - start: entry.clone().request.url.source_info.start, - end: entry.clone().request.url.source_info.end, + start: entry.request.url.source_info.start, + end: entry.request.url.source_info.end, }, inner: runner_error, assert: false, }], time_in_ms: 0, + }]; + } + }; + + let mut entry_results = vec![]; + for (i, (http_request, http_response)) in calls.iter().enumerate() { + let mut captures = vec![]; + let mut asserts = vec![]; + let mut errors = vec![]; + let time_in_ms = http_response.duration.as_millis(); + + // Last call + if i == calls.len() - 1 { + captures = match entry.response.clone() { + None => vec![], + Some(response) => match eval_captures(response, http_response, variables) { + Ok(captures) => captures, + Err(e) => { + return vec![EntryResult { + request: Some(http_request.clone()), + response: Some(http_response.clone()), + captures: vec![], + asserts: vec![], + errors: vec![e], + time_in_ms, + }]; + } + }, }; - } - }; - - let time_in_ms = http_response.duration.as_millis(); - log_verbose(format!("Response Time: {}ms", time_in_ms.to_string()).as_str()); - - let captures = match entry.response.clone() { - None => vec![], - Some(response) => match eval_captures(response, http_response.clone(), variables) { - Ok(captures) => captures, - Err(e) => { - return EntryResult { - request: Some(http_request), - response: Some(http_response), - captures: vec![], - asserts: vec![], - errors: vec![e], - time_in_ms, - }; + // update variables now! + for capture_result in captures.clone() { + variables.insert(capture_result.name, capture_result.value); } - }, - }; + asserts = match entry.response.clone() { + None => vec![], + Some(response) => eval_asserts( + response, + variables, + http_response.clone(), + context_dir.clone(), + ), + }; + errors = asserts + .iter() + .filter_map(|assert| assert.clone().error()) + .map( + |Error { + source_info, inner, .. + }| Error { + source_info, + inner, + assert: true, + }, + ) + .collect(); - // update variables now! - for capture_result in captures.clone() { - variables.insert(capture_result.name, capture_result.value); - } - - let asserts = match entry.response { - None => vec![], - Some(response) => eval_asserts(response, variables, http_response.clone(), context_dir), - }; - let errors = asserts - .iter() - .filter_map(|assert| assert.clone().error()) - .map( - |Error { - source_info, inner, .. - }| Error { - source_info, - inner, - assert: true, - }, - ) - .collect(); - - if !captures.is_empty() { - log_verbose("Captures"); - for capture in captures.clone() { - log_verbose(format!("{}: {}", capture.name, capture.value).as_str()); + if !captures.is_empty() { + log_verbose("Captures"); + for capture in captures.clone() { + log_verbose(format!("{}: {}", capture.name, capture.value).as_str()); + } + } + log_verbose(""); } + + let entry_result = EntryResult { + request: Some(http_request.clone()), + response: Some(http_response.clone()), + captures, + asserts, + errors, + time_in_ms, + }; + entry_results.push(entry_result); } - log_verbose(""); - - EntryResult { - request: Some(http_request), - response: Some(http_response), - captures, - asserts, - errors, - time_in_ms, - } + entry_results } -pub fn log_request(log_verbose: impl Fn(&str), request: &http::Request) { +pub fn log_request(log_verbose: impl Fn(&str), request: &http::RequestSpec) { log_verbose("Request"); log_verbose(format!("{} {}", request.method, request.url).as_str()); for header in request.headers.clone() { diff --git a/packages/hurl/src/runner/hurl_file.rs b/packages/hurl/src/runner/hurl_file.rs index 036aa746e..387155656 100644 --- a/packages/hurl/src/runner/hurl_file.rs +++ b/packages/hurl/src/runner/hurl_file.rs @@ -124,7 +124,7 @@ pub fn run( break; } - let entry_result = entry::run( + let entry_results = entry::run( entry, http_client, entry_index, @@ -133,12 +133,15 @@ pub fn run( &log_verbose, &log_error_message, ); - entries.push(entry_result.clone()); - for e in entry_result.errors.clone() { - log_error(&e, false); + + for entry_result in entry_results.clone() { + for e in entry_result.errors.clone() { + log_error(&e, false); + } + entries.push(entry_result.clone()); } let exit = (options.post_entry)(); - if exit || (options.fail_fast && !entry_result.errors.is_empty()) { + if exit || (options.fail_fast && !entry_results.last().unwrap().errors.is_empty()) { break; } } diff --git a/packages/hurl/src/runner/log_deserialize.rs b/packages/hurl/src/runner/log_deserialize.rs index 97275a71f..61345ee1f 100644 --- a/packages/hurl/src/runner/log_deserialize.rs +++ b/packages/hurl/src/runner/log_deserialize.rs @@ -16,7 +16,6 @@ * */ -use crate::http; use crate::http::*; use super::cookie::*; @@ -99,11 +98,90 @@ fn parse_entry_result(value: serde_json::Value) -> Result { time_in_ms: 0, }) } +// pub fn _parse_request(value: serde_json::Value) -> Result { +// if let serde_json::Value::Object(map) = value { +// let method = match map.get("method") { +// Some(serde_json::Value::String(s)) => parse_method(s.clone())?, +// _ => return Err("expecting a string for the method".to_string()), +// }; +// let url = match map.get("url") { +// Some(serde_json::Value::String(s)) => s.to_string(), +// _ => return Err("expecting a string for the url".to_string()), +// }; +// +// let headers = match map.get("headers") { +// Some(serde_json::Value::Array(values)) => { +// let mut headers = vec![]; +// for value in values { +// let header = parse_header(value.clone())?; +// headers.push(header); +// } +// headers +// } +// _ => vec![], +// }; +// +// let querystring = match map.get("queryString") { +// Some(serde_json::Value::Array(values)) => { +// let mut params = vec![]; +// for value in values { +// let param = parse_param(value.clone())?; +// params.push(param); +// } +// params +// } +// _ => vec![], +// }; +// +// let form = match map.get("form") { +// Some(serde_json::Value::Array(values)) => { +// let mut params = vec![]; +// for value in values { +// let param = parse_param(value.clone())?; +// params.push(param); +// } +// params +// } +// _ => vec![], +// }; +// +// let cookies = match map.get("cookies") { +// Some(serde_json::Value::Array(values)) => { +// let mut headers = vec![]; +// for value in values { +// let header = parse_request_cookie(value.clone())?; +// headers.push(header); +// } +// headers +// } +// _ => vec![], +// }; +// +// // TODO +// let multipart = vec![]; +// let body = http::Body::Binary(vec![]); +// let content_type = None; +// +// Ok(Request { +// method, +// url, +// headers, +// querystring, +// form, +// multipart, +// cookies, +// body, +// content_type, +// }) +// } else { +// Err("expecting an object for the request".to_string()) +// } +// } pub fn parse_request(value: serde_json::Value) -> Result { if let serde_json::Value::Object(map) = value { let method = match map.get("method") { - Some(serde_json::Value::String(s)) => parse_method(s.clone())?, + Some(serde_json::Value::String(s)) => parse_method(s.clone())?.to_string(), _ => return Err("expecting a string for the method".to_string()), }; let url = match map.get("url") { @@ -123,57 +201,10 @@ pub fn parse_request(value: serde_json::Value) -> Result { _ => vec![], }; - let querystring = match map.get("queryString") { - Some(serde_json::Value::Array(values)) => { - let mut params = vec![]; - for value in values { - let param = parse_param(value.clone())?; - params.push(param); - } - params - } - _ => vec![], - }; - - let form = match map.get("form") { - Some(serde_json::Value::Array(values)) => { - let mut params = vec![]; - for value in values { - let param = parse_param(value.clone())?; - params.push(param); - } - params - } - _ => vec![], - }; - - let cookies = match map.get("cookies") { - Some(serde_json::Value::Array(values)) => { - let mut headers = vec![]; - for value in values { - let header = parse_request_cookie(value.clone())?; - headers.push(header); - } - headers - } - _ => vec![], - }; - - // TODO - let multipart = vec![]; - let body = http::Body::Binary(vec![]); - let content_type = None; - Ok(Request { - method, url, + method, headers, - querystring, - form, - multipart, - cookies, - body, - content_type, }) } else { Err("expecting an object for the request".to_string()) @@ -256,37 +287,37 @@ fn parse_header(value: serde_json::Value) -> Result { } } -pub fn parse_param(value: serde_json::Value) -> Result { - if let serde_json::Value::Object(map) = value { - let name = match map.get("name") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the cookie name".to_string()), - }; - let value = match map.get("value") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the cookie value".to_string()), - }; - Ok(Param { name, value }) - } else { - Err("Expecting object for the param".to_string()) - } -} +// pub fn parse_param(value: serde_json::Value) -> Result { +// if let serde_json::Value::Object(map) = value { +// let name = match map.get("name") { +// Some(serde_json::Value::String(s)) => s.to_string(), +// _ => return Err("expecting a string for the cookie name".to_string()), +// }; +// let value = match map.get("value") { +// Some(serde_json::Value::String(s)) => s.to_string(), +// _ => return Err("expecting a string for the cookie value".to_string()), +// }; +// Ok(Param { name, value }) +// } else { +// Err("Expecting object for the param".to_string()) +// } +// } -pub fn parse_request_cookie(value: serde_json::Value) -> Result { - if let serde_json::Value::Object(map) = value { - let name = match map.get("name") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the cookie name".to_string()), - }; - let value = match map.get("value") { - Some(serde_json::Value::String(s)) => s.to_string(), - _ => return Err("expecting a string for the cookie value".to_string()), - }; - Ok(RequestCookie { name, value }) - } else { - Err("Expecting object for the request cookie".to_string()) - } -} +// pub fn parse_request_cookie(value: serde_json::Value) -> Result { +// if let serde_json::Value::Object(map) = value { +// let name = match map.get("name") { +// Some(serde_json::Value::String(s)) => s.to_string(), +// _ => return Err("expecting a string for the cookie name".to_string()), +// }; +// let value = match map.get("value") { +// Some(serde_json::Value::String(s)) => s.to_string(), +// _ => return Err("expecting a string for the cookie value".to_string()), +// }; +// Ok(RequestCookie { name, value }) +// } else { +// Err("Expecting object for the request cookie".to_string()) +// } +// } #[allow(dead_code)] pub fn parse_response_cookie(value: serde_json::Value) -> Result { @@ -383,59 +414,59 @@ mod tests { use super::*; use crate::runner::value::Value; - #[test] - fn test_parse_request() { - let v: serde_json::Value = serde_json::from_str( - r#"{ - "method": "GET", - "url": "http://localhost:8000/hello", - "headers": [] -}"#, - ) - .unwrap(); - assert_eq!(parse_request(v).unwrap(), hello_http_request()); - - let v: serde_json::Value = serde_json::from_str( - r#"{ - "method": "GET", - "url": "http://localhost:8000/querystring-params?param1=value1¶m2=a%20b", - "headers": [] -}"#, - ) - .unwrap(); - assert_eq!( - parse_request(v).unwrap(), - http::Request { - method: http::Method::Get, - url: "http://localhost:8000/querystring-params?param1=value1¶m2=a%20b" - .to_string(), - querystring: vec![], - headers: vec![], - cookies: vec![], - body: http::Body::Binary(vec![]), - form: vec![], - multipart: vec![], - content_type: None, - } - ); - - let v: serde_json::Value = serde_json::from_str( - r#"{ - "method": "GET", - "url": "http://localhost/custom", - "headers": [ - {"name": "User-Agent", "value": "iPhone"}, - {"name": "Foo", "value": "Bar"} - ], - "cookies": [ - {"name": "theme", "value": "light"}, - {"name": "sessionToken", "value": "abc123"} - ] -}"#, - ) - .unwrap(); - assert_eq!(parse_request(v).unwrap(), custom_http_request()); - } + // #[test] + // fn test_parse_request() { + // let v: serde_json::Value = serde_json::from_str( + // r#"{ + // "method": "GET", + // "url": "http://localhost:8000/hello", + // "headers": [] + // }"#, + // ) + // .unwrap(); + // assert_eq!(_parse_request(v).unwrap(), hello_http_request()); + // + // let v: serde_json::Value = serde_json::from_str( + // r#"{ + // "method": "GET", + // "url": "http://localhost:8000/querystring-params?param1=value1¶m2=a%20b", + // "headers": [] + // }"#, + // ) + // .unwrap(); + // assert_eq!( + // _parse_request(v).unwrap(), + // http::Request { + // method: http::Method::Get, + // url: "http://localhost:8000/querystring-params?param1=value1¶m2=a%20b" + // .to_string(), + // querystring: vec![], + // headers: vec![], + // cookies: vec![], + // body: http::Body::Binary(vec![]), + // form: vec![], + // multipart: vec![], + // content_type: None, + // } + // ); + // + // let v: serde_json::Value = serde_json::from_str( + // r#"{ + // "method": "GET", + // "url": "http://localhost/custom", + // "headers": [ + // {"name": "User-Agent", "value": "iPhone"}, + // {"name": "Foo", "value": "Bar"} + // ], + // "cookies": [ + // {"name": "theme", "value": "light"}, + // {"name": "sessionToken", "value": "abc123"} + // ] + // }"#, + // ) + // .unwrap(); + // assert_eq!(_parse_request(v).unwrap(), custom_http_request()); + // } #[test] fn test_parse_response() { diff --git a/packages/hurl/src/runner/log_serialize.rs b/packages/hurl/src/runner/log_serialize.rs index 2b752b492..608f88537 100644 --- a/packages/hurl/src/runner/log_serialize.rs +++ b/packages/hurl/src/runner/log_serialize.rs @@ -94,20 +94,12 @@ impl Serialize for Request { where S: Serializer, { - // 3 is the number of fields in the struct. - let mut state = serializer.serialize_struct("??", 3)?; - state.serialize_field("method", &self.clone().method.to_string())?; + let mut state = serializer.serialize_struct("Request", 5)?; + state.serialize_field("method", &self.clone().method)?; state.serialize_field("url", &self.clone().url)?; - state.serialize_field("queryString", &self.clone().querystring)?; state.serialize_field("headers", &self.clone().headers)?; - state.serialize_field("cookies", &self.clone().cookies)?; - state.serialize_field("multipartFormData", &self.clone().multipart)?; - - if !self.clone().form.is_empty() { - state.serialize_field("form", &self.clone().form)?; - } - state.serialize_field("body", &base64::encode(&self.body.bytes()))?; - + state.serialize_field("cookies", &self.clone().cookies())?; + state.serialize_field("queryString", &self.clone().query_string_params())?; state.end() } } @@ -118,7 +110,7 @@ impl Serialize for Response { S: Serializer, { // 3 is the number of fields in the struct. - let mut state = serializer.serialize_struct("??", 3)?; + let mut state = serializer.serialize_struct("Response", 3)?; state.serialize_field("httpVersion", &self.clone().version)?; state.serialize_field("status", &self.clone().status)?; state.serialize_field("cookies", &self.clone().cookies())?; diff --git a/packages/hurl/src/runner/request.rs b/packages/hurl/src/runner/request.rs index e49fa628d..bd241fa54 100644 --- a/packages/hurl/src/runner/request.rs +++ b/packages/hurl/src/runner/request.rs @@ -36,7 +36,7 @@ pub fn eval_request( request: Request, variables: &HashMap, context_dir: String, -) -> Result { +) -> Result { let method = eval_method(request.method.clone()); let url = eval_template(request.clone().url, &variables)?; @@ -121,7 +121,7 @@ pub fn eval_request( // } // } - Ok(http::Request { + Ok(http::RequestSpec { method, url, headers, diff --git a/packages/hurl/src/runner/response.rs b/packages/hurl/src/runner/response.rs index fdf7b7dae..7274c30e4 100644 --- a/packages/hurl/src/runner/response.rs +++ b/packages/hurl/src/runner/response.rs @@ -212,7 +212,7 @@ pub fn eval_asserts( pub fn eval_captures( response: Response, - http_response: http::Response, + http_response: &http::Response, variables: &HashMap, ) -> Result, Error> { let mut captures = vec![]; @@ -321,7 +321,7 @@ mod tests { assert_eq!( eval_captures( user_response(), - http::xml_two_users_http_response(), + &http::xml_two_users_http_response(), &variables, ) .unwrap(), diff --git a/packages/hurl/tests/libcurl.rs b/packages/hurl/tests/libcurl.rs index e6aadf7a6..a25975462 100644 --- a/packages/hurl/tests/libcurl.rs +++ b/packages/hurl/tests/libcurl.rs @@ -14,8 +14,8 @@ fn default_client() -> Client { Client::init(options) } -fn default_get_request(url: String) -> Request { - Request { +fn default_get_request(url: String) -> RequestSpec { + RequestSpec { method: Method::Get, url, headers: vec![], @@ -33,13 +33,25 @@ fn default_get_request(url: String) -> Request { #[test] fn test_hello() { let mut client = default_client(); - let request = default_get_request("http://localhost:8000/hello".to_string()); + let request_spec = default_get_request("http://localhost:8000/hello".to_string()); assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/hello'".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!(request.method, "GET".to_string()); + assert_eq!(request.url, "http://localhost:8000/hello".to_string()); + assert_eq!(request.headers.len(), 3); + assert!(request.headers.contains(&Header { + name: "Host".to_string(), + value: "localhost:8000".to_string() + })); + assert!(request.headers.contains(&Header { + name: "Accept".to_string(), + value: "*/*".to_string() + })); + assert_eq!(response.version, Version::Http10); assert_eq!(response.status, 200); assert_eq!(response.body, b"Hello World!".to_vec()); @@ -63,7 +75,7 @@ fn test_hello() { #[test] fn test_put() { let mut client = default_client(); - let request = Request { + let request_spec = RequestSpec { method: Method::Put, url: "http://localhost:8000/put".to_string(), headers: vec![], @@ -75,11 +87,22 @@ fn test_put() { content_type: None, }; assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/put' -X PUT".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!(request.method, "PUT".to_string()); + assert_eq!(request.url, "http://localhost:8000/put".to_string()); + assert!(request.headers.contains(&Header { + name: "Host".to_string(), + value: "localhost:8000".to_string() + })); + assert!(request.headers.contains(&Header { + name: "Accept".to_string(), + value: "*/*".to_string() + })); + assert_eq!(response.status, 200); assert!(response.body.is_empty()); } @@ -87,7 +110,7 @@ fn test_put() { #[test] fn test_patch() { let mut client = default_client(); - let request = Request { + let request_spec = RequestSpec { method: Method::Patch, url: "http://localhost:8000/patch/file.txt".to_string(), headers: vec![ @@ -112,11 +135,25 @@ fn test_patch() { content_type: None, }; assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/patch/file.txt' -X PATCH -H 'Host: www.example.com' -H 'Content-Type: application/example' -H 'If-Match: \"e0023aa4e\"'".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!(request.method, "PATCH".to_string()); + assert_eq!( + request.url, + "http://localhost:8000/patch/file.txt".to_string() + ); + assert!(request.headers.contains(&Header { + name: "Host".to_string(), + value: "www.example.com".to_string() + })); + assert!(request.headers.contains(&Header { + name: "Content-Type".to_string(), + value: "application/example".to_string() + })); + assert_eq!(response.status, 204); assert!(response.body.is_empty()); } @@ -128,7 +165,7 @@ fn test_patch() { #[test] fn test_custom_headers() { let mut client = default_client(); - let request = Request { + let request_spec = RequestSpec { method: Method::Get, url: "http://localhost:8000/custom-headers".to_string(), headers: vec![ @@ -147,11 +184,20 @@ fn test_custom_headers() { }; assert!(client.options.curl_args().is_empty()); assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/custom-headers' -H 'Fruit: Raspberry' -H 'Fruit: Apple' -H 'Fruit: Banana' -H 'Fruit: Grape' -H 'Color: Green'".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!(request.method, "GET".to_string()); + assert_eq!( + request.url, + "http://localhost:8000/custom-headers".to_string() + ); + assert!(request.headers.contains(&Header { + name: "Fruit".to_string(), + value: "Raspberry".to_string() + })); assert_eq!(response.status, 200); assert!(response.body.is_empty()); } @@ -163,7 +209,7 @@ fn test_custom_headers() { #[test] fn test_querystring_params() { let mut client = default_client(); - let request = Request { + let request_spec = RequestSpec { method: Method::Get, url: "http://localhost:8000/querystring-params".to_string(), headers: vec![], @@ -192,10 +238,14 @@ fn test_querystring_params() { content_type: None, }; assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3Db¶m4=1%2C2%2C3'".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!(request.method, "GET".to_string()); + assert_eq!(request.url, "http://localhost:8000/querystring-params?param1=value1¶m2=¶m3=a%3Db¶m4=1%2C2%2C3".to_string()); + assert_eq!(request.headers.len(), 3); + assert_eq!(response.status, 200); assert!(response.body.is_empty()); } @@ -207,7 +257,7 @@ fn test_querystring_params() { #[test] fn test_form_params() { let mut client = default_client(); - let request = Request { + let request_spec = RequestSpec { method: Method::Post, url: "http://localhost:8000/form-params".to_string(), headers: vec![], @@ -236,17 +286,27 @@ fn test_form_params() { content_type: Some("application/x-www-form-urlencoded".to_string()), }; assert_eq!( - client.curl_command_line(&request), + 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() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!(request.method, "POST".to_string()); + assert_eq!(request.url, "http://localhost:8000/form-params".to_string()); + assert!(request.headers.contains(&Header { + name: "Content-Type".to_string(), + value: "application/x-www-form-urlencoded".to_string() + })); + assert_eq!(response.status, 200); assert!(response.body.is_empty()); // make sure you can reuse client for other request let request = default_get_request("http://localhost:8000/hello".to_string()); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request).unwrap(); + assert_eq!(request.method, "GET".to_string()); + assert_eq!(request.url, "http://localhost:8000/hello".to_string()); + assert_eq!(request.headers.len(), 3); assert_eq!(response.status, 200); assert_eq!(response.body, b"Hello World!".to_vec()); } @@ -256,11 +316,15 @@ fn test_form_params() { // region redirect #[test] -fn test_follow_location() { - let request = default_get_request("http://localhost:8000/redirect".to_string()); +fn test_redirect() { + let request_spec = default_get_request("http://localhost:8000/redirect".to_string()); let mut client = default_client(); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!(request.method, "GET".to_string()); + assert_eq!(request.url, "http://localhost:8000/redirect".to_string()); + assert_eq!(request.headers.len(), 3); + assert_eq!(response.status, 302); assert_eq!( response @@ -270,6 +334,11 @@ fn test_follow_location() { "http://localhost:8000/redirected" ); assert_eq!(client.redirect_count, 0); +} + +#[test] +fn test_follow_location() { + let request_spec = default_get_request("http://localhost:8000/redirect".to_string()); let options = ClientOptions { follow_location: true, @@ -288,24 +357,35 @@ fn test_follow_location() { let mut client = Client::init(options); assert_eq!(client.options.curl_args(), vec!["-L".to_string(),]); assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/redirect' -L".to_string() ); - let response = client.execute(&request, 0).unwrap(); - assert_eq!(response.status, 200); - assert_eq!( - response - .get_header_values("Content-Length".to_string()) - .get(0) - .unwrap(), - "0" - ); + let calls = client.execute_with_redirect(&request_spec).unwrap(); + assert_eq!(calls.len(), 2); + + let (request1, response1) = calls.get(0).unwrap(); + assert_eq!(request1.method, "GET".to_string()); + assert_eq!(request1.url, "http://localhost:8000/redirect".to_string()); + assert_eq!(request1.headers.len(), 3); + assert_eq!(response1.status, 302); + assert!(response1.headers.contains(&Header { + name: "Location".to_string(), + value: "http://localhost:8000/redirected".to_string() + })); + + let (request2, response2) = calls.get(1).unwrap(); + assert_eq!(request2.method, "GET".to_string()); + assert_eq!(request2.url, "http://localhost:8000/redirected".to_string()); + assert_eq!(request2.headers.len(), 3); + assert_eq!(response2.status, 200); + assert_eq!(client.redirect_count, 1); // make sure that the redirect count is reset to 0 let request = default_get_request("http://localhost:8000/hello".to_string()); - let response = client.execute(&request, 0).unwrap(); + let calls = client.execute_with_redirect(&request).unwrap(); + let (_, response) = calls.get(0).unwrap(); assert_eq!(response.status, 200); assert_eq!(response.body, b"Hello World!".to_vec()); assert_eq!(client.redirect_count, 0); @@ -328,18 +408,25 @@ fn test_max_redirect() { context_dir: ".".to_string(), }; let mut client = Client::init(options); - let request = default_get_request("http://localhost:8000/redirect".to_string()); + + let request_spec = default_get_request("http://localhost:8000/redirect/15".to_string()); assert_eq!( - client.curl_command_line(&request), - "curl 'http://localhost:8000/redirect' -L --max-redirs 10".to_string() + client.curl_command_line(&request_spec), + "curl 'http://localhost:8000/redirect/15' -L --max-redirs 10".to_string() ); - - let response = client.execute(&request, 5).unwrap(); - assert_eq!(response.status, 200); - assert_eq!(client.redirect_count, 6); - - let error = client.execute(&request, 11).err().unwrap(); + let error = client.execute_with_redirect(&request_spec).err().unwrap(); assert_eq!(error, HttpError::TooManyRedirect); + + let request_spec = default_get_request("http://localhost:8000/redirect/8".to_string()); + assert_eq!( + client.curl_command_line(&request_spec), + "curl 'http://localhost:8000/redirect/8' -L --max-redirs 10".to_string() + ); + let calls = client.execute_with_redirect(&request_spec).unwrap(); + let (request, response) = calls.last().unwrap(); + assert_eq!(request.url, "http://localhost:8000/redirect/0".to_string()); + assert_eq!(response.status, 200); + assert_eq!(client.redirect_count, 8); } // endregion @@ -349,7 +436,7 @@ fn test_max_redirect() { #[test] fn test_multipart_form_data() { let mut client = default_client(); - let request = Request { + let request_spec = RequestSpec { method: Method::Post, url: "http://localhost:8000/multipart-form-data".to_string(), headers: vec![], @@ -384,17 +471,22 @@ fn test_multipart_form_data() { content_type: Some("multipart/form-data".to_string()), }; assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/multipart-form-data' -F 'key1=value1' -F 'upload1=@data.txt;type=text/plain' -F 'upload2=@data.html;type=text/html' -F 'upload3=@data.txt;type=text/html'".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert!(request.headers.contains(&Header { + name: "Content-Length".to_string(), + value: "616".to_string(), + })); assert_eq!(response.status, 200); assert!(response.body.is_empty()); // make sure you can reuse client for other request - let request = default_get_request("http://localhost:8000/hello".to_string()); - let response = client.execute(&request, 0).unwrap(); + let request_spec = default_get_request("http://localhost:8000/hello".to_string()); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!(request.method, "GET".to_string()); assert_eq!(response.status, 200); assert_eq!(response.body, b"Hello World!".to_vec()); } @@ -406,7 +498,7 @@ fn test_multipart_form_data() { #[test] fn test_post_bytes() { let mut client = default_client(); - let request = Request { + let request_spec = RequestSpec { method: Method::Post, url: "http://localhost:8000/post-base64".to_string(), headers: vec![], @@ -418,10 +510,15 @@ fn test_post_bytes() { content_type: None, }; assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/post-base64' -H 'Content-Type: application/octet-stream' --data $'\\x48\\x65\\x6c\\x6c\\x6f\\x20\\x57\\x6f\\x72\\x6c\\x64\\x21'".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert!(request.headers.contains(&Header { + name: "Content-Length".to_string(), + value: "12".to_string(), + })); + assert_eq!(response.status, 200); assert!(response.body.is_empty()); } @@ -431,7 +528,7 @@ fn test_post_bytes() { #[test] fn test_expect() { let mut client = default_client(); - let request = Request { + let request_spec = RequestSpec { method: Method::Post, url: "http://localhost:8000/expect".to_string(), headers: vec![Header { @@ -446,11 +543,15 @@ fn test_expect() { content_type: None, }; assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/expect' -H 'Expect: 100-continue' -H 'Content-Type:' --data 'data'".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert!(request.headers.contains(&Header { + name: "Expect".to_string(), + value: "100-continue".to_string(), + })); assert_eq!(response.status, 200); assert_eq!(response.version, Version::Http10); assert!(response.body.is_empty()); @@ -473,7 +574,7 @@ fn test_basic_authentication() { context_dir: ".".to_string(), }; let mut client = Client::init(options); - let request = Request { + let request_spec = RequestSpec { method: Method::Get, url: "http://localhost:8000/basic-authentication".to_string(), headers: vec![], @@ -485,16 +586,20 @@ fn test_basic_authentication() { content_type: None, }; assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/basic-authentication' --user 'bob:secret'".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert!(request.headers.contains(&Header { + name: "Authorization".to_string(), + value: "Basic Ym9iOnNlY3JldA==".to_string(), + })); assert_eq!(response.status, 200); assert_eq!(response.version, Version::Http10); assert_eq!(response.body, b"You are authenticated".to_vec()); let mut client = default_client(); - let request = Request { + let request_spec = RequestSpec { method: Method::Get, url: "http://bob:secret@localhost:8000/basic-authentication".to_string(), headers: vec![], @@ -506,10 +611,14 @@ fn test_basic_authentication() { content_type: None, }; assert_eq!( - request.curl_args(".".to_string()), + request_spec.curl_args(".".to_string()), vec!["'http://bob:secret@localhost:8000/basic-authentication'".to_string()] ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert!(request.headers.contains(&Header { + name: "Authorization".to_string(), + value: "Basic Ym9iOnNlY3JldA==".to_string(), + })); assert_eq!(response.status, 200); assert_eq!(response.version, Version::Http10); assert_eq!(response.body, b"You are authenticated".to_vec()); @@ -521,7 +630,7 @@ fn test_basic_authentication() { fn test_error_could_not_resolve_host() { let mut client = default_client(); let request = default_get_request("http://unknown".to_string()); - let error = client.execute(&request, 0).err().unwrap(); + let error = client.execute(&request).err().unwrap(); assert_eq!(error, HttpError::CouldNotResolveHost("unknown".to_string())); } @@ -529,8 +638,8 @@ fn test_error_could_not_resolve_host() { #[test] fn test_error_fail_to_connect() { let mut client = default_client(); - let request = default_get_request("http://localhost:9999".to_string()); - let error = client.execute(&request, 0).err().unwrap(); + let request_spec = default_get_request("http://localhost:9999".to_string()); + let error = client.execute(&request_spec).err().unwrap(); assert_eq!(error, HttpError::FailToConnect); let options = ClientOptions { @@ -549,7 +658,7 @@ fn test_error_fail_to_connect() { }; let mut client = Client::init(options); let request = default_get_request("http://localhost:8000/hello".to_string()); - let error = client.execute(&request, 0).err().unwrap(); + let error = client.execute(&request).err().unwrap(); assert_eq!(error, HttpError::FailToConnect); } @@ -570,8 +679,8 @@ fn test_error_could_not_resolve_proxy_name() { context_dir: ".".to_string(), }; let mut client = Client::init(options); - let request = default_get_request("http://localhost:8000/hello".to_string()); - let error = client.execute(&request, 0).err().unwrap(); + let request_spec = default_get_request("http://localhost:8000/hello".to_string()); + let error = client.execute(&request_spec).err().unwrap(); assert_eq!(error, HttpError::CouldNotResolveProxyName); } @@ -592,8 +701,8 @@ fn test_error_ssl() { context_dir: ".".to_string(), }; let mut client = Client::init(options); - let request = default_get_request("https://localhost:8001/hello".to_string()); - let error = client.execute(&request, 0).err().unwrap(); + let request_spec = default_get_request("https://localhost:8001/hello".to_string()); + let error = client.execute(&request_spec).err().unwrap(); let message = if cfg!(windows) { "schannel: SEC_E_UNTRUSTED_ROOT (0x80090325) - The certificate chain was issued by an authority that is not trusted.".to_string() } else { @@ -619,8 +728,8 @@ fn test_timeout() { context_dir: ".".to_string(), }; let mut client = Client::init(options); - let request = default_get_request("http://localhost:8000/timeout".to_string()); - let error = client.execute(&request, 0).err().unwrap(); + let request_spec = default_get_request("http://localhost:8000/timeout".to_string()); + let error = client.execute(&request_spec).err().unwrap(); assert_eq!(error, HttpError::Timeout); } @@ -642,7 +751,7 @@ fn test_accept_encoding() { }; let mut client = Client::init(options); - let request = Request { + let request_spec = RequestSpec { method: Method::Get, url: "http://localhost:8000/compressed/gzip".to_string(), headers: vec![], @@ -653,7 +762,11 @@ fn test_accept_encoding() { body: Body::Binary(vec![]), content_type: None, }; - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert!(request.headers.contains(&Header { + name: "Accept-Encoding".to_string(), + value: "gzip, deflate, br".to_string() + })); assert_eq!(response.status, 200); assert!(response.headers.contains(&Header { name: "Content-Length".to_string(), @@ -678,12 +791,12 @@ fn test_connect_timeout() { context_dir: ".".to_string(), }; let mut client = Client::init(options); - let request = default_get_request("http://10.0.0.0".to_string()); + let request_spec = default_get_request("http://10.0.0.0".to_string()); assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://10.0.0.0' --connect-timeout 1".to_string() ); - let error = client.execute(&request, 0).err().unwrap(); + let error = client.execute(&request_spec).err().unwrap(); if cfg!(target_os = "macos") { assert_eq!(error, HttpError::FailToConnect); } else { @@ -697,7 +810,7 @@ fn test_connect_timeout() { #[test] fn test_cookie() { let mut client = default_client(); - let request = Request { + let request_spec = RequestSpec { method: Method::Get, url: "http://localhost:8000/cookies/set-request-cookie1-valueA".to_string(), headers: vec![], @@ -712,18 +825,22 @@ fn test_cookie() { content_type: None, }; assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/cookies/set-request-cookie1-valueA' --cookie 'cookie1=valueA'" .to_string() ); //assert_eq!(request.cookies(), vec!["cookie1=valueA".to_string(),]); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert!(request.headers.contains(&Header { + name: "Cookie".to_string(), + value: "cookie1=valueA".to_string() + })); assert_eq!(response.status, 200); assert!(response.body.is_empty()); - let request = Request { + let request_spec = RequestSpec { method: Method::Get, url: "http://localhost:8000/cookies/assert-that-cookie1-is-not-in-session".to_string(), headers: vec![], @@ -734,14 +851,14 @@ fn test_cookie() { body: Body::Binary(vec![]), content_type: None, }; - let response = client.execute(&request, 0).unwrap(); + let (_request, response) = client.execute(&request_spec).unwrap(); assert_eq!(response.status, 200); } #[test] fn test_multiple_request_cookies() { let mut client = default_client(); - let request = Request { + let request_spec = RequestSpec { method: Method::Get, url: "http://localhost:8000/cookies/set-multiple-request-cookies".to_string(), headers: vec![], @@ -762,11 +879,15 @@ fn test_multiple_request_cookies() { content_type: None, }; assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/cookies/set-multiple-request-cookies' --cookie 'user1=Bob; user2=Bill'".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert!(request.headers.contains(&Header { + name: "Cookie".to_string(), + value: "user1=Bob; user2=Bill".to_string() + })); assert_eq!(response.status, 200); assert!(response.body.is_empty()); } @@ -774,9 +895,13 @@ fn test_multiple_request_cookies() { #[test] fn test_cookie_storage() { let mut client = default_client(); - let request = + let request_spec = default_get_request("http://localhost:8000/cookies/set-session-cookie2-valueA".to_string()); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!( + request.url, + "http://localhost:8000/cookies/set-session-cookie2-valueA".to_string() + ); assert_eq!(response.status, 200); assert!(response.body.is_empty()); @@ -791,14 +916,18 @@ fn test_cookie_storage() { expires: "0".to_string(), name: "cookie2".to_string(), value: "valueA".to_string(), - http_only: false + http_only: false, } ); - let request = default_get_request( + let request_spec = default_get_request( "http://localhost:8000/cookies/assert-that-cookie2-is-valueA".to_string(), ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert!(request.headers.contains(&Header { + name: "Cookie".to_string(), + value: "cookie2=valueA".to_string() + })); assert_eq!(response.status, 200); assert!(response.body.is_empty()); } @@ -820,15 +949,24 @@ fn test_cookie_file() { context_dir: ".".to_string(), }; let mut client = Client::init(options); - let request = default_get_request( + let request_spec = default_get_request( "http://localhost:8000/cookies/assert-that-cookie2-is-valueA".to_string(), ); assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/cookies/assert-that-cookie2-is-valueA' --cookie tests/cookies.txt".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!( + request.url, + "http://localhost:8000/cookies/assert-that-cookie2-is-valueA" + ); + assert!(request.headers.contains(&Header { + name: "Cookie".to_string(), + value: "cookie2=valueA".to_string() + })); + assert_eq!(response.status, 200); assert!(response.body.is_empty()); } @@ -855,12 +993,13 @@ fn test_proxy() { context_dir: ".".to_string(), }; let mut client = Client::init(options); - let request = default_get_request("http://localhost:8000/proxy".to_string()); + let request_spec = default_get_request("http://localhost:8000/proxy".to_string()); assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'http://localhost:8000/proxy' --proxy 'localhost:8888'".to_string() ); - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!(request.url, "http://localhost:8000/proxy"); assert_eq!(response.status, 200); } @@ -884,15 +1023,13 @@ fn test_insecure() { }; let mut client = Client::init(options); assert_eq!(client.options.curl_args(), vec!["--insecure".to_string()]); - let request = default_get_request("https://localhost:8001/hello".to_string()); + let request_spec = default_get_request("https://localhost:8001/hello".to_string()); assert_eq!( - client.curl_command_line(&request), + client.curl_command_line(&request_spec), "curl 'https://localhost:8001/hello' --insecure".to_string() ); - let response = client.execute(&request, 0).unwrap(); - assert_eq!(response.status, 200); - - let response = client.execute(&request, 0).unwrap(); + let (request, response) = client.execute(&request_spec).unwrap(); + assert_eq!(request.url, "https://localhost:8001/hello"); assert_eq!(response.status, 200); }