Skip to content
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

compatible with HTTP [1.1.0 - 1.6.0) #182

Merged
merged 13 commits into from
Nov 25, 2022
4 changes: 2 additions & 2 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "WebSockets"
uuid = "104b5d7c-a370-577a-8038-80a2059c5097"
version = "1.5.10"
version = "1.6.0"

[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
Expand All @@ -10,7 +10,7 @@ Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"

[compat]
HTTP = "0.8, 0.9, 1"
HTTP = "1.1.0, 1.5"
julia = "0.7, 1"

[extras]
Expand Down
51 changes: 42 additions & 9 deletions src/HTTP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,14 @@ import HTTP:Response, # For upgrade
setstatus, # For upgrade
startwrite, # For upgrade
startread, # For _openstream
handle, # For _servercoroutine
Connection, # For handshaketest
Transaction,# For handshaketest
URI, # For open
Handler, # For WebSocketHandler, ServerWS, error_test
RequestHandlerFunction, # For ServerWS
StatusError, # For open
Servers, # For further imports
Streams, # For further imports
ConnectionPool # For further imports
import HTTP.ConnectionPool:
getrawstream # For _openstream
getrawstream, # For _openstream
Connection # For handshaketest
import HTTP.Streams:
Stream # For is_upgrade, handshaketest
import HTTP.Servers: MbedTLS # For further imports
Expand Down Expand Up @@ -95,9 +91,9 @@ function open(f::Function, url; verbose=false, subprotocol = "", kw...)
"GET", uri, headers;
reuse_limit=0, verbose=verbose ? 2 : 0, kw...)
catch err
if typeof(err) <: HTTP.IOExtras.IOError
throw(WebSocketClosedError(" while open ws|client: $(string(err.e.msg))"))
elseif typeof(err) <: HTTP.StatusError
if typeof(err) <: HTTP.Exceptions.ConnectError
throw(WebSocketClosedError(" while open ws|client: $(string(err.error))"))
elseif typeof(err) <: HTTP.StatusError
return err.response
else
rethrow(err)
Expand Down Expand Up @@ -270,6 +266,43 @@ target(req::Request) = req.target
subprotocol(req::Request) = header(req, "Sec-WebSocket-Protocol")
origin(req::Request) = header(req, "Origin")

#functions which are not present in HTTP 1.0
function handle end

abstract type Handler end

"""
RequestHandler

Abstract type representing objects that handle `HTTP.Request` and return `HTTP.Response` objects.

See `?HTTP.RequestHandlerFunction` for an example of a concrete implementation.
"""
abstract type RequestHandler <: Handler end

"""
StreamHandler

Abstract type representing objects that handle `HTTP.Stream` objects directly.

See `?HTTP.StreamHandlerFunction` for an example of a concrete implementation.
"""
abstract type StreamHandler <: Handler end

"""
RequestHandlerFunction(f)

A function-wrapper type that is a subtype of `RequestHandler`. Takes a single function as an argument
that should be of the form `f(::HTTP.Request) => HTTP.Response`
"""
struct RequestHandlerFunction{F} <: RequestHandler
func::F # func(req)
end

#handle(h::RequestHandlerFunction, req::Request, args...) = h.func(req, args...)
handle(h::RequestHandlerFunction, stream::HTTP.Streams.Stream, args...) = h.func(stream, args...)


"""
WSHandlerFunction(f::Function) <: Handler
The provided argument should be one of the forms
Expand Down
1 change: 1 addition & 0 deletions test/client_listen_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ for i = 1:3
println("Status($(i)): $(status)")
@test 200 == status
close(server)
sleep(1)
end
end

Expand Down
3 changes: 3 additions & 0 deletions test/client_serverWS_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ for i = 1:3
server = startserver(url=ip)
@test 200 == WebSockets.HTTP.request("GET", "http://$SURL:$PORT").status
close(server)
sleep(1)
end
end

Expand All @@ -50,13 +51,15 @@ let
WebSockets.open(initiatingws, "ws://$SURL:$PORT")
close(server)
end
sleep(1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move inside let block? We should not be surprised that a 'sleep' is required. 'yield' would probably be sufficient and not take extra time.


@info "ServerWS: Server side initiates message exchange."
let
server = startserver()
WebSockets.open(echows, "ws://$SURL:$PORT", subprotocol = SUBPROTOCOL)
close(server)
end
sleep(1)

@info "ServerWS: Server side initiates message exchange. Close from within server side handler."
let
Expand Down
9 changes: 7 additions & 2 deletions test/client_server_functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ end
`test_handler` is called by WebSockets inner function `_servercoroutine` for all accepted http requests
that are not upgrades. We don't check what's actually requested.
"""
test_handler(req::HTTP.Request) = HTTP.Response(200, "OK")
function test_handler(stream::HTTP.Streams.Stream)
request = stream.message
request.response = HTTP.Response(200, "OK")
request.response.request = request
write(stream, request.response.body)
end

