Skip to content

Commit

Permalink
Merge pull request #184 from JuliaWeb/cleanup182
Browse files Browse the repository at this point in the history
Cleanup prior to tag
  • Loading branch information
hustf committed Nov 29, 2022
2 parents 69c582d + 8939845 commit 811d94b
Show file tree
Hide file tree
Showing 12 changed files with 137 additions and 122 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Expand Up @@ -3,7 +3,6 @@ os:
- linux
- osx
julia:
- 1.0
- 1
sudo: false
notifications:
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Expand Up @@ -11,7 +11,7 @@ Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"

[compat]
HTTP = "1.1.0, 1.5"
julia = "1.6"
julia = "1.8.2"

[extras]
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand Down
87 changes: 64 additions & 23 deletions README.md
Expand Up @@ -6,52 +6,86 @@

Server and client side [Websockets](https://tools.ietf.org/html/rfc6455) protocol in Julia. WebSockets is a small overhead message protocol layered over [TCP](https://tools.ietf.org/html/rfc793). It uses HTTP(S) for establishing the connections.

## Getting started
In the package manager, add WebSockets. Then [paste](https://docs.julialang.org/en/v1/stdlib/REPL/index.html#The-Julian-mode-1) this into a REPL:
## Upgrading to v. 1.6
Julia 1.8.2 or higher is now required due to some instabilities.

There are minor 'public API' changes in v. 1.6. We advise 'using' each function like below, except when experimenting.

This example tries typical 'old' code, shows errors, and shows replacement code.

```julia
julia> using WebSockets
julia> using WebSockets: serve, writeguarded, readguarded, @wslog, open,
HTTP, Response, ServerWS, with_logger, WebSocketLogger
julia> begin
function handler(req)
@wslog "Somebody wants a http response"
Response(200)
end
function wshandler(ws_server)
@wslog "A client opened this websocket connection"
writeguarded(ws_server, "Hello")
readguarded(ws_server)
end
serverWS = ServerWS(handler, wshandler)
servetask = @async with_logger(WebSocketLogger()) do
serve(serverWS, port = 8000)
"Task ended"
end
end
[ Info: Listening on: 127.0.0.1:8000
Task (runnable) @0x000001921cbd2ca0
```
julia> serverWS = WebSockets.ServerWS((req) -> WebSockets.Response(200), (ws_server) -> (writeguarded(ws_server, "Hello"); readguarded(ws_server)))
WebSockets.ServerWS(handler=#17(req), wshandler=#18(ws_server), connection_count=7)
The above would work on earlier versions. But now test in a browser: [http://127.0.0.1:8000](http://127.0.0.1:8000). The browser would show: `Server never wrote a response`, and the REPL would show:
julia> ta = @async WebSockets.with_logger(WebSocketLogger()) do
WebSockets.serve(serverWS, port = 8000)
end
Task (runnable) @0x000000000fc91cd0
```julia
julia> [ Wslog 11:08:09.957: Somebody wants a http response
[ Wslog 11:08:10.078: Somebody wants a http response
```
julia> WebSockets.HTTP.get("http://127.0.0.1:8000")
HTTP.Messages.Response:
"""
HTTP/1.1 200 OK
Transfer-Encoding: chunked
We had two requests from the browser - one was for the 'favicon' of our site. But something went wrong here. If you like long stacktraces, also try ```HTTP.get("http://127.0.0.1:8000");```
"""
__Let's revise the http handler to match the new requirements:__
```julia
julia> function handler(req)
@wslog "HTTP.jl v1.0+ requires more of a response"
Response(200, "Nothing to see here!")
end
```
julia> WebSockets.open("ws://127.0.0.1:8000") do ws_client
Reload the browser page to verify the server is updated and working!
Let us test the websockets!
```julia
julia> open("ws://127.0.0.1:8000") do ws_client
data, success = readguarded(ws_client)
if success
println(stderr, ws_client, " received:", String(data))
println(stderr, ws_client, " received: ", String(data))
end
end;
WebSocket(client, CONNECTED) received:Hello

WARNING: Workqueue inconsistency detected: popfirst!(Workqueue).state != :queued
[ LogLevel(50): A client opened this websocket connection
WebSocket(client, CONNECTED) received: Hello
```
That's it, we have upgraded by simply modifing the Response constructors. The websocket was closed at exiting the handler, and to close the running server:
```julia
julia> put!(serverWS.in, "close!")
[ Info: Server on 127.0.0.1:8000 closing
"close!"

julia> ta
Task (done) @0x000000000fc91cd0

julia> servetask
Task (done) @0x000001d6457a1180
```
Access inline documentation and have a look at the examples folder! The testing files also demonstrate a variety of uses. Benchmarks show examples of websockets and servers running on separate processes, as oposed to asyncronous tasks.
### About this package
Originally from 2013 and Julia 0.2, the WebSockets API has remained largely unchanged. It now depends on [HTTP.jl](https://github.com/JuliaWeb/HTTP.jl) for establishing the http connections. That package is in ambitious development, and most functionality of this package is already implemented directly in HTTP.jl.
This more downstream package may lag behind the latest version of HTTP.jl, and in so doing perhaps avoid some borderline bugs. This is why the examples and tests do not import HTTP methods directly, but rely on the methods imported in this package. E.g. by using `WebSockets.HTTP.listen` instead of `HTTP.listen` you may possibly be using the previous release of package HTTP. The imported HTTP version is capped so as to avoid possible issues when new versions of HTTP are released.
We aim to replace code with similar code in HTTP when possible, reducing this package to a wrapper. Ideally, all top-level tests will continue to pass without change.
## What can you do with it?
- read and write between entities you can program or know about
- serve an svg file to the web browser, containing javascript for connecting back through a websocket, adding two-way interaction with graphics
Expand Down Expand Up @@ -83,13 +117,20 @@ If you prefer to write your own server coroutine with this approach, error messa
## Development, new features, comments
The issues section is used for planning development: Contributions are welcome.
- Version 1.6 makes necessary changes to use HTTP 1.1.0 and limits the Julia versions to 1.8.2+.
- Version 1.5 shows the current number of connections on ServerWS. ServerWS in itself is immutable.
- Version 1.4 removes a ratelimiter function.
- Version 1.3 integrates `WebSocketLogger`. It closely resembles `ConsoleLogger` from the Julia standard library. Additional features: see inline docs and 'examples/count_with_logger.jl'. With this closer integration with Julia's core logging functionality, we also introduce `@debug` statements in `readguarded` and `writeguarded` (as well as when receiving 'ping' or 'pong'). The functions still return a boolean to indicate failure, but return no reason except the logger messages.
- The /benchmark folder contain some code that is not currently working, pending logging facilities.
- Alternative Julia packages: [DandelionWebSockets](https://github.com/dandeliondeathray/DandelionWebSockets.jl) and the direct implementation in [HTTP.jl](https://github.com/JuliaWeb/HTTP.jl).
## Errors after updating?
### To version 1.6
Updated to use HTTP 1.1.0-1.5 as a dependency.
In your code: Response(200) -> Response(200, "OK")
Also see the example at the top.
### To version 1.5.6
Updated to use HTTP 0.9 as a dependency.
Expand Down
4 changes: 2 additions & 2 deletions REQUIRE
@@ -1,2 +1,2 @@
julia 0.7 1.99
HTTP 0.8.0 0.8.99
julia 1.8.2 1.99
HTTP 1.1.0, 1.5
3 changes: 1 addition & 2 deletions appveyor.yml
@@ -1,7 +1,6 @@
environment:
matrix:
- julia_version: 1.6.3
- julia_version: 1.8.3
- julia_version: 1.8.2
- julia_version: nightly

platform:
Expand Down
8 changes: 4 additions & 4 deletions benchmark/functions_open_browsers.jl
Expand Up @@ -14,7 +14,7 @@ const COUNTBROWSER = Countbrowser(0)
"Get application path for developer applications"
function fwhich(s)
fi = ""
if Sys.is_windows()
if Sys.iswindows()
try
fi = split(read(`where.exe $s`, String), "\r\n")[1]
if !isfile(fi)
Expand All @@ -35,7 +35,7 @@ end
function browser_path_unix_apple(shortname)
trypath = ""
if shortname == "chrome"
if Base.Sys.is_apple()
if Base.Sys.isapple()
return "Google Chrome"
else
return "google-chrome"
Expand All @@ -45,7 +45,7 @@ function browser_path_unix_apple(shortname)
return "firefox"
end
if shortname == "safari"
if Base.Sys.is_apple()
if Base.Sys.isapple()
return "safari"
else
return ""
Expand All @@ -65,7 +65,7 @@ function browser_path_windows(shortname)
path32 = homdr * "/Program Files (x86)/"
path64 = homdr * "/Program Files/"
if shortname == "chrome"
trypath = path64 * "Chrome/Application/chrome.exe"
trypath = path64 * "Google/Chrome/Application/chrome.exe"
isfile(trypath) && return trypath
trypath = path32 * "Google/Chrome/Application/chrome.exe"
isfile(trypath) && return trypath
Expand Down
5 changes: 2 additions & 3 deletions src/Logger/websocketlogger.jl
Expand Up @@ -11,9 +11,8 @@ import Base.CoreLogging: logmsg_code,
_min_enabled_level,
current_logger_for_env,
logging_error
if VERSION >= v"1.1"
import Base.CoreLogging: _invoked_shouldlog
end
import Base.CoreLogging: _invoked_shouldlog


import Base.string_with_env
const Wslog = LogLevel(50)
Expand Down
20 changes: 4 additions & 16 deletions test/error_test.jl
Expand Up @@ -14,7 +14,7 @@ const FPORT = 8092
@info "Start a server with a ws handler that is unresponsive. \nClose from client side. The " *
" close handshake aborts after $(WebSockets.TIMEOUT_CLOSEHANDSHAKE) seconds..."
s = WebSockets.ServerWS(
req::HTTP.Request -> HTTP.Response(200),
req::HTTP.Request -> HTTP.Response(200, "OK"),
(req::HTTP.Request, ws::WebSocket) -> begin
for i=1:16
sleep(1)
Expand All @@ -32,7 +32,7 @@ close(s)
@info "Start a server with a ws handler that always reads guarded."
sleep(1)
s = WebSockets.ServerWS(
req -> HTTP.Response(200),
req -> HTTP.Response(200, "OK"),
(req, ws_serv) -> begin
while isopen(ws_serv)
WebSockets.readguarded(ws_serv)
Expand Down Expand Up @@ -95,7 +95,7 @@ close(s)
sleep(1)
chfromserv=Channel(2)
s = WebSockets.ServerWS(
req-> HTTP.Response(200),
req-> HTTP.Response(200, "OK"),
ws_serv->begin
while isopen(ws_serv)
try
Expand All @@ -117,10 +117,6 @@ global err = take!(chfromserv)
@test typeof(err) <: WebSocketClosedError
@test err.message == "while read(ws|server) Client side closed socket connection - Performed closing handshake."
global stack_trace = take!(chfromserv)
if VERSION <= v"1.0.2"
# Stack trace on master is zero. Unknown cause.
@test length(stack_trace) == 2
end

close(s)

Expand All @@ -129,7 +125,7 @@ sleep(1)
@info "Start a server. Errors are output on built-in channel"
sleep(1)
s = WebSockets.ServerWS(
req-> HTTP.Response(200),
req-> HTTP.Response(200, "OK"),
ws_serv->begin
while isopen(ws_serv)
read(ws_serv)
Expand All @@ -146,10 +142,6 @@ global err = take!(s.out)
@test err.message == "while read(ws|server) Client side closed socket connection - Performed closing handshake."
sleep(1)
global stack_trace = take!(s.out);
if VERSION <= v"1.0.2"
# Stack trace on master is zero. Unknown cause.
@test length(stack_trace) in [5, 6]
end

while isready(s.out)
take!(s.out)
Expand All @@ -173,10 +165,6 @@ for (ke, va) in WebSockets.codeDesc
@test err.message == "ws|server respond to OPCODE_CLOSE $ke: $va"
wait(s.out)
stacktra = take!(s.out)
if VERSION <= v"1.0.2"
# Unknown cause, nighly behaves differently
@test length(stacktra) == 0
end
while isready(s.out)
take!(s.out)
end
Expand Down
2 changes: 1 addition & 1 deletion test/show_test.jl
Expand Up @@ -188,7 +188,7 @@ output = String(take!(io.io))
@test output == "WebSocket{BufferStream}(client, \e[32mCONNECTED\e[39m): \e[32m✓\e[39m, 2 bytes"

### For testing Base.show(ServerWS)
h(r) = HTTP.Response(200)
h(r) = HTTP.Response(200, "OK")
w(s) = nothing
io = IOBuffer()
WebSockets._show(io, h)
Expand Down

2 comments on commit 811d94b

@hustf
Copy link
Collaborator Author

@hustf hustf commented on 811d94b Nov 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/73083

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v1.6.0 -m "<description of version>" 811d94bcd23a6a5249f5b74b9989021fbe034629
git push origin v1.6.0

Please sign in to comment.