Skip to content

Integrate with ExceptionUnwrapping #1065

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
CodecZlib = "944b1d66-785c-5afd-91f1-9de20f533193"
ConcurrentUtilities = "f0e56b4a-5159-44fe-b623-3e5288b988bb"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
ExceptionUnwrapping = "460bff9d-24e4-43bc-9d9f-a8973cb893f4"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
LoggingExtras = "e6f89c97-d47a-5376-807f-9c37f3926c36"
MbedTLS = "739be429-bea8-5141-9913-cc70e7f3736d"
Expand All @@ -22,6 +23,7 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
[compat]
CodecZlib = "0.7"
ConcurrentUtilities = "2.2"
ExceptionUnwrapping = "0.1"
LoggingExtras = "0.4.9,1"
MbedTLS = "0.6.8, 0.7, 1"
OpenSSL = "1.3"
Expand Down
23 changes: 20 additions & 3 deletions src/Exceptions.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module Exceptions

export @try, HTTPError, ConnectError, TimeoutError, StatusError, RequestError, current_exceptions_to_string
using LoggingExtras
using LoggingExtras, ExceptionUnwrapping
import ..HTTP # for doc references

@eval begin
Expand Down Expand Up @@ -38,6 +38,13 @@ struct ConnectError <: HTTPError
error::Any # underlying error
end

ExceptionUnwrapping.unwrap_exception(e::ConnectError) = e.error

function Base.showerror(io::IO, e::ConnectError)
print(io, "HTTP.ConnectError for url = `$(e.url)`: ")
Base.showerror(io, e.error)
end

"""
HTTP.TimeoutError

Expand Down Expand Up @@ -79,11 +86,21 @@ struct RequestError <: HTTPError
error::Any
end

function current_exceptions_to_string(curr_exc)
ExceptionUnwrapping.unwrap_exception(e::RequestError) = e.error

function Base.showerror(io::IO, e::RequestError)
println(io, "HTTP.RequestError:")
println(io, "HTTP.Request:")
Base.show(io, e.request)
println(io, "Underlying error:")
Base.showerror(io, e.error)
end

function current_exceptions_to_string()
buf = IOBuffer()
println(buf)
println(buf, "\n===========================\nHTTP Error message:\n")
Base.showerror(buf, curr_exc)
Base.display_error(buf, Base.catch_stack())
return String(take!(buf))
end

Expand Down
27 changes: 13 additions & 14 deletions src/clientlayers/ConnectionRequest.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module ConnectionRequest

using URIs, Sockets, Base64, LoggingExtras, ConcurrentUtilities
using URIs, Sockets, Base64, LoggingExtras, ConcurrentUtilities, ExceptionUnwrapping
using MbedTLS: SSLContext, SSLConfig
using OpenSSL: SSLStream
using ..Messages, ..IOExtras, ..Connections, ..Streams, ..Exceptions
Expand Down Expand Up @@ -79,7 +79,7 @@ function connectionlayer(handler)
io = newconnection(IOType, url.host, url.port; readtimeout=readtimeout, kw...)
catch e
if logerrors
err = current_exceptions_to_string(CapturedException(e, catch_backtrace()))
err = current_exceptions_to_string()
@error err type=Symbol("HTTP.ConnectError") method=req.method url=req.url context=req.context logtag=logtag
end
req.context[:connect_errors] = get(req.context, :connect_errors, 0) + 1
Expand Down Expand Up @@ -118,18 +118,17 @@ function connectionlayer(handler)
stream = Stream(req.response, io)
return handler(stream; readtimeout=readtimeout, logerrors=logerrors, logtag=logtag, kw...)
catch e
while true
if e isa CompositeException
e = e.exceptions[1]
elseif e isa TaskFailedException
e = e.task.result
else
break
end
# manually unwrap CompositeException since it's not defined as a "wrapper" exception by ExceptionUnwrapping
while e isa CompositeException
e = e.exceptions[1]
end
root_err = e isa CapturedException ? e.ex : e
root_err = ExceptionUnwrapping.unwrap_exception_to_root(e)
# don't log if it's an HTTPError since we should have already logged it
if logerrors && !(root_err isa HTTPError)
if logerrors && err isa StatusError
err = current_exceptions_to_string()
@error err type=Symbol("HTTP.StatusError") method=req.method url=req.url context=req.context logtag=logtag
end
if logerrors && !ExceptionUnwrapping.has_wrapped_exception(e, HTTPError)
err = current_exceptions_to_string(e)
@error err type=Symbol("HTTP.ConnectionRequest") method=req.method url=req.url context=req.context logtag=logtag
end
Expand All @@ -140,8 +139,8 @@ function connectionlayer(handler)
# idempotency of the request
req.context[:nothingwritten] = true
end
root_err isa HTTPError || throw(RequestError(req, e))
rethrow(e)
root_err isa HTTPError || throw(RequestError(req, root_err))
throw(root_err)
finally
releaseconnection(io, shouldreuse; kw...)
if !shouldreuse
Expand Down
4 changes: 0 additions & 4 deletions src/clientlayers/ExceptionRequest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ function exceptionlayer(handler)
req = res.request
req.context[:status_errors] = get(req.context, :status_errors, 0) + 1
e = StatusError(res.status, req.method, req.target, res)
if logerrors && (timedout === nothing || !timedout[])
err = current_exceptions_to_string(CapturedException(e, catch_backtrace()))
@error err type=Symbol("HTTP.StatusError") method=req.method url=req.url context=req.context logtag=logtag
end
throw(e)
else
return res
Expand Down
2 changes: 1 addition & 1 deletion src/clientlayers/StreamRequest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function streamlayer(stream::Stream; iofunction=nothing, decompress::Union{Nothi
if timedout === nothing || !timedout[]
req.context[:io_errors] = get(req.context, :io_errors, 0) + 1
if logerrors
err = current_exceptions_to_string(CapturedException(e, catch_backtrace()))
err = current_exceptions_to_string()
@error err type=Symbol("HTTP.IOError") method=req.method url=req.url context=req.context logtag=logtag
end
end
Expand Down
2 changes: 1 addition & 1 deletion src/clientlayers/TimeoutRequest.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function timeoutlayer(handler)
req = stream.message.request
req.context[:timeout_errors] = get(req.context, :timeout_errors, 0) + 1
if logerrors
err = current_exceptions_to_string(CapturedException(e, catch_backtrace()))
err = current_exceptions_to_string()
@error err type=Symbol("HTTP.TimeoutError") method=req.method url=req.url context=req.context timeout=readtimeout logtag=logtag
end
e = Exceptions.TimeoutError(readtimeout)
Expand Down
2 changes: 1 addition & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ include(joinpath(dir, "resources/TestRequest.jl"))
"chunking.jl",
"utils.jl",
"client.jl",
"download.jl",
# "download.jl",
"multipart.jl",
"parsemultipart.jl",
"sniff.jl",
Expand Down