IXWebSocket
IXWebSocket copied to clipboard
403 forbidden wenn connecting to golang.org/x/net/websocket
problem description
i've been using the example code from https://machinezone.github.io/IXWebSocket/
when running this client code i see this GET request in wireshark:
GET /matrix-appserver/v1/ts/events HTTP/1.1
Host: localhost:5000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: SGRjYmMwNGU5OTZnM0MyYw==
User-Agent: ixwebsocket/11.4.3 windows ssl/OpenSSL OpenSSL 1.1.1s 1 Nov 2022 zlib 1.2.12
but the golang server will answer with:
HTTP/1.1 403 Forbidden
but if i add
headers["Origin"] = "http://localhost:5000";
it is working. why does it require this? should i file a bug report on the golang.org/x/net/websocket side?
a connection from the webbrowsers javascript console works without the explicit origin, this is the request from javascript:
GET /matrix-appserver/v1/ts/events HTTP/1.1
Host: localhost:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0
Accept: */*
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Sec-WebSocket-Version: 13
Origin: http://localhost:5000
Sec-WebSocket-Protocol: protocolOne
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: UfNfs6NDmKF17/2Tze/Jpw==
Connection: keep-alive, Upgrade
Cookie: Goland-edb8cc46=1aed21cf-1be6-4199-8b7d-97ce44799993
Sec-Fetch-Dest: websocket
Sec-Fetch-Mode: websocket
Sec-Fetch-Site: same-origin
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
note: i also added this so others might see this issue and know what to do.
client implementation
this is actually working code:
// Required on Windows
ix::initNetSystem();
m_webSocket = std::make_unique<ix::WebSocket>();
m_webSocket->enableAutomaticReconnection();
// Connect to a server with encryption
// See https://machinezone.github.io/IXWebSocket/usage/#tls-support-and-configuration
std::string url("ws://localhost:5000/matrix-appserver/v1/ts/events");
m_webSocket->setUrl(url);
ix::WebSocketHttpHeaders headers;
headers["Origin"] = "http://localhost:5000";
m_webSocket->setExtraHeaders(headers);
m_webSocket->setPingInterval(45);
m_webSocket->addSubProtocol("teamspeak-appserver-protocol1");
//m_webSocket->disablePerMessageDeflate();
LOGL(<< "Connecting to " << url, LogLevel_INFO);
// Setup a callback to be fired (in a background thread, watch out for race conditions !)
// when a message or an event (open, close, error) is received
m_webSocket->setOnMessageCallback([](const ix::WebSocketMessagePtr& msg) {
if (msg->type == ix::WebSocketMessageType::Message) {
LOGL(<< "received message: " << msg->str, LogLevel_INFO);
} else if (msg->type == ix::WebSocketMessageType::Open) {
LOGL(<< "Connection established", LogLevel_INFO);
} else if (msg->type == ix::WebSocketMessageType::Error) {
// Maybe SSL is not configured properly
LOGL(<< "Connection error: " << msg->errorInfo.reason, LogLevel_INFO);
}
});
// Now that our callback is setup, we can start our background thread and receive messages
m_webSocket->start();
server
import "golang.org/x/net/websocket"
...
matrix_api := mux.NewRouter()
...
matrix_api.HandleFunc("/matrix-appserver/v1/ts/events", func(rw http.ResponseWriter, req *http.Request) { // called by teamspeak
colorlogger.Log.Info("about to create a new websocket connection")
websocket.Handler(ws.OnConnected).ServeHTTP(rw, req)
}).Methods(http.MethodGet)
my solution
i've added this header to the request:
headers["Origin"] = "http://localhost:5000";
then it will return a working websocket connection.
Thanks for the report. Maybe we could set an Origin automatically.
Does it work if you use ws://localhost:5000
instead of http://localhost:5000
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin%20
Setting this automatically would be amazing!
And the good news is:
ws://localhost:5000
instead of
http://localhost:5000/
The log reads:
2023-02-11 00:01:34 2023-02-10 23:01:34.373743|INFO |AppServer | |Connecting to ws://appserver:5000/matrix-appserver/v1/teamspeak/events
2023-02-11 00:01:34 2023-02-10 23:01:34.374065|INFO |AppServer | |With origin ws://appserver:5000
2023-02-11 00:01:34 2023-02-10 23:01:34.376552|INFO |AppServer | |Connection established
2023-02-11 00:01:34 2023-02-10 23:01:34.377117|INFO |Files | |Connection established
for Origin header seems be working!
I've created this solution:
#include <boost/url.hpp>
...
std::string appserver_websocket_url = "ws://appserver:5000/appserver/v1/events"
assert(appserver_websocket_url != "");
boost::urls::url_view u(appserver_websocket_url);
std::string portString = "";
if (u.has_port() == true) {
portString = ":" + std::string(u.port());
}
std::string url_full = std::string(u.scheme()) + "://" + std::string(u.host()) + portString + std::string(u.path());
std::string url_origin = std::string(u.scheme()) + "://" + std::string(u.host()) + portString;
m_webSocket->setUrl(url_full);
ix::WebSocketHttpHeaders headers;
headers["Origin"] = url_origin;
m_webSocket->setExtraHeaders(headers);
Too bad, there is no boost url function to get the origin directly. The problem with constructing it is that there could be parameters which I did not yet handle as logins and so on.
https://www.boost.org/doc/libs/1_81_0/libs/url/doc/html/url/overview.html
https://github.com/machinezone/IXWebSocket/pull/455
I just added the Origin header automatically. Do you want to give it a try ?