Skip to content

Commit

Permalink
Non-blocking API Pluto.run! that returns a reference to the running s…
Browse files Browse the repository at this point in the history
…erver (#2643)
  • Loading branch information
fonsp authored Sep 13, 2023
1 parent b8d8af7 commit bd6703e
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 40 deletions.
47 changes: 32 additions & 15 deletions src/webserver/WebServer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,43 @@ function port_serversocket(hostIP::Sockets.IPAddr, favourite_port, port_hint)
return port, serversocket
end

struct RunningPlutoServer
http_server
initial_registry_update_task::Task
end

function Base.close(ssc::RunningPlutoServer)
close(ssc.http_server)
wait(ssc.http_server)
wait(ssc.initial_registry_update_task)
end

function Base.wait(ssc::RunningPlutoServer)
try
# create blocking call and switch the scheduler back to the server task, so that interrupts land there
while isopen(ssc.http_server)
sleep(.1)
end
catch e
println()
println()
Base.close(ssc)
(e isa InterruptException) || rethrow(e)
end

nothing
end

"""
run(session::ServerSession)
Specifiy the [`Pluto.ServerSession`](@ref) to run the web server on, which includes the configuration. Passing a session as argument allows you to start the web server with some notebooks already running. See [`SessionActions`](@ref) to learn more about manipulating a `ServerSession`.
"""
function run(session::ServerSession)
Base.wait(run!(session))
end

function run!(session::ServerSession)
if is_first_run[]
is_first_run[] = false
@info "Loading..."
Expand Down Expand Up @@ -293,21 +324,7 @@ function run(session::ServerSession)
will_update && println(" Updating registry done ✓")
end

try
# create blocking call and switch the scheduler back to the server task, so that interrupts land there
while isopen(server)
sleep(.1)
end
catch e
println()
println()
close(server)
wait(server)
wait(initial_registry_update_task)
(e isa InterruptException) || rethrow(e)
end

nothing
return RunningPlutoServer(server, initial_registry_update_task)
end
precompile(run, (ServerSession, HTTP.Handlers.Router{Symbol("##001")}))

Expand Down
5 changes: 2 additions & 3 deletions test/Configuration.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ end
host = 🍭.options.server.host
secret = 🍭.secret
println("Launching test server...")
server_task = @async Pluto.run(🍭)
sleep(2)
server = Pluto.run!(🍭)

local_url(suffix) = "http://$host:$port/$suffix"
withsecret(url) = occursin('?', url) ? "$url&secret=$secret" : "$url?secret=$secret"
Expand Down Expand Up @@ -123,7 +122,7 @@ end
@test requeststatus(url, method) 200:399 # 3xx are redirects
end

@async schedule(server_task, InterruptException(); error=true)
close(server)
end

@testset "disable mimetype via workspace_custom_startup_expr" begin
Expand Down
39 changes: 17 additions & 22 deletions test/webserver.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,21 @@ using Pluto.WorkspaceManager: WorkspaceManager, poll
base_url,
)
🍭 = Pluto.ServerSession(; options)
server_task = @async Pluto.run(🍭)
server = Pluto.run!(🍭)

# FYI, you should normally use a PlutoEvent for things we do in this test instead of polling! Don't use this as an example.
@test poll(10) do
server_running()
end
@test server_running()

sleep(20)
@test HTTP.get("http://$host:$port/edit"; status_exception=false).status == 404
sleep(3)
@test poll(20) do
# should not exist because of the base url setting
HTTP.get("http://$host:$port/edit"; status_exception=false).status == 404
end

for notebook in values(🍭.notebooks)
SessionActions.shutdown(🍭, notebook; keep_in_session=false)
end

schedule(server_task, InterruptException(); error=true)

# wait for the server task to finish
# normally this `wait` would rethrow the above InterruptException, but Pluto.run should catch for InterruptExceptions and not bubble them up.
wait(server_task)
close(server)
end

@testset "Exports" begin
Expand All @@ -69,14 +65,16 @@ end


# without notebook at startup
options = Pluto.Configuration.from_flat_kwargs(; port, launch_browser=false, workspace_use_distributed=true, require_secret_for_access=false, require_secret_for_open_links=false)
options = Pluto.Configuration.from_flat_kwargs(;
port, launch_browser=false,
workspace_use_distributed=true,
require_secret_for_access=false,
require_secret_for_open_links=false
)
🍭 = Pluto.ServerSession(; options)
server_task = @async Pluto.run(🍭)
server = Pluto.run!(🍭)

# FYI, you should normally use a PlutoEvent for things we do in this test instead of polling! Don't use this as an example.
@test poll(10) do
server_running()
end
@test server_running()

@test isempty(🍭.notebooks)

Expand Down Expand Up @@ -112,10 +110,7 @@ end
SessionActions.shutdown(🍭, notebook; keep_in_session=false)
end

schedule(server_task, InterruptException(); error=true)
# wait for the server task to finish
# normally this `wait` would rethrow the above InterruptException, but Pluto.run should catch for InterruptExceptions and not bubble them up.
wait(server_task)
close(server)
end

end # testset

0 comments on commit bd6703e

Please sign in to comment.