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 ...
+
+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