diff --git a/Project.toml b/Project.toml index dd85c48d..40a04a52 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ AssetRegistry = "bf4720bc-e11a-5d0c-854e-bdca1663c893" Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" FunctionalCollections = "de31a74c-ac4f-5751-b3fd-e18cd04993ca" +HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" Observables = "510215fc-4207-5dde-b226-833fc4488ee2" @@ -16,17 +17,16 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Requires = "ae029012-a4dd-5104-9daa-d747884805df" Sockets = "6462fe0b-24de-5631-8697-dd941f90decc" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" -WebSockets = "104b5d7c-a370-577a-8038-80a2059c5097" Widgets = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62" [compat] AssetRegistry = "0.1.0" FunctionalCollections = "0.5.0" +HTTP = "1" JSExpr = "0.5" JSON = "0.18, 0.19, 0.20, 0.21" Observables = "0.5" Requires = "0.4.4, 0.5, 1.0.0" -WebSockets = "1.5.0" Widgets = "0.6.2" julia = "0.7, 1" diff --git a/src/WebIO.jl b/src/WebIO.jl index 35fd2441..27b950fe 100644 --- a/src/WebIO.jl +++ b/src/WebIO.jl @@ -119,7 +119,7 @@ function __init__() @require IJulia="7073ff75-c697-5162-941a-fcdaad2a7d2a" begin include_string(@__MODULE__, provider_ijulia.code, provider_ijulia.file) end - @require WebSockets="104b5d7c-a370-577a-8038-80a2059c5097" begin + @require HTTP="cd3eb016-35fb-5094-929b-558a96fad6f3" begin include_string(@__MODULE__, provider_generic_http.code, provider_generic_http.file) end diff --git a/src/providers/generic_http.jl b/src/providers/generic_http.jl index def98ed9..03f26421 100644 --- a/src/providers/generic_http.jl +++ b/src/providers/generic_http.jl @@ -1,16 +1,16 @@ using .Sockets import .AssetRegistry, .JSON using .WebIO -using .WebSockets: is_upgrade, upgrade, writeguarded -using .WebSockets: HTTP +import .HTTP +import .HTTP.WebSockets -struct WSConnection{T} <: WebIO.AbstractConnection - sock::T +struct WSConnection <: WebIO.AbstractConnection + stream::HTTP.WebSocket end -Sockets.send(p::WSConnection, data) = writeguarded(p.sock, JSON.json(data)) -Base.isopen(p::WSConnection) = isopen(p.sock) +Sockets.send(p::WSConnection, data) = HTTP.send(p.stream, JSON.json(data)) +Base.isopen(p::WSConnection) = !HTTP.WebSockets.isclosed(p.stream) if !isfile(GENERIC_HTTP_BUNDLE_PATH) error( @@ -25,7 +25,7 @@ include(joinpath(@__DIR__, "..", "..", "deps", "mimetypes.jl")) """ Serve an asset from the asset registry. """ -function serve_assets(req) +function serve_assets(req::HTTP.Request) if haskey(AssetRegistry.registry, req.target) filepath = AssetRegistry.registry[req.target] if isfile(filepath) @@ -42,22 +42,23 @@ function serve_assets(req) return HTTP.Response(404) end -function websocket_handler(ws) +function websocket_handler(ws::HTTP.WebSocket) conn = WSConnection(ws) - while isopen(ws) - data, success = WebSockets.readguarded(ws) - !success && break + for data in ws msg = JSON.parse(String(data)) WebIO.dispatch(conn, msg) end end -struct WebIOServer{S} - server::S - serve_task::Task +struct WebIOServer + server::HTTP.Server end -kill!(server::WebIOServer) = put!(server.server.in, HTTP.Servers.KILL) + +function kill!(s::WebIOServer) + close(s.server) + wait(s.server) +end const singleton_instance = Ref{WebIOServer}() @@ -93,17 +94,40 @@ function WebIOServer( ) # TODO test if actually still running, otherwise restart even if singleton if !singleton || !isassigned(singleton_instance) - function handler(req) + + function handler(req::HTTP.Request) response = default_response(req) response !== missing && return response return serve_assets(req) end - function wshandler(req, sock) - req.target == websocket_route && websocket_handler(sock) + + function wshandler(req::HTTP.Request) + if req.target == websocket_route + HTTP.WebSockets.upgrade(http) do clientstream + if !HTTP.WebSockets.isclosed(clientstream) + websocket_handler(clientstream) + end + end + end + end + + server = HTTP.listen!(baseurl, http_port; stream = true, server_kw_args...) do http::HTTP.Stream + if HTTP.WebSockets.isupgrade(http.message) + wshandler(request) + else + request::HTTP.Request = http.message + request.body = read(http) + + response = handler(request) + response.request = request + + HTTP.startwrite(http) + write(http, response.body) + HTTP.closewrite(http) + end end - server = WebSockets.ServerWS(handler, wshandler; server_kw_args...) - server_task = @async WebSockets.serve(server, baseurl, http_port, verbose) - singleton_instance[] = WebIOServer(server, server_task) + + singleton_instance[] = WebIOServer(server) bundle_url = get(ENV, "WEBIO_BUNDLE_URL") do webio_base = WebIO.baseurl[] base = if startswith(webio_base, "http") # absolute url @@ -117,7 +141,7 @@ function WebIOServer( while time() - start < wait_time # Block as long as our server doesn't actually serve the bundle try - resp = WebSockets.HTTP.get(bundle_url) + resp = HTTP.get(bundle_url) resp.status == 200 && break sleep(0.1) catch e diff --git a/test/http-tests.jl b/test/http-tests.jl index bd6ea73f..77d03a2b 100644 --- a/test/http-tests.jl +++ b/test/http-tests.jl @@ -1,5 +1,5 @@ using WebIO -using WebSockets +using HTTP using Test @testset "HTTP provider" begin