"""
`test_wshandler` is called by WebSockets inner function
Expand Down Expand Up @@ -134,7 +139,7 @@ function initiatingws(ws::WebSocket; msglengths = MSGLENGTHS, closebeforeexit =
end

test_serverws = WebSockets.ServerWS(
HTTP.RequestHandlerFunction(test_handler),
WebSockets.RequestHandlerFunction(test_handler),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the change. Too many things were called prefixed HTTP. I would also think it's OK to export RequestHandlerFunction and similar, and refer to them wihout module prefix here in the tests.

WebSockets.WSHandlerFunction(test_wshandler))

function startserver(serverws=test_serverws;url=SURL, port=PORT, verbose=false)
Expand Down
6 changes: 3 additions & 3 deletions test/client_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ catch err
global caughterr = err
end
@test typeof(caughterr) <: WebSocketClosedError
@test caughterr.message == " while open ws|client: connect: connection refused (ECONNREFUSED)"
@test caughterr.message == " while open ws|client: Base.IOError(\"connect: connection refused (ECONNREFUSED)\", -4078)"

@info "Try open with unknown scheme."
sleep(1)
Expand Down Expand Up @@ -82,7 +82,7 @@ res = WebSockets.open((_)->nothing, URL);

@info "Open with a ws client handler that throws a domain error."
sleep(1)
@test_throws DomainError WebSockets.open((_)->sqrt(-2), URL);
@test_throws HTTP.Exceptions.RequestError WebSockets.open((_)->sqrt(-2), URL);

@info "Stop the server in morse code."
sleep(1)
Expand All @@ -100,7 +100,7 @@ sethd(resp, "Upgrade" => "websocket")
sethd(resp, "Sec-WebSocket-Accept" => WebSockets.generate_websocket_key(key))
sethd(resp, "Connection" => "Upgrade")
servsock = BufferStream()
s = HTTP.Stream(resp, HTTP.Transaction(HTTP.Connection(servsock)))
s = HTTP.Stream(resp, HTTP.Connection(servsock))
write(servsock, resp)
function dummywsh(dws::WebSockets.WebSocket{BufferStream})
close(dws.socket)
Expand Down
8 changes: 4 additions & 4 deletions test/error_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ try
read(ws_client)
end
catch err
@test typeof(err) <: ErrorException
@test err.msg == "Attempt to read from closed WebSocket|client. First isopen(ws), or use readguarded(ws)!"
@test typeof(err) <: HTTP.Exceptions.RequestError
@test err.error == ErrorException("Attempt to read from closed WebSocket|client. First isopen(ws), or use readguarded(ws)!")
end
sleep(1)

Expand All @@ -85,8 +85,8 @@ try
end
catch err
show(err)
@test typeof(err) <: WebSocketClosedError
@test err.message == " while open ws|client: stream is closed or unusable"
@test typeof(err) <: HTTP.Exceptions.RequestError
@test err.error == Base.IOError("stream is closed or unusable", 0)
end

close(s)
Expand Down
3 changes: 1 addition & 2 deletions test/handshaketest_functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ end
function handshakeresponse(request::HTTP.Request)
buf = BufferStream()
c = HTTP.Connection(buf)
t = HTTP.Transaction(c)
s = HTTP.Stream(request, t)
s = HTTP.Stream(request, c)
WebSockets.upgrade(dummywshandler, s)
close(buf)
takefirstline(buf)
Expand Down
6 changes: 3 additions & 3 deletions test/show_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ end

let kws = [], msgs =[]
ds = DummyStream(IOBuffer(), 0, 0)
for s = 0:9, h in [Base.C_NULL, Ptr{UInt64}(3)]
for s = 0:9, h in [Base.C_NULL, Ptr{Cvoid}(3)]
Copy link

@mind6 mind6 Nov 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just making a note as I study your changes.
WebSockets uses Base.LibuvStream, which depends on Base.LibuvServer, which specifically requires handle to be of type Ptr{Cvoid} after Julia 1.6. Cvoid is aliased to the Nothing type.

ds.handle = h
ds.status = s
kwarg, msg = WebSockets._uv_status_tuple(ds)
Expand Down Expand Up @@ -50,7 +50,7 @@ rm("temptemp")
output = String(take!(io))
@test output == "✓"

ds = DummyStream(IOBuffer(), 0, 0x00000001)
ds = DummyStream(IOBuffer(), 0, Ptr{Cvoid}(1))
io = IOBuffer()
WebSockets._show(io, ds)
# The handle type depends on operating system, skip that
Expand Down Expand Up @@ -222,7 +222,7 @@ let chnlout, sws, sws1, sws2
io = IOBuffer()
show(io, sws)
output = String(take!(io))
@test output == "WebSockets.ServerWS(handler=h(r), wshandler=w(s)).out:Channel{Any}(sz_max:2,sz_curr:2) "
@test output == "WebSockets.ServerWS(handler=h(r), wshandler=w(s)).out:Channel{Any}(2) "
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Base.Channel no longer maintains a sz_curr field, so it only displays sz_max without naming it.


sws1 = WebSockets.ServerWS(h, w)
sws2 = WebSockets.ServerWS(h, w)
Expand Down