Keno/URIParser.jl — pull request #13 by samoconnor (2014-08-06 13:57:03+00:00)
Title: Hex encoding fixes for RFC3986 compliance

My use case involves generating AWS authentication signatures.

Uppercase hex encoding per RFC3986 is needed in URIParser.escape_with() -- called by Requests.format_query_str().

URIParser.query_params() should do unescaping so that passing params back to Requests.format_query_str() does not result in double-escaping.

```julia
# Create AWS Signature Version 4 Authentication Headers.
# http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html

function sign_aws4_request!(r::AWSRequest, t)

# ISO8601 date/time strings for time of request...
date = format(t,"yyyymmdd")
datetime = format(t,"yyyymmddTHHMMSSZ")

# Authentication scope...
scope = {date, r.aws["region"], r.aws["service"], "aws4_request"}

# Signing key generated from today's scope string...
signing_key = string("AWS4", r.aws["secret_key"])
for element in scope
signing_key = sha256_hmac(signing_key, element)
end

# Authentication scope string...
scope = join(scope, "/")

# SHA256 hash of content...
content_hash = sha256_hash(r.content) |> bytes2hex

# HTTP headers...
delete!(r.headers, "Authorization")
uri = URI(r.url)
merge!(r.headers, {
"x-amz-content-sha256" => content_hash,
"x-amz-date" => datetime,
"Host" => uri.host,
"Content-MD5" => base64(md5_hash(r.content))
})
if !haskey(r.headers, "Content-Type") && r.verb == "POST"
r.headers["Content-Type"] =
"application/x-www-form-urlencoded; charset=utf-8"
end
if haskey(r.aws, "AWSToken")
r.headers["x-amz-security-token"] = r.aws["AWSToken"]
end

# Sort and lowercase() Headers to produce canonical form...
canonical_headers = ["$(lowercase(k)):$(strip(v))" for (k,v) in r.headers]
signed_headers = join(sort([lowercase(k) for k in keys(r.headers)]), ";")

# Sort Query String...
query = query_params(uri)
query = [(k, query[k]) for k in sort(collect(keys(query)))]

# Create hash of canonical request...
canonical_form = string(r.verb, "\n",
uri.path, "\n",
format_query_str(query), "\n",
join(sort(canonical_headers), "\n"), "\n\n",
signed_headers, "\n",
content_hash)
canonical_hash = sha256_hash(canonical_form) |> bytes2hex

# Create and sign "String to Sign"...
string_to_sign = "AWS4-HMAC-SHA256\n$datetime\n$scope\n$canonical_hash"
signature = sha256_hmac(signing_key, string_to_sign) |> bytes2hex

# Append Authorization header...
r.headers["Authorization"] = string (
"AWS4-HMAC-SHA256 ",
"Credential=$(r.aws["access_key_id"])/$scope, ",
"SignedHeaders=$signed_headers, ",
"Signature=$signature"
)
end
```