diff --git a/.travis.yml b/.travis.yml index 5be4390..00dd9c9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,8 @@ os: - linux - osx julia: - - 0.4 - 0.5 - - nightly + - 0.6 sudo: false notifications: email: false diff --git a/appveyor.yml b/appveyor.yml index 61c22fb..aa55302 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,12 +1,11 @@ environment: matrix: - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.4/julia-0.4-latest-win32.exe" - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.4/julia-0.4-latest-win64.exe" - - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe" + - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe" - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.5/julia-0.5-latest-win64.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" - - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" - + - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe" + - JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.5/julia-0.5-latest-win32.exe" +# HttpCommon not building yet - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe" +# HttpCommon not building yet - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe" branches: only: - master @@ -18,7 +17,11 @@ notifications: on_build_failure: false on_build_status_changed: false +# Not installing PhantomJS. It is being replaced by chrome headless browser in the long run. install: + - choco install firefox + - choco install googlechrome + - choco install ie11 - ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12" # Download most recent Julia Windows binary - ps: (new-object net.webclient).DownloadFile( @@ -30,8 +33,15 @@ install: build_script: # Need to convert from shallow to complete for Pkg.clone to work - IF EXIST .git\shallow (git fetch --unshallow) - - C:\projects\julia\bin\julia -e "versioninfo(); - Pkg.clone(pwd(), \"WebSockets\"); Pkg.build(\"WebSockets\")" - + - ps: | + Write-Host "This is PowerShell - STDERR output will be red. Redirect! " + $env:PATH+=";C:\projects\julia\bin\" + julia -e 'versioninfo();redirect_stderr(STDOUT);println(STDOUT, \"stdout\"); println(STDERR, \"stderr\")' + julia -e 'redirect_stderr(STDOUT);Pkg.init()' + julia --depwarn=no -e 'redirect_stderr(STDOUT);Pkg.add(\"HttpServer\");using HttpServer;' + julia -e 'redirect_stderr(STDOUT);Pkg.clone(pwd(), \"WebSockets\");Pkg.build(\"WebSockets\")' + Write-Host "This is PowerShell, build script finished." test_script: - - C:\projects\julia\bin\julia -e "Pkg.test(\"WebSockets\")" + - ps: | + Write-Host "This is PS" + julia -e 'redirect_stderr(STDOUT);info(\"Local websockets directory:\", Pkg.dir(\"WebSockets\"));Pkg.test(\"WebSockets\")' \ No newline at end of file diff --git a/test/REQUIRE b/test/REQUIRE new file mode 100644 index 0000000..ffc727b --- /dev/null +++ b/test/REQUIRE @@ -0,0 +1,6 @@ +julia 0.5 +Compat 0.9.5 +HttpCommon 0.0.3 +HttpServer 0.0.4 +Codecs +MbedTLS diff --git a/test/browsertest.html b/test/browsertest.html new file mode 100644 index 0000000..d51ec6b --- /dev/null +++ b/test/browsertest.html @@ -0,0 +1,190 @@ + + + + + WebSockets browsertest.html + + +

Test tries to open some websockets, browser says hello from browser, and continues to echo messages from server.

+

+

  This is ...

+

ws1 Websocket

+

ws2 Websocket: websocket-testprotocol

+

ws3 Websocket: server_denies_protocol

+ + + diff --git a/test/browsertest.jl b/test/browsertest.jl new file mode 100644 index 0000000..cfd3b6e --- /dev/null +++ b/test/browsertest.jl @@ -0,0 +1,60 @@ +# Included in runtests at the end. + +const WEBSOCKETS = Dict{Int, WebSockets.WebSocket}() +const WEBSOCKETS_SUBPROTOCOL = Dict{Int, WebSockets.WebSocket}() +const RECEIVED_WS_MSGS = Dict{Int, Vector{String}}() +global noofresponders = 0 + +include("functions_server.jl") + +server = start_ws_server_async() + +# Give the server 5 seconds to get going. +sleep(5) +info("We waited 5 seconds after starting the server.") +for (ke, va) in WEBSOCKETS + if isopen(va) + info("Somebody opened a websocket during that time. Closing it now.") + close(va) + end +end + +info("This OS is ", Sys.KERNEL) +include("functions_open_browsers.jl") +noofbrowsers = open_all_browsers() +const CLOSEAFTER = Base.Dates.Second(15) +t0 = now() +while now()-t0 < CLOSEAFTER && length(keys(RECEIVED_WS_MSGS)) < noofbrowsers * 2 + sleep(1) +end +info("Received messages on ", length(keys(RECEIVED_WS_MSGS)), " sockets.") +info("Tell the special sockets to initiate a close.") +for (ke, va) in WEBSOCKETS_SUBPROTOCOL + writeto(ke, "YOU hang up!") +end + +sleep(10) +info("Closing down after ", now()-t0, " including 10 seconds pause.") +countstillopen = 0 +for (ke, va) in WEBSOCKETS + if isopen(va) + info(" Websocket to close: ", va) + countstillopen +=1 + close(va) + end +end +sleep(5) +close(server) +server = nothing +info("Openened $noofbrowsers, from which $noofresponders requested the HTML page.") +info("Received messages for each websocket:") +display(RECEIVED_WS_MSGS) +println() +@test countstillopen == noofresponders +countmessages = 0 +for (ke, va) in RECEIVED_WS_MSGS + countmessages += length(va) +end +@test length(keys(RECEIVED_WS_MSGS)) == noofresponders * 2 +@test countmessages == noofresponders * 6 +nothing diff --git a/test/favicon.ico b/test/favicon.ico new file mode 100644 index 0000000..6d1163d Binary files /dev/null and b/test/favicon.ico differ diff --git a/test/functions_log_test.jl b/test/functions_log_test.jl new file mode 100644 index 0000000..54e8dd0 --- /dev/null +++ b/test/functions_log_test.jl @@ -0,0 +1,205 @@ +# Note there is some type piracy going on here. This is not for general use. +# Also, modifying display might be preferable to print. +import HttpServer.Client +import HttpServer.HttpHandler +import HttpServer.Response +import HttpServer.Request +import HttpServer.Server +import HttpCommon.Cookie +import HttpCommon.Request +import HttpCommon.Response +import HttpCommon.STATUS_CODES +import URIParser.URI +import WebSockets.WebSocketHandler +import WebSockets.WebSocket +import Base.print +"Date time group string" +dtg() = Dates.format(now(), "d u yy HH:MM:SS") +""" +Console log with a heading, buffer and no mixing with output from other simultaneous tasks. +We may, of course, be interrupting other tasks like stacktrace output. +""" +function clog(args...) + buf = startbuf() + pwc(buf, dtg(), " ", args...) + lock(STDERR) + print(STDERR, String(take!(buf))) + unlock(STDERR) +end +"Print with 'color' arguments Base.text_colors placed anywhere. Color_normal is set after the call is finished. " +function pwc(io::IO, args...) + for arg in args + if isa(arg,Symbol) + print_color(io, arg) + else + print(io,arg) + end + end + print(io, Base.color_normal) + nothing +end +"Type piracy on dictionaries, not nice normally" +function print(io::IO, headers::Dict{AbstractString, AbstractString}) + for pa in headers + print(io, pa) + end + print(io, "\n") + nothing +end +function print(io::IO, headers::Dict{String, Function}) + for pa in headers + print(io, pa) + end + print(io, "\n") + nothing +end + +"Add color code" +print_color(io::IO, color::Symbol) = print(io, get(Base.text_colors, color, Base.color_normal)) + "Return an IO stream with colored heading and tabs" +function startbuf(prefix = "INFO:") + buf = IOBuffer() + pwc(buf, Base.info_color(), :bold , prefix ) + length(prefix) > 0 && prefix[end]!= "\t" && print(buf, "\t") + buf +end + +"Pair print on a line with tab indent" +function print(io::IO, p::Pair) + print(io, "\t") + isa(p.first,Pair) && print(io, "(") + print(io, p.first) + isa(p.first,Pair) && print(io, ")") + print(io, "\t=> ") + isa(p.second,Pair) && print(io, "(") + print(io, p.second) + isa(p.second,Pair) && print(io, ")") + print(io,"\n") + nothing +end +"Print function name with reference, not nice type piracy.." +function print(io::IO, f::Function) + show(io, f) + print(io, "\t") + mt = typeof(f).name.mt + print(io, mt.defs.func.file) + print(io, ":") + print(io, mt.defs.func.line) + nothing +end +"Print client" +function print(io::IO, client::Client{TCPSocket}) + pwc(io, typeof(client), " ", :bold, client.id,"\n") + pwc(io, :cyan, "\tsock ", :bold, client.sock, "\n") + pwc(io, :cyan, "\tparser ", :bold, client.parser.parser) + nothing +end +"Print response" +function print(io::IO, response:: Response) + pwc(io, :green, "\tResponse status: ", :bold, response.status," ") + pwc(io, :green, get(STATUS_CODES, response.status, "--"), " ") + pwc(io, :green, " Finished: ", :bold, response.finished) + if !isempty(response.headers) + pwc(io, :green, " Headers: ", :bold, response.headers.count) + pwc(io, :green, " Age: ", :bold, Int(response.headers.age)) + pwc(io, :green, "\n", response.headers) + end + if !isempty(response.cookies) + pwc(io, " Cookies: ", :bold, length(response.cookies)) + pwc(io, " Age: ", :bold, Int(response.headers.age)) + pwc("\n", response.cookies) + end + if !isempty(response.data) + printdata(io, response.data, get(response.headers, "Content-Type","image/jpeg")) + end + nothing +end +"Print request" +function print(io::IO, request::Request) + pwc(io, :cyan, :bold, "\tRequest: ", request.method, :normal, " resource ", :bold, request.resource) + pwc(io, :cyan, "\tUri:", :bold, sprint(print, request.uri), "\n") + if !isempty(request.headers) + pwc(io, :cyan, "\tHeaders: ", :bold, request.headers.count, " Age: ", :bold, Int(request.headers.age)) + pwc(io, :cyan, "\n", request.headers) + end + if !isempty(request.data) + printdata(io, request.data, get(request.headers, "Content-type","text/html; charset=utf-8")) + end + nothing +end + +"Prints data, limited length" +function limlen(data) + le= length(data) + if le < 100 + return String(data) + else + # Not sure if this is a safe way to split some types, but String should throw an error. + return "Truncated:\n$(String(data)[1:65]).....$(String(data)[le-29:end])" + end +end + +"Indents and cleans up for printing" +function compact_format(s::String) + replace(s, "\r\n", "\n") |> + s-> replace(s, "\n\n", "\n") |> + s -> replace(s, "\t\t", "\t") |> + s -> replace(s, "\n\t", "\n") |> + s -> replace(s, "\n", "\n\t") |> + s-> startswith(s,"\t") ? s : "\t"*s |> + s-> endswith(s,"\n") ? s : s*"\n" +end +"Text data as a (truncated) repl-friendly string ." +function printdata{T<:Array{UInt8,1}}(io::IO, data::T, contenttype::String) + le = length(data) + pwc(io, :green, "\tData length: ", le,"\n") + if ismatch(r"^text", contenttype) + pwc(io, :blue, data|> prettystring) + end +end +"Text data as a (truncated) repl-friendly string ." +function prettystring(s::String) + s|> limlen |> compact_format +end +prettystring(u::Array{UInt8,1}) = prettystring(String(u)) +"Print server" +function print(io::IO, server::Server) + pwc(io, :bold , Base.info_color(), "Server\n") + server.http != nothing && print(io, server.http) + server.websock != nothing && print(io, server.websock) +end +"Print httphandler" +function print(io::IO, httphandler::HttpHandler) + pwc(io, :bold , Base.info_color(), "\tHttpHandler\n") + pwc(io, :blue, :bold, "\t", :normal, "Called on opening (websocket, response):\n") + pwc(io, :green, "\t", httphandler.handle, "\n") + pwc(io, " \t", "Functions called on events:\n") + pwc(io, :green, httphandler.events) + pwc(io, " \t", "Socket:\t", :green, httphandler.sock,"\n") + nothing +end +""" +Print websockethandler +""" +function print(io::IO, wsh::WebSocketHandler) + try + pwc(io, :bold , Base.info_color(), "\tWebSocketHandler") + pwc(io, :blue, :bold, "\t", :normal, "Function called with (request, client):\n") + pwc(io, :green, "\t", wsh.handle, "\n") + end + nothing +end + + +""" +Print websocket +""" +function print(io::IO, ws::WebSocket) + try + pwc(io, :green, "\t", :normal, "Websocket id: ", :bold, ws.id) + pwc(io, :green, :bold, "\t", ws.state ) + pwc(io, "\n") + end + nothing +end +nothing diff --git a/test/functions_open_browsers.jl b/test/functions_open_browsers.jl new file mode 100644 index 0000000..54222ff --- /dev/null +++ b/test/functions_open_browsers.jl @@ -0,0 +1,137 @@ +# Included in runtests at the end. +"Get application path for developer applications" +function fwhich(s) + fi = "" + if Sys.is_windows() + try + fi = split(readstring(`where.exe $s`), "\r\n")[1] + if !isfile(fi) + fi = "" + end + catch + fi ="" + end + else + try + fi = readchomp(`which $s`) + catch + fi ="" + end + end + fi +end +function browser_path_unix_apple(shortname) + trypath = "" + if shortname == "chrome" + if Base.Sys.is_apple() + return "Google Chrome" + else + return "google-chrome" + end + end + if shortname == "firefox" + return "firefox" + end + if shortname == "safari" + if Base.Sys.is_apple() + return "safari" + else + return "" + end + end + if shortname == "phantomjs" + return fwhich(shortname) + end + return "" +end +function browser_path_windows(shortname) + # windows accepts english paths anywhere. + # Forward slash acceptable too. + # Searches C and D drives.... + trypath = "" + homdr = ENV["HOMEDRIVE"] + path32 = homdr * "/Program Files (x86)/" + path64 = homdr * "/Program Files/" + if shortname == "chrome" + trypath = path64 * "Chrome/Application/chrome.exe" + isfile(trypath) && return trypath + trypath = path32 * "Google/Chrome/Application/chrome.exe" + isfile(trypath) && return trypath + end + if shortname == "firefox" + trypath = path64 * "Mozilla Firefox/firefox.exe" + isfile(trypath) && return trypath + trypath = path32 * "Mozilla Firefox/firefox.exe" + isfile(trypath) && return trypath + end + if shortname == "safari" + trypath = path64 * "Safari/Safari.exe" + isfile(trypath) && return trypath + trypath = path32 * "Safari/Safari.exe" + isfile(trypath) && return trypath + end + if shortname == "iexplore" + trypath = path64 * "Internet Explorer/iexplore.exe" + isfile(trypath) && return trypath + trypath = path32 * "Internet Explorer/iexplore.exe" + isfile(trypath) && return trypath + end + if shortname == "phantomjs" + return fwhich(shortname) + end + return "" +end +function launch_command(shortbrowsername) + url = "http://localhost:8080/browsertest.html" + if Sys.is_windows() + pt = browser_path_windows(shortbrowsername) + else + pt = browser_path_unix_apple(shortbrowsername) + end + pt == "" && return `` + if shortbrowsername == "iexplore" + prsw = "-private" + else + prsw = "--incognito" + end + if shortbrowsername == "phantomjs" + return Cmd(`$pt phantom.js $url`) + else + if Sys.is_windows() + Cmd( [ pt, url , prsw]) + else + if Sys.is_apple() + return Cmd(`open --fresh -n $url -a $pt --args $prsw`) + elseif Sys.is_linux() || Sys.is_bsd() + return Cmd(`xdg-open $(url) $pt`) + end + end + end +end + + +function open_testpage(shortbrowsername) + dmc = launch_command(shortbrowsername) + if dmc == `` + info("\tCould not find " * shortbrowsername) + return false + else + try + spawn(dmc) + catch + info("\tFailed to spawn " * shortbrowsername) + return false + end + end + return true +end +function open_all_browsers() + info("Try to open browsers") + brs = ["chrome", "firefox", "iexplore", "safari", "phantomjs"] + openbrowsers = 0 + for b in brs + openbrowsers += open_testpage(b) + end + info("Out of google chrome, firefox, iexplore, safari and phantomjs, tried to spawn ", openbrowsers) + openbrowsers +end diff --git a/test/functions_server.jl b/test/functions_server.jl new file mode 100644 index 0000000..406750e --- /dev/null +++ b/test/functions_server.jl @@ -0,0 +1,78 @@ +include("functions_log_test.jl") +include("handler_functions_events.jl") +include("handler_functions_websockets_general_test.jl") +include("handler_functions_websockets_subprotocol_test.jl") +""" +Although a websocket could be opened from a file on any server or just from the browser +working on the file system, we open a test http server. +The term 'handler' is relative to point of view. We make a hierachy of functions and then give +the types defined in HttpServer references to the functions. More commonly, anonymous functions +are used. +""" +function httphandle(request::Request, response::Response) + global noofresponders + id = "server_functions.httphandle\t" + clog(id, :cyan, request, "\n") + if request.method=="GET" + if request.resource =="/favicon.ico" + response = HttpServer.FileResponse(joinpath(@__DIR__, "favicon.ico")) + else + response = HttpServer.FileResponse(joinpath(@__DIR__, "browsertest.html")) + noofresponders += 1 + end + else + response = Response(404, "$id, unexpected request.") + end + clog(id, response, "\n") + return response +end + +""" +Inner function for WebsocketHandler. Called on opening a new websocket after +the handshake procedure is finished. +The request contains info which can be used for additional delegation or gatekeeping. +Function never exits until the websocket is closed, but calls are made asyncronously. +""" +function websockethandle(wsrequest::Request, websocket::WebSocket) + id = "server_functions.websockethandle\t" + clog(id, :cyan, wsrequest, "\t", :yellow, websocket, "\n") + if haskey(wsrequest.headers,"Sec-WebSocket-Protocol") + clog(id, "subprotocol spec: \n\t\t\t", + :yellow, "Sec-WebSocket-Protocol => ", wsrequest.headers["Sec-WebSocket-Protocol"],"\n") + if wsrequest.headers["Sec-WebSocket-Protocol"] == "websocket-testprotocol" + clog(id, "Websocket-testprotocol, calling handler\n") + ws_test_protocol(websocket) + clog(id, "Websocket-testprotocol, exiting handler\n") + else + clog(id, :red, "Unknown sub protocol let through WebSockets.jl, not responding further. \n") + end + else + clog(id, "General websocket, calling handler\n") + ws_general(websocket) + clog(id, "General websocket, exiting handler\n") + end + clog(id, "Exiting \n") + nothing +end + + +function start_ws_server_async() + id = "server_functions.start_ws_server\t" + # Specify this subprotocol is to be let through to websockethandle: + WebSockets.addsubproto("websocket-testprotocol") + # Tell HttpHandler which functions to spawn when something happens. + httpha = HttpHandler(httphandle) + httpha.events["error"] = ev_error + httpha.events["listen"] = ev_listen + httpha.events["connect"] = ev_connect + httpha.events["close"] = ev_close + httpha.events["write"] = ev_write + httpha.events["reset"] = ev_reset + # Pack the websockethandle function in an interface container + wsh = WebSocketHandler(websockethandle) + server = Server(httpha, wsh ) + clog(id, "Server to be started:\n", server ) + servertask = @async run( server, 8080) + clog(id, "Server listening on localhost:8080\n") + server +end diff --git a/test/handler_functions_events.jl b/test/handler_functions_events.jl new file mode 100644 index 0000000..3cd2ad3 --- /dev/null +++ b/test/handler_functions_events.jl @@ -0,0 +1,25 @@ +# Provides extra verbose output for testing +function ev_error(client::Client, args...) + id = "events.ev_error\t" + clog(id, :yellow, "client $client ", :normal, " ", args..., "\n") +end +function ev_listen(port, args...) + id = "events.ev_listen\t" + #clog(id, :yellow, " from port ", :bold, port , :normal, " ", args..., " that was all I think. \n") +end +function ev_connect(client::Client, args...) + id = "events.ev_connect\t" + clog(id, :yellow, " from client ", :bold, client , :normal, " ", args..., "\n") +end +function ev_close(client::Client, args...) + id = "events.ev_close\t" + clog(id, :yellow, " from client ", :bold, client , :normal, " ", args..., "\n") +end +function ev_write(client::Client, response::Response) + id = "events.ev_write\t" + #clog(id, "to ",:yellow, client , :bold, client , :normal, "\n", response, "\n") +end +function ev_reset(client::Client, response::Response) + id = "events.ev_reset\t" + clog(id, :yellow, " client ", :bold, client , :normal, "\n", response, "\n") +end diff --git a/test/handler_functions_websockets_general_test.jl b/test/handler_functions_websockets_general_test.jl new file mode 100644 index 0000000..9448eea --- /dev/null +++ b/test/handler_functions_websockets_general_test.jl @@ -0,0 +1,57 @@ +# These functions deal with unspecified websocket protocols. +# Included in server_functions.jl + + +function ws_general(ws::WebSockets.WebSocket) + id = "ws_general #$(ws.id)\t" + WEBSOCKETS[ws.id] = ws + RECEIVED_WS_MSGS[ws.id] = String[] + clog(id, " Entering to read websocket.\n") + stri = wsmsg_general(ws) + clog(id, " Send a message\n") + push!(RECEIVED_WS_MSGS[ws.id], stri) + writeto(ws.id, "Here, have a message!") + clog(id, " Continue to listen for message\n") + while isopen(ws) + stri = wsmsg_general(ws) + push!(RECEIVED_WS_MSGS[ws.id], stri) + end + clog(id, "Websocket was closed; exiting read loop\n") + nothing +end + +function writeto(wsid::Int, message) + if haskey(WEBSOCKETS, wsid) + write(WEBSOCKETS[wsid], message) + end +end + +""" +Listens for the next websocket message +""" +function wsmsg_general(ws::WebSockets.WebSocket) + id = "wsmsg_general #$(ws.id)\t" + clog(id, " Entering to read websocket.\n") + stri = "" + try + # Holding here. Cleanup code could work with InterruptException and Base.throwto. + # This is not considered necessary for the test (and doesn't really work well). + # So, this may continue running after the test is finished. + stri = ws|> read |> String + clog(id, "Received: \n", :yellow, :bold, stri, "\n") + # push! + catch e + if typeof(e) == WebSockets.WebSocketClosedError + clog(id, :green, " Websocket was or is being closed.\n") + else + clog(id, "Caught exception..\n", "\t\t", :red, e, "\n") + if isopen(ws) + clog(id, "Closing\n") + close(ws) + end + end + end + + clog(id, "Exiting\n") + return stri +end diff --git a/test/handler_functions_websockets_subprotocol_test.jl b/test/handler_functions_websockets_subprotocol_test.jl new file mode 100644 index 0000000..eb1801c --- /dev/null +++ b/test/handler_functions_websockets_subprotocol_test.jl @@ -0,0 +1,54 @@ +# These functions deal with specified websocket protocols. +# Included in server_functions.jl + +function ws_test_protocol(ws::WebSockets.WebSocket) + id = "ws_test_protocol #$(ws.id)\t" + WEBSOCKETS[ws.id] = ws + WEBSOCKETS_SUBPROTOCOL[ws.id] = ws + RECEIVED_WS_MSGS[ws.id] = String[] + clog(id, " Entering to read websocket.\n") + stri = wsmsg_general(ws) + clog(id, " Send a message\n") + push!(RECEIVED_WS_MSGS[ws.id], stri) + writeto(ws.id, "Here, have a message!") + clog(id, " Continue to listen for message\n") + while isopen(ws) + stri = wsmsg_testprotocol(ws) + push!(RECEIVED_WS_MSGS[ws.id], stri) + end + clog(id, "Websocket was closed; exiting read loop\n") + nothing +end + +""" +Listens for the next websocket message. +""" +function wsmsg_testprotocol(ws::WebSockets.WebSocket) + id = "wsmsg_testprotocol #$(ws.id)\t" + clog(id, " Entering to read websocket.\n") + stri = "" + try + # Holding here. Cleanup code could work with InterruptException and Base.throwto. + # This is not considered necessary for the test (and doesn't really work well). + # So, this may continue running after the test is finished. + stri = ws|> read |> String + clog(id, "Received: \n", :yellow, :bold, stri, "\n") + catch e + if typeof(e) == WebSockets.WebSocketClosedError + clog(id, :green, " Websocket was or is being closed.\n") + else + clog(id, "Caught exception..\n", "\t\t", :red, e, "\n") + if isopen(ws) + clog(id, "Closing\n") + close(ws) + end + end + end + clog(id, "Exiting\n") + return stri +end + + + + +return nothing diff --git a/test/phantom.js b/test/phantom.js new file mode 100644 index 0000000..09aaf58 --- /dev/null +++ b/test/phantom.js @@ -0,0 +1,34 @@ +// Note that the console log output does not appear in Julia REPL when +// this is called using spawn. For debugging, use "run". These functions +// are run in a shell outside the web page. +var page = require('webpage').create(), + system = require('system'), + t, address; + +if (system.args.length === 1) { + console.log('Phantomjs.js: too few arguments. Need phantom.js '); + phantom.exit(); +} +console.log('PhantomJS: The default user agent is ' + page.settings.userAgent); +page.settings.userAgent = 'PhantomJS'; +console.log('PhantomJS: User agent set to ' + page.settings.userAgent); + + +t = Date.now(); +address = system.args[1]; +page.open(address, function(status) { + if (status !== 'success') { + console.log('FAIL to load the address'); + } else { + t = Date.now() - t; + console.log('PhantomJS loading ' + system.args[1]); + console.log('PhantomJS loading time ' + t + ' msec'); + window.setTimeout( (function() { + page.render("phantomjs.png"); + console.log("PhantomJS saved render, exits after 30s") + phantom.exit() + }), + 30000) + } + } +); diff --git a/test/runtests.jl b/test/runtests.jl index c7905c7..ff25684 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -200,4 +200,5 @@ for request in [chromerequest, firefoxrequest] push!(request.headers, "Sec-WebSocket-Protocol" => "my.server/json-zmq") @test handshakeresponse(request) == SWITCH end -close(io) \ No newline at end of file +close(io) +include("browsertest.jl") \ No newline at end of file