diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index f1d0556fcf..ea1a7ff758 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -222,44 +222,44 @@ static CURLcode uri_encode_path(struct Curl_str *original_path, return CURLE_OK; } -static CURLcode encode_query_component(char *component, size_t len, - struct dynbuf *db) +/* Normalize the query part. Make sure %2B is left percent encoded, and not + decoded to plus, then encoded to space. +*/ +static CURLcode normalize_query(const char *string, size_t len, + struct dynbuf *db) { - size_t i; - for(i = 0; i < len; i++) { - CURLcode result = CURLE_OK; - unsigned char this_char = component[i]; + CURLcode result = CURLE_OK; - if(is_reserved_char(this_char)) + while(len && !result) { + unsigned char in = (unsigned char)*string; + if(('%' == in) && (len > 2) && + ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { + /* this is two hexadecimal digits following a '%' */ + in = (unsigned char)((curlx_hexval(string[1]) << 4) | + curlx_hexval(string[2])); + string += 3; + len -= 3; + if(in == '+') { + /* decodes to plus, so leave this encoded */ + result = curlx_dyn_addn(db, "%2B", 3); + continue; + } + } + else { + string++; + len--; + } + + if(is_reserved_char(in)) /* Escape unreserved chars from RFC 3986 */ - result = curlx_dyn_addn(db, &this_char, 1); - else if(this_char == '+') + result = curlx_dyn_addn(db, &in, 1); + else if(in == '+') /* Encode '+' as space */ result = curlx_dyn_add(db, "%20"); else - result = curlx_dyn_addf(db, "%%%02X", this_char); - if(result) - return result; + result = curlx_dyn_addf(db, "%%%02X", in); } - return CURLE_OK; -} - -/* - * Populates a dynbuf containing url_encode(url_decode(in)) - */ -static CURLcode http_aws_decode_encode(const char *in, size_t in_len, - struct dynbuf *out) -{ - char *out_s; - size_t out_s_len; - CURLcode result = - Curl_urldecode(in, in_len, &out_s, &out_s_len, REJECT_NADA); - - if(!result) { - result = encode_query_component(out_s, out_s_len, out); - Curl_safefree(out_s); - } return result; } @@ -750,8 +750,8 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) counted_query_components++; /* Decode/encode the key */ - result = http_aws_decode_encode(in_key, in_key_len, - &encoded_query_array[index].key); + result = normalize_query(in_key, in_key_len, + &encoded_query_array[index].key); if(result) { goto fail; } @@ -761,8 +761,8 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) size_t in_value_len; const char *in_value = offset + 1; in_value_len = query_part + query_part_len - (offset + 1); - result = http_aws_decode_encode(in_value, in_value_len, - &encoded_query_array[index].value); + result = normalize_query(in_value, in_value_len, + &encoded_query_array[index].value); if(result) { goto fail; } diff --git a/tests/unit/unit1979.c b/tests/unit/unit1979.c index 943abfed65..821ec5608d 100644 --- a/tests/unit/unit1979.c +++ b/tests/unit/unit1979.c @@ -83,6 +83,12 @@ static CURLcode test_unit1979(const char *arg) "/example space/", "/example%20space/" }, + { + "get-plus-normalized", + true, + "/example+space/", + "/example%2Bspace/" + }, { "get-slash-dot-slash-unnormalized", false, diff --git a/tests/unit/unit1980.c b/tests/unit/unit1980.c index aa08c44e6a..0c2977a11f 100644 --- a/tests/unit/unit1980.c +++ b/tests/unit/unit1980.c @@ -79,6 +79,11 @@ static CURLcode test_unit1980(const char *arg) "p3= &p1=+&p2=%20", "p1=%20&p2=%20&p3=%20" }, + { + "2b-incoming", + "p3=%2b&p1=+", + "p1=%20&p3=%2B" + }, }; size_t i;