diff --git a/src/HTTP.jl b/src/HTTP.jl index fab06395d..ef9c3cb59 100644 --- a/src/HTTP.jl +++ b/src/HTTP.jl @@ -149,6 +149,11 @@ Redirect options - `redirect = true`, follow 3xx redirect responses. - `redirect_limit = 3`, number of times to redirect. + - `redirect_method = nothing`, the method to use for the redirected request; by default, + GET will be used, only responses with 307/308 will use the same original request method. + Pass `:same` to pass the same method as the orginal request though note that some servers + may not response/accept the same method. It's also valid to pass the exact method to use + as a string, like `redirect_method="PUT"`. - `forwardheaders = true`, forward original headers on redirect. diff --git a/src/Messages.jl b/src/Messages.jl index 861ec9eeb..5eb6420b8 100644 --- a/src/Messages.jl +++ b/src/Messages.jl @@ -71,7 +71,7 @@ using ..Pairs using ..IOExtras using ..Parsers import ..@require, ..precondition_error -import ..bytes +import ..bytes, ..Form include("ascii.jl") @@ -597,6 +597,12 @@ body_show_max = 1000 The first chunk of the Message Body (for display purposes). """ bodysummary(body) = isbytes(body) ? view(bytes(body), 1:min(nbytes(body), body_show_max)) : "[Message Body was streamed]" +function bodysummary(body::Form) + if length(body.data) == 1 && isa(body.data[1], IOBuffer) + return body.data[1].data[1:body.data[1].ptr-1] + end + return "[Message Body was streamed]" +end function compactstartline(m::Message) b = IOBuffer() diff --git a/src/RedirectRequest.jl b/src/RedirectRequest.jl index 50fb755cf..1f6c07050 100644 --- a/src/RedirectRequest.jl +++ b/src/RedirectRequest.jl @@ -14,7 +14,7 @@ export redirectlayer, nredirects Redirects the request in the case of 3xx response status. """ function redirectlayer(handler) - return function(req; redirect::Bool=true, redirect_limit::Int=3, forwardheaders::Bool=true, response_stream=nothing, kw...) + return function(req; redirect::Bool=true, redirect_limit::Int=3, redirect_method=nothing, forwardheaders::Bool=true, response_stream=nothing, kw...) if !redirect || redirect_limit == 0 # no redirecting return handler(req; redirect_limit=redirect_limit, kw...) @@ -35,7 +35,9 @@ function redirectlayer(handler) # follow redirect oldurl = req.url url = resolvereference(req.url, location) - req = Request(req.method, resource(url), copy(req.headers), req.body; + method = newmethod(req.method, res.status, redirect_method) + body = method == "GET" ? UInt8[] : req.body + req = Request(method, resource(url), copy(req.headers), body; url=url, version=req.version, responsebody=response_stream, parent=res, context=req.context) if forwardheaders req.headers = filter(req.headers) do (header, _) @@ -44,6 +46,8 @@ function redirectlayer(handler) return false elseif (header in SENSITIVE_HEADERS && !isdomainorsubdomain(url.host, oldurl.host)) return false + elseif method == "GET" && header in ("Content-Type", "Content-Length") + return false else return true end @@ -84,4 +88,21 @@ function verify_url(url::URI) end end +function newmethod(request_method, response_status, redirect_method) + # using https://everything.curl.dev/http/redirects#get-or-post as a reference + # also reference: https://github.com/curl/curl/issues/5237#issuecomment-618293609 + if response_status == 307 || response_status == 308 + # specific status codes that indicate an identical request should be made to new location + return request_method + elseif response_status == 303 + # 303 means it's a new/different URI, so only GET allowed + return "GET" + elseif redirect_method == :same + return request_method + elseif redirect_method !== nothing && redirect_method in ("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "PATCH") + return redirect_method + end + return "GET" +end + end # module RedirectRequest