diff --git a/.travis.yml b/.travis.yml index 00dd9c9..ea5faa0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ os: - linux - osx julia: - - 0.5 - 0.6 sudo: false notifications: diff --git a/appveyor.yml b/appveyor.yml index aa55302..75c64a0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,7 @@ environment: matrix: - 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://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: diff --git a/examples/chat-client.html b/examples/chat-client.html index 5ae1c61..f1847f8 100644 --- a/examples/chat-client.html +++ b/examples/chat-client.html @@ -65,6 +65,14 @@

Select a userinput

var $chatForm = document.querySelector("#say_message"); var $chatInput = $chatForm.querySelector("input[name=say]"); + + function uuid4() { + return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => + (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) + ) + } + var id = uuid4(); + function addContent(html) { var $content = document.querySelector("#content"); var div = document.createElement("div"); @@ -86,7 +94,11 @@

Select a userinput

if( !uname.replace(/\s/gi,'').length ) { alert("Please select a valid userinput"); } else { - connection.send('setusername:'+ uname); + var msg = { + "id": id, + "userName": uname + }; + connection.send(JSON.stringify(msg)); $userName.innerHTML = uname; $welcome.style.display = "none"; $chat.style.display = "block"; @@ -94,12 +106,16 @@

Select a userinput

} function whenChatMessage() { - var msg = $chatInput.value; - if(!msg.replace(/\s/gi,'').length) { + var content = $chatInput.value; + if( !content.replace(/\s/gi,'').length) { /* nothing to do */ } else { - connection.send('say:'+ msg); - addContent(`

${you}: ${msg}

`); + var msg = { + "id": id, + "say": content + }; + connection.send(JSON.stringify(msg)); + addContent(`

${you}: ${content}

`); $chatInput.focus(); } } @@ -110,19 +126,14 @@

Select a userinput

whenChatMessage(); return false; }) - - $chatInput.addEventListener("keypress", (e) => { - if( e.keyCode === 13 ) { whenChatMessage(); } ; - return false; - }, false) - + $userForm.addEventListener("submit", function(e){ e.preventDefault(); e.stopImmediatePropagation(); whenUserName(); return false; }); - + const connection = new SocketConnection(onMessageReceived); connection.start(); diff --git a/examples/chat.jl b/examples/chat.jl index 8983e4d..dfaa307 100644 --- a/examples/chat.jl +++ b/examples/chat.jl @@ -1,38 +1,44 @@ using HttpServer using WebSockets +using JSON +struct User + name::String + client::WebSocket +end #global Dict to store open connections in -global connections = Dict{Int,WebSocket}() -global usernames = Dict{Int,String}() +global connections = Dict{String,User}() function decodeMessage( msg ) - String(copy(msg)) + JSON.parse(String(copy(msg))) end wsh = WebSocketHandler() do req, client global connections - @show connections[client.id] = client while true msg = read(client) msg = decodeMessage(msg) - if startswith(msg, "setusername:") - println("SETTING USERNAME: $msg") - usernames[client.id] = msg[13:end] + id = msg["id"] + if haskey(msg,"userName") && !haskey(connections,id) + uname = msg["userName"] + println("SETTING USERNAME: $(uname)") + connections[id] = User(uname,client) end - if startswith(msg, "say:") - println("EMITTING MESSAGE: $msg") + if haskey(msg,"say") + content = msg["say"] + println("EMITTING MESSAGE: $(content)") for (k,v) in connections - if k != client.id - write(v, (usernames[client.id] * ": " * msg[5:end])) + if k != id + write(v.client, (v.name * ": " * content)) end end end end end -onepage = readstring(Pkg.dir("WebSockets","examples","chat-client.html")) httph = HttpHandler() do req::Request, res::Response - Response(onepage) + onepage = readstring(Pkg.dir("WebSockets","examples","chat-client.html")) + Response(onepage) end server = Server(httph, wsh) diff --git a/examples/server.jl b/examples/server.jl index 0cf883b..78ac7da 100644 --- a/examples/server.jl +++ b/examples/server.jl @@ -1,10 +1,6 @@ using HttpServer using WebSockets -#global Dict to store open connections in -global connections = Dict{Int,WebSocket}() -global usernames = Dict{Int,String}() - function decodeMessage( msg ) String(copy(msg)) end @@ -19,8 +15,6 @@ function eval_or_describe_error(strmsg) end wsh = WebSocketHandler() do req, client - global connections - connections[client.id] = client while true val = client |> read |> decodeMessage |> eval_or_describe_error output = String(take!(Base.mystreamvar)) diff --git a/src/HTTP.jl b/src/HTTP.jl new file mode 100644 index 0000000..f713c42 --- /dev/null +++ b/src/HTTP.jl @@ -0,0 +1,76 @@ +info("Loading HTTP methods...") + +function open(f::Function, url; binary=false, verbose=false, kw...) + + key = base64encode(rand(UInt8, 16)) + + headers = [ + "Upgrade" => "websocket", + "Connection" => "Upgrade", + "Sec-WebSocket-Key" => key, + "Sec-WebSocket-Version" => "13" + ] + + HTTP.open("GET", url, headers; + reuse_limit=0, verbose=verbose ? 2 : 0, kw...) do http + + HTTP.startread(http) + + status = http.message.status + if status != 101 + return + end + + check_upgrade(http) + + if HTTP.header(http, "Sec-WebSocket-Accept") != generate_websocket_key(key) + throw(WebSocketError(0, "Invalid Sec-WebSocket-Accept\n" * + "$(http.message)")) + end + + io = HTTP.ConnectionPool.getrawstream(http) + f(WebSocket(io,false)) + end +end + +function upgrade(f::Function, http::HTTP.Stream; binary=false) + + check_upgrade(http) + if !HTTP.hasheader(http, "Sec-WebSocket-Version", "13") + throw(WebSocketError(0, "Expected \"Sec-WebSocket-Version: 13\"!\n$(http.message)")) + end + + HTTP.setstatus(http, 101) + HTTP.setheader(http, "Upgrade" => "websocket") + HTTP.setheader(http, "Connection" => "Upgrade") + key = HTTP.header(http, "Sec-WebSocket-Key") + HTTP.setheader(http, "Sec-WebSocket-Accept" => generate_websocket_key(key)) + + HTTP.startwrite(http) + + io = HTTP.ConnectionPool.getrawstream(http) + f(WebSocket(io, true)) +end + +function check_upgrade(http) + if !HTTP.hasheader(http, "Upgrade", "websocket") + throw(WebSocketError(0, "Expected \"Upgrade: websocket\"!\n$(http.message)")) + end + + if !HTTP.hasheader(http, "Connection", "upgrade") + throw(WebSocketError(0, "Expected \"Connection: upgrade\"!\n$(http.message)")) + end +end + +function is_upgrade(r::HTTP.Message) + (r isa HTTP.Request && r.method == "GET" || r.status == 101) && + HTTP.hasheader(r, "Connection", "upgrade") && + HTTP.hasheader(r, "Upgrade", "websocket") +end + +# function listen(f::Function, host::String="localhost", port::UInt16=UInt16(8081); binary=false, verbose=false) +# HTTP.listen(host, port; verbose=verbose) do http +# upgrade(f, http; binary=binary) +# end +# end + diff --git a/src/HttpServer.jl b/src/HttpServer.jl new file mode 100644 index 0000000..6d8523f --- /dev/null +++ b/src/HttpServer.jl @@ -0,0 +1,74 @@ +info("Loading HttpServer methods...") + +export WebSocketHandler + + +""" +Responds to a WebSocket handshake request. +Checks for required headers and subprotocols; sends Response(400) if they're missing or bad. Otherwise, transforms client key into accept value, and sends Reponse(101). +Function returns true for accepted handshakes. +""" +function websocket_handshake(request,client) + if !haskey(request.headers, "Sec-WebSocket-Key") + Base.write(client.sock, HttpServer.Response(400)) + return false + end + if get(request.headers, "Sec-WebSocket-Version", "13") != "13" + response = HttpServer.Response(400) + response.headers["Sec-WebSocket-Version"] = "13" + Base.write(client.sock, response) + return false + end + + key = request.headers["Sec-WebSocket-Key"] + if length(base64decode(key)) != 16 # Key must be 16 bytes + Base.write(client.sock, HttpServer.Response(400)) + return false + end + resp_key = generate_websocket_key(key) + + response = HttpServer.Response(101) + response.headers["Upgrade"] = "websocket" + response.headers["Connection"] = "Upgrade" + response.headers["Sec-WebSocket-Accept"] = resp_key + + if haskey(request.headers, "Sec-WebSocket-Protocol") + if hasprotocol(request.headers["Sec-WebSocket-Protocol"]) + response.headers["Sec-WebSocket-Protocol"] = request.headers["Sec-WebSocket-Protocol"] + else + Base.write(client.sock, HttpServer.Response(400)) + return false + end + end + + Base.write(client.sock, response) + return true +end + +""" Implement the WebSocketInterface, for compatilibility with HttpServer.""" +struct WebSocketHandler <: HttpServer.WebSocketInterface + handle::Function +end + +""" +Performs handshake. If successfull, establishes WebSocket type and calls +handler with the WebSocket and the original request. On exit from handler, closes websocket. No return value. +""" +function HttpServer.handle(handler::WebSocketHandler, req::HttpServer.Request, client::HttpServer.Client) + websocket_handshake(req, client) || return + sock = WebSocket(client.sock,true) + handler.handle(req, sock) + if isopen(sock) + try + close(sock) + end + end +end + +function HttpServer.is_websocket_handshake(handler::WebSocketHandler, req::HttpServer.Request) + is_get = req.method == "GET" + # "upgrade" for Chrome and "keep-alive, upgrade" for Firefox. + is_upgrade = contains(lowercase(get(req.headers, "Connection", "")),"upgrade") + is_websockets = lowercase(get(req.headers, "Upgrade", "")) == "websocket" + return is_get && is_upgrade && is_websockets +end \ No newline at end of file diff --git a/src/WebSockets.jl b/src/WebSockets.jl index 95ac3f1..bb90be3 100644 --- a/src/WebSockets.jl +++ b/src/WebSockets.jl @@ -20,9 +20,8 @@ an http server. """ module WebSockets -using HTTP -import HTTP: digest, MD_SHA1 - +import MbedTLS: digest, MD_SHA1 +using Requires export WebSocket, write, @@ -410,48 +409,6 @@ function generate_websocket_key(key) return base64encode(digest(MD_SHA1, hashkey)) end -# """ -# Responds to a WebSocket handshake request. -# Checks for required headers and subprotocols; sends Response(400) if they're missing or bad. Otherwise, transforms client key into accept value, and sends Reponse(101). -# Function returns true for accepted handshakes. -# """ -# function websocket_handshake(request,client) -# if !haskey(request.headers, "Sec-WebSocket-Key") -# Base.write(client.sock, Response(400)) -# return false -# end -# if get(request.headers, "Sec-WebSocket-Version", "13") != "13" -# response = Response(400) -# response.headers["Sec-WebSocket-Version"] = "13" -# Base.write(client.sock, response) -# return false -# end - -# key = request.headers["Sec-WebSocket-Key"] -# if length(decode(Base64,key)) != 16 # Key must be 16 bytes -# Base.write(client.sock, Response(400)) -# return false -# end -# resp_key = generate_websocket_key(key) - -# response = Response(101) -# response.headers["Upgrade"] = "websocket" -# response.headers["Connection"] = "Upgrade" -# response.headers["Sec-WebSocket-Accept"] = resp_key - -# if haskey(request.headers, "Sec-WebSocket-Protocol") -# if hasprotocol(request.headers["Sec-WebSocket-Protocol"]) -# response.headers["Sec-WebSocket-Protocol"] = request.headers["Sec-WebSocket-Protocol"] -# else -# Base.write(client.sock, Response(400)) -# return false -# end -# end - -# Base.write(client.sock, response) -# return true -# end - function mask!(data, mask=rand(UInt8, 4)) for i in 1:length(data) data[i] = data[i] ⊻ mask[((i-1) % 4)+1] @@ -459,117 +416,8 @@ function mask!(data, mask=rand(UInt8, 4)) return mask end -function open(f::Function, url; binary=false, verbose=false, kw...) - - key = HTTP.base64encode(rand(UInt8, 16)) - - headers = [ - "Upgrade" => "websocket", - "Connection" => "Upgrade", - "Sec-WebSocket-Key" => key, - "Sec-WebSocket-Version" => "13" - ] - - HTTP.open("GET", url, headers; - reuse_limit=0, verbose=verbose ? 2 : 0, kw...) do http - - HTTP.startread(http) - - status = http.message.status - if status != 101 - return - end - - check_upgrade(http) - - if HTTP.header(http, "Sec-WebSocket-Accept") != generate_websocket_key(key) - throw(WebSocketError(0, "Invalid Sec-WebSocket-Accept\n" * - "$(http.message)")) - end - - io = HTTP.ConnectionPool.getrawstream(http) - f(WebSocket(io,false)) - end -end - -# mutable struct WebSocket -# id::Int -# socket::TCPSock -# state::ReadyState - -# function WebSocket(id::Int,socket::TCPSock) -# init_socket(socket) -# new(id, socket, CONNECTED) -# end -# end - -# function listen(f::Function, host::String="localhost", port::UInt16=UInt16(8081); binary=false, verbose=false) -# HTTP.listen(host, port; verbose=verbose) do http -# upgrade(f, http; binary=binary) -# end -# end - -function upgrade(f::Function, http::HTTP.Stream; binary=false) - - check_upgrade(http) - if !HTTP.hasheader(http, "Sec-WebSocket-Version", "13") - throw(WebSocketError(0, "Expected \"Sec-WebSocket-Version: 13\"!\n$(http.message)")) - end - - HTTP.setstatus(http, 101) - HTTP.setheader(http, "Upgrade" => "websocket") - HTTP.setheader(http, "Connection" => "Upgrade") - key = HTTP.header(http, "Sec-WebSocket-Key") - HTTP.setheader(http, "Sec-WebSocket-Accept" => generate_websocket_key(key)) - - HTTP.startwrite(http) - - io = HTTP.ConnectionPool.getrawstream(http) - f(WebSocket(io, true)) -end - -function check_upgrade(http) - if !HTTP.hasheader(http, "Upgrade", "websocket") - throw(WebSocketError(0, "Expected \"Upgrade: websocket\"!\n$(http.message)")) - end - - if !HTTP.hasheader(http, "Connection", "upgrade") - throw(WebSocketError(0, "Expected \"Connection: upgrade\"!\n$(http.message)")) - end -end - -function is_upgrade(r::HTTP.Message) - (r isa HTTP.Request && r.method == "GET" || r.status == 101) && - HTTP.hasheader(r, "Connection", "upgrade") && - HTTP.hasheader(r, "Upgrade", "websocket") -end +@require HTTP include("HTTP.jl") -# """ Implement the WebSocketInterface, for compatilibility with HttpServer.""" -# struct WebSocketHandler <: HttpServer.WebSocketInterface -# handle::Function -# end - -# import HttpServer: handle, is_websocket_handshake -# """ -# Performs handshake. If successfull, establishes WebSocket type and calls -# handler with the WebSocket and the original request. On exit from handler, closes websocket. No return value. -# """ -# function handle(handler::WebSocketHandler, req::Request, client::HttpServer.Client) -# websocket_handshake(req, client) || return -# sock = WebSocket(client.id, client.sock) -# handler.handle(req, sock) -# if isopen(sock) -# try -# close(sock) -# end -# end -# end -# function is_websocket_handshake(handler::WebSocketHandler, req::Request) -# is_get = req.method == "GET" -# # "upgrade" for Chrome and "keep-alive, upgrade" for Firefox. -# is_upgrade = contains(lowercase(get(req.headers, "Connection", "")),"upgrade") -# is_websockets = lowercase(get(req.headers, "Upgrade", "")) == "websocket" -# return is_get && is_upgrade && is_websockets -# end +@require HttpServer include("HttpServer.jl") end # module WebSockets diff --git a/test/HTTP.jl b/test/HTTP.jl new file mode 100644 index 0000000..25d7b36 --- /dev/null +++ b/test/HTTP.jl @@ -0,0 +1,53 @@ +@testset "HTTP" begin + +using HTTP +using WebSockets +# using HTTP.IOExtras +using Base.Test + +import WebSockets: CONNECTED, CLOSING, CLOSED + +info("Testing ws...") +WebSockets.open("ws://echo.websocket.org") do ws + write(ws, "Foo") + @test String(read(ws)) == "Foo" + + close(ws) +end +sleep(1) + +info("Testing wss...") +WebSockets.open("wss://echo.websocket.org") do ws + write(ws, "Foo") + @test String(read(ws)) == "Foo" + + close(ws) +end +sleep(1) + +p = UInt16(8000) +@async HTTP.listen("127.0.0.1",p) do http + if WebSockets.is_upgrade(http.message) + WebSockets.upgrade(http) do ws + while ws.state == CONNECTED + data = String(read(ws)) + write(ws,data) + end + end + end +end + +sleep(2) + +info("Testing local server...") +WebSockets.open("ws://127.0.0.1:$(p)") do ws + write(ws, "Foo") + @test String(read(ws)) == "Foo" + + write(ws, "Bar") + @test String(read(ws)) == "Bar" + + close(ws) +end + +end # testset \ No newline at end of file diff --git a/test/HttpServer.jl b/test/HttpServer.jl new file mode 100644 index 0000000..7a7351a --- /dev/null +++ b/test/HttpServer.jl @@ -0,0 +1,211 @@ +@testset "HttpServer" begin + +using HttpServer +using WebSockets +import WebSockets: generate_websocket_key, + write_fragment, + read_frame, + websocket_handshake + # is_websocket_handshake, + # handle +import HttpCommon: Request, Response + + +@sync yield() # avoid mixing of output with possible deprecation warnings from .juliarc +info("Starting test WebSockets...") +#is_control_frame is one line, checking one bit. +#get_websocket_key grabs a header. +#is_websocket_handshake grabs a header. +#generate_websocket_key makes a call to a library. +info("Test generate_websocket_key") +@test generate_websocket_key("dGhlIHNhbXBsZSBub25jZQ==") == "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=" + +# Test writing + +function xor_payload(maskkey, data) + out = Array{UInt8,1}(length(data)) + for i in 1:length(data) + d = data[i] + d = xor(d , maskkey[mod(i - 1, 4) + 1]) + out[i] = d + end + out +end + +const io = IOBuffer() + +info("Test length less than 126") +for len = [8, 125], op = (rand(UInt8) & 0b1111), fin=[true, false] + + test_str = randstring(len) + write_fragment(io, fin, op, false, Vector{UInt8}(test_str)) + + frame = take!(io) + + @test bits(frame[1]) == (fin ? "1" : "0") * "000" * bits(op)[end-3:end] + @test frame[2] == UInt8(len) + @test String(frame[3:end]) == test_str + + # The new WebSockets can handle both client and server so can handle no masks + # =========================================================================== + # # Check to see if reading message without a mask fails + # in_buf = IOBuffer(String(frame)) + # @test_throws ErrorException read_frame(in_buf) + # close(in_buf) + + # add a mask + maskkey = rand(UInt8, 4) + data = vcat( + frame[1], + frame[2] | 0b1000_0000, + maskkey, + xor_payload(maskkey, frame[3:end]) + ) + frame_back = read_frame(IOBuffer(data)) + + @test frame_back.is_last == fin + @test frame_back.rsv1 == false + @test frame_back.rsv2 == false + @test frame_back.rsv3 == false + @test frame_back.opcode == op + @test frame_back.is_masked == true + @test frame_back.payload_len == len + @test all(map(==, frame_back.maskkey, maskkey)) + @test test_str == String(frame_back.data) +end + +info("Test length 126 or more") +for len = 126:129, op = 0b1111, fin=[true, false] + + test_str = randstring(len) + write_fragment(io, fin, op, false, Vector{UInt8}(test_str)) + + frame = take!(io) + + @test bits(frame[1]) == (fin ? "1" : "0") * "000" * bits(op)[end-3:end] + @test frame[2] == 126 + + @test bits(frame[4])*bits(frame[3]) == bits(hton(UInt16(len))) + + # add a mask + maskkey = rand(UInt8, 4) + data = vcat( + frame[1], + frame[2] | 0b1000_0000, + frame[3], + frame[4], + maskkey, + xor_payload(maskkey, frame[5:end]) + ) + frame_back = read_frame(IOBuffer(data)) + + @test frame_back.is_last == fin + @test frame_back.rsv1 == false + @test frame_back.rsv2 == false + @test frame_back.rsv3 == false + @test frame_back.opcode == op + @test frame_back.is_masked == true + @test frame_back.payload_len == len + @test all(map(==, frame_back.maskkey, maskkey)) + @test test_str == String(frame_back.data) +end + +# TODO: test for length > typemax(Uint32) + +info("Tests for is_websocket_handshake") +chromeheaders = Dict{String, String}( + "Connection"=>"Upgrade", + "Upgrade"=>"websocket" + ) +chromerequest = HttpCommon.Request( + "GET", + "", + chromeheaders, + "" + ) + +firefoxheaders = Dict{String, String}( + "Connection"=>"keep-alive, Upgrade", + "Upgrade"=>"websocket" + ) + +firefoxrequest= Request( + "GET", + "", + firefoxheaders, + "" + ) + +wshandler = WebSocketHandler((x,y)->nothing);#Dummy wshandler + +for request in [chromerequest, firefoxrequest] + @test HttpServer.is_websocket_handshake(wshandler,request) == true +end + +info("Test of handshake response") +takefirstline(buf) = split(buf |> take! |> String, "\r\n")[1] + +take!(io) +Base.write(io, "test") +@test takefirstline(io) == "test" + +info("Test reject / switch format") +const SWITCH = "HTTP/1.1 101 Switching Protocols " +const REJECT = "HTTP/1.1 400 Bad Request " +Base.write(io, Response(400)) +@test takefirstline(io) == REJECT +Base.write(io, Response(101)) +@test takefirstline(io) == SWITCH + +function handshakeresponse(request) + cli = HttpServer.Client(2, IOBuffer()) + websocket_handshake(request, cli) + takefirstline(cli.sock) +end + +info("Test simple handshakes that are unacceptable") +for request in [chromerequest, firefoxrequest] + @test handshakeresponse(request) == REJECT + push!(request.headers, "Sec-WebSocket-Version" => "13") + @test handshakeresponse(request) == REJECT + push!(request.headers, "Sec-WebSocket-Key" => "mumbojumbobo") + @test handshakeresponse(request) == REJECT + push!(request.headers, "Sec-WebSocket-Version" => "11") + push!(request.headers, "Sec-WebSocket-Key" => "zkG1WqHM8BJdQMXytFqiUw==") + @test handshakeresponse(request) == REJECT +end + +info("Test simple handshakes, acceptable") +for request in [chromerequest, firefoxrequest] + push!(request.headers, "Sec-WebSocket-Version" => "13") + push!(request.headers, "Sec-WebSocket-Key" => "zkG1WqHM8BJdQMXytFqiUw==") + @test handshakeresponse(request) == SWITCH +end + +info("Test unacceptable subprotocol handshake subprotocol") +for request in [chromerequest, firefoxrequest] + push!(request.headers, "Sec-WebSocket-Version" => "13") + push!(request.headers, "Sec-WebSocket-Key" => "zkG1WqHM8BJdQMXytFqiUw==") + push!(request.headers, "Sec-WebSocket-Protocol" => "my.server/json-zmq") + @test handshakeresponse(request) == REJECT +end + +info("add simple subprotocol to acceptable list") +@test true == WebSockets.addsubproto("xml") + +info("add subprotocol with difficult name") +@test true == WebSockets.addsubproto("my.server/json-zmq") + +info("Test handshake subprotocol now acceptable") +for request in [chromerequest, firefoxrequest] + push!(request.headers, "Sec-WebSocket-Version" => "13") + push!(request.headers, "Sec-WebSocket-Key" => "zkG1WqHM8BJdQMXytFqiUw==") + push!(request.headers, "Sec-WebSocket-Protocol" => "xml") + @test handshakeresponse(request) == SWITCH + push!(request.headers, "Sec-WebSocket-Protocol" => "my.server/json-zmq") + @test handshakeresponse(request) == SWITCH +end +close(io) +include("browsertest.jl") + +end \ No newline at end of file diff --git a/test/browsertest.html b/test/browsertest.html index 63216ff..941031a 100644 --- a/test/browsertest.html +++ b/test/browsertest.html @@ -24,6 +24,14 @@

ws3 Websocket: server_denies_protocol