Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot support proxy? #172

Closed
liupgd opened this issue Apr 28, 2021 · 6 comments
Closed

Cannot support proxy? #172

liupgd opened this issue Apr 28, 2021 · 6 comments

Comments

@liupgd
Copy link

liupgd commented Apr 28, 2021

I tried with examples. If no proxy used, it succeeded. But if I use proxy, 'Bad request' returned. My test code is:

using WebSockets
WebSockets.open("wss://echo.websocket.org", proxy="http://127.0.0.1:10809") do ws
    println("ok")
end

Any help?

@hustf
Copy link
Collaborator

hustf commented Apr 28, 2021

Hi,
I'm afraid you'll have to dive a bit deeper in, here. Stack traces are boring, but often the key. You can confirm if this is what you get, too:

ERROR: WebSocketClosedError(" while open ws|client: connect: connection refused (ECONNREFUSED)")
Stacktrace:
 [1] open(f::Function, url::String; verbose::Bool, subprotocol::String, kw::Base.Iterators.Pairs{Symbol, String, Tuple{Symbol}, NamedTuple{(:proxy,), Tuple{String}}})
   @ WebSockets ~\.julia\packages\WebSockets\QcswW\src\HTTP.jl:99
 [2] top-level scope
   @ REPL[5]:1

caused by: IOError(Base.IOError("connect: connection refused (ECONNREFUSED)", -4078) during request(http://127.0.0.1:10809))

Stacktrace:
  [1] getconnection(::Type{Sockets.TCPSocket}, host::SubString{String}, port::SubString{String}; keepalive::Bool, connect_timeout::Int64, readtimeout::Int64, kw::Base.Iterators.Pairs{Symbol, Any, NTuple{4, Symbol}, NamedTuple{(:require_ssl_verification, :iofunction, :reached_redirect_limit, :verbose), Tuple{Bool, WebSockets.var"#openstream#4"{var"#3#4", String}, Bool, Int64}}})
    @ HTTP.ConnectionPool ~\.julia\packages\HTTP\dCYNB\src\ConnectionPool.jl:702
  [2] newconnection(pod::HTTP.ConnectionPool.Pod, T::Type, host::SubString{String}, port::SubString{String}, pipeline_limit::Int64, require_ssl_verification::Bool, idle_timeout::Int64; kw::Base.Iterators.Pairs{Symbol, Any, Tuple{Symbol, Symbol, Symbol}, NamedTuple{(:iofunction, :reached_redirect_limit, :verbose), Tuple{WebSockets.var"#openstream#4"{var"#3#4", String}, Bool, Int64}}})
    @ HTTP.ConnectionPool ~\.julia\packages\HTTP\dCYNB\src\ConnectionPool.jl:624
  [3] getconnection(::Type{HTTP.ConnectionPool.Transaction{Sockets.TCPSocket}}, host::SubString{String}, port::SubString{String}; connection_limit::Int64, pipeline_limit::Int64, idle_timeout::Int64, reuse_limit::Int64, require_ssl_verification::Bool, kw::Base.Iterators.Pairs{Symbol, Any, Tuple{Symbol, Symbol, Symbol}, NamedTuple{(:iofunction, :reached_redirect_limit, :verbose), Tuple{WebSockets.var"#openstream#4"{var"#3#4", String}, Bool, Int64}}})
    @ HTTP.ConnectionPool ~\.julia\packages\HTTP\dCYNB\src\ConnectionPool.jl:568
  [4] request(::Type{HTTP.ConnectionRequest.ConnectionPoolLayer{HTTP.StreamRequest.StreamLayer{Union{}}}}, url::URIs.URI, req::HTTP.Messages.Request, body::Nothing; proxy::String, socket_type::Type, reuse_limit::Int64, kw::Base.Iterators.Pairs{Symbol, Any, Tuple{Symbol, Symbol, Symbol}, NamedTuple{(:iofunction, :reached_redirect_limit, :verbose), Tuple{WebSockets.var"#openstream#4"{var"#3#4", String}, Bool, Int64}}})

What you are doing here is calling to a server you don't control. I suppose that server replies to an ordinary http connection with a response that includes a header, something like '407 Proxy Authentication Required'. Setting up a http connection is just the first step in establishing a websocket, and there's not much hope to do this if that server don't like proxies.

The headers will not be in scope in your function ('println'), but have a look at the 'guarded' examples. They are intended primarily for when you take the role as server and are reluctant to accept potentially malicious connections from outside. I hope this was helpful?

@liupgd
Copy link
Author

liupgd commented Apr 28, 2021

@hustf Thanks for your reply.
Please, forgive me, I'm a novice in Julia. I've tried my best to dive into the request codes but no helpful clues found.
In fact, the ws link "wss://echo.websocket.org" is from repo's example codes. I've tried the example codes. If there is no proxy, they're working fine. However, my situation is, I need the proxy.
I see your ERROR info, it's probably because you do not have a vpn? I've installed a VPN, the 'http://127.0.0....' means my local vpn address. It cannot be accessed untill you have also setup a VPN in your own computer.

Some supplements:
Without proxy, the example code prints some info:

HTTP.Messages.Response:
"""
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Date: Wed, 28 Apr 2021 14:27:46 GMT
Sec-WebSocket-Accept: hg0skb/YlvPJTKJTn8KFcBBBDUo=
Server: Kaazing Gateway
Upgrade: websocket
"""

With proxy, the example code prints some info:

HTTP.Messages.Response:
"""
HTTP/1.1 400 Bad Request
Content-Type: text/plain
Connection: close
"""

I also debug the code with my very limited julia ablity. I debug the code in vscode. When the debugger step to line 108 in ConnectionRequest.jl of HTTP package, julia crashed. The crash happened so fast, I even can't get the error prints. The line crashed during debug is as bellow:
image
I don't know how to step deeper. Can you get some useful information from above? Thanks.

@hustf
Copy link
Collaborator

hustf commented Apr 28, 2021 via email

@liupgd
Copy link
Author

liupgd commented Apr 29, 2021

Thanks. My use case is I need to subscribe data stream from a platform. But that platform doesn't offer websocket service in my country. So a VPN is needed.
I tried to setup a websocket server for proxy test, it turns out to fail again. The server side never received requests after the proxy is used in the client side.
Here is my experiment code:

  • server
using WebSockets
using Sockets
const LOCALIP = ip"127.0.0.1"
const PORT = 60100

handle(req) = "Hello..." |> WebSockets.Response

function gatekeeper(req, ws)
    orig = WebSockets.origin(req)
    @info "\nOrign: $orig Target: $(req.target) subprotocol: $(subprotocol(req))"
end

const server = WebSockets.ServerWS(handle, gatekeeper)

@async WebSockets.with_logger(WebSocketLogger()) do
    WebSockets.serve(server, LOCALIP, PORT)
end
  • client
WebSockets.open("ws://127.0.0.1:60100", proxy="http://127.0.0.1:10809") do ws
       println("ok")
end
  • failure info

HTTP.Messages.Response:
"""
HTTP/1.1 400 Bad Request
Content-Type: text/plain
Connection: close
"""

If there is no proxy, everything is fine.

I also tried python websocket, that works fine. Maybe I have to use python as data receiver.

@liupgd
Copy link
Author

liupgd commented Apr 29, 2021

Hi @hustf Today, I try to modified HTTP.jl package(not the HTTP.jl of this repo).
HTTP.jl--> ConnectionRequest.jl line 96:

  • Ther Original code:
try
    if proxy !== nothing && target_url.scheme == "https"
        # tunnel request
        target_url = merge(target_url, port=443)
        r = connect_tunnel(io, target_url, req)
        if r.status != 200
            close(io)
            return r
        end
        io = ConnectionPool.sslupgrade(io, target_url.host; kw...)
        req.headers = filter(x->x.first != "Proxy-Authorization", req.headers)
    end

    r =  request(Next, io, req, body; kw...)
......
  • Now I changed to this:
try
    if proxy !== nothing# && target_url.scheme == "https"
        # tunnel request
        if target_url.scheme == "https" || target_url.scheme == "wss"
            target_url = merge(target_url, port=443)
        end
        r = connect_tunnel(io, target_url, req)
        if r.status != 200
            close(io)
            return r
        end
        if target_url.scheme == "https" || target_url.scheme == "wss"
            io = ConnectionPool.sslupgrade(io, target_url.host; kw...)
        end
        req.headers = filter(x->x.first != "Proxy-Authorization", req.headers)
    end

    r =  request(Next, io, req, body; kw...)
......

Now, it works for proxy now. I'm not sure whether my modification would affect other functions of HTTP. At least it works for me now.

@liupgd liupgd closed this as completed Apr 29, 2021
@hustf
Copy link
Collaborator

hustf commented Apr 30, 2021

Hi, and sorry for the late reply. Yes, the fix seems quite obvious, and could save quite some time for other people meeting this. I don't believe the use of websockets outside of local connections and with Julia have been very widely used. This is one example of the maturing which happens when we do that.

I suppose you use 'develop HTTP', and no, I don't see downsides expect that you'll miss updates. Please open an issue on HTTP.jl and send a pull request referring it!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants