Padrino websockets + Heroku; Connection closed before receiving a handshake response - heroku

I am using padrino websockets (https://github.com/dariocravero/padrino-websockets) to provide a chat system for my site, and it works great on my local machine. However, after deploying to heroku (free), the websocket won't make a connection and will return
failed: Connection closed before receiving a handshake response
It works fine on localhost, where I am using this to connect:
connection = new WebSocket('ws://localhost:3000/channel');
But, when used on heroku with this:
connection = new WebSocket('ws://******.herokuapp.com:3000/channel');
it returns a handshake error (above)
My implementation server side
websocket :channel do
on :newmessage do |message|
currentAccount = Account.find_by(lastLoginIP: message["ip"]) rescue nil
if currentAccount != nil
broadcast :channel, {
"name" => currentAccount.nickname,
"url" => currentAccount.url,
"image" => currentAccount.image,
"chatmessage" => message["chatmessage"][0..80]
}
end
end
end
inside my main Padrino app.rb, and this in my Procfile. What is going on?
web: bundle exec puma -t 1:16 -p ${PORT:-3000} -e ${RACK_ENV:-production}

Your Websocket port (3000) isn't publicly available on Heroku.
Heroku forwards any requests made to port 80 or port 443 to the dynamic port of your web dyno, stored in the $PORT bash variable.
In your browser (client), try replacing this line:
connection = new WebSocket('ws://localhost:3000/channel');
With this line:
connection = new WebSocket('ws://' + window.document.location.host + 'channel');
Or, if you want to support both SSL and unencrypted Websockets:
ws_uri = (window.location.protocol.match(/https/) ? 'wss' : 'ws') +
'://' + window.document.location.host + 'channel';
connection = new WebSocket(ws_uri)
It should work if both your app and the websocket layer are sharing the same server.

Related

Fetch emails through IMAP with proxy of form user:password:host:port

I have code to login to my email account to fetch recent emails:
def fetchRecentEmail(emailAddr, emailPassword, timeout=120):
host = fetch_imap_server(emailAddr) # e.g. 'outlook.office365.com'
with IMAP4_SSL(host) as session:
status, _ = session.login(emailAddr, emailPassword)
if status == 'OK':
# fetch most recent message
status, messageData = session.select("Inbox")
:
I'm trying to tweak it to go through a proxy.
ref: How can I fetch emails via POP or IMAP through a proxy?
ref: https://gist.github.com/sstevan/efccf3d5d3e73039c21aa848353ff52f
In each of the above resources, the proxy is of clean form IP:PORT.
However my proxy is of the form USER:PASS:HOST:PORT.
The proxy works:
USER = 'Pp7fwti5n-res-any-sid-' + random8Digits()
PASS = 'abEDxts7v'
HOST = 'gw.proxy.rainproxy.io'
PORT = 5959
proxy = f'{USER}:{PASS}#{HOST}:{PORT}'
proxies = {
'http': 'http://' + proxy,
'https': 'http://' + proxy
}
response = requests.get(
'https://ip.nf/me.json',
proxies=proxies, timeout=15
)
The following code looks like it should work, but errors:
HOST = 'outlook.office365.com'
IMAP_PORT = 963
PROXY_TYPE = 'http' # rainproxies are HTTP
mailbox = SocksIMAP4SSL(
host=HOST,
port=IMAP_PORT,
proxy_type=PROXY_TYPE,
proxy_addr=URL,
proxy_port=PORT,
username=USER,
password=PASS
)
emailAddress, emailPassword = EMAIL.split(',')
mailbox.login(emailAddress, emailPassword)
typ, data = mailbox.list()
print(typ)
print(data)
I needed to add a timeout arg/param in 2 places to get the code to run:
def _create_socket(self, timeout=None):
sock = SocksIMAP4._create_socket(self, timeout)
server_hostname = self.host if ssl.HAS_SNI else None
return self.ssl_context.wrap_socket(
sock, server_hostname=server_hostname
)
def open(self, host='', port=IMAP4_PORT, timeout=None):
SocksIMAP4.open(self, host, port, timeout)
Rather confusing that nobody else seems to have flagged that in the gist.
But it still won't work.
If I use any number other than 443 for IMAP_PORT I get this error:
GeneralProxyError: Socket error: 403: Forbidden
[*] Note: The HTTP proxy server may not be supported by PySocks (must be a CONNECT tunnel proxy)
And if I use 443, while I now get no error, mailbox = SocksIMAP4SSL( never completes.
So I am still far from a working solution.
I am hoping to run this code simultaneously on 2 CPU cores, so I don't understand the implications of using port 443. Is that going to mean that no other process on my system can use that port? And if this code is using this port simultaneously in two processes, does this mean that there will be a conflict?
Maybe you can try monkeypatching socket.socket with PySocket.
import socket
import socks
socks.set_default_proxy(socks.SOCKS5, HOST, PORT, True, USER, PASS)
socket.socket = socks.socksocket
Then check if your IMAP traffic is going through a given proxy.

Bad record MAC on TLS v1.3 when using early_data

I have a project to complete in Ruby involving TLS v.1.3. I want to optimize requests and thus use "early data". I'm using a package called tttls1.3 and the client works until I send early data to the server. What's even more wired is that a request with early data goes through and I get a response from the server but immediately after the reply (response message) an alert 20 (Bad Record MAC) is received. I went so far that I even go and recalculate the "client-finished" message which seemed suspicious but it looks correct.
What could be the problem? Is there a TCP or other issue I could check?
Here's an example:
require 'socket'
require 'tttls1.3'
settings2 = {
alpn: ['http/1.1'],
supported_groups: [TTTLS13::NamedGroup::SECP256R1],
cipher_suites: [TTTLS13::CipherSuite::TLS_AES_256_GCM_SHA384],
check_certificate_status: false,
}
settings1 = {
alpn: ['http/1.1'],
supported_groups: [TTTLS13::NamedGroup::SECP256R1],
cipher_suites: [TTTLS13::CipherSuite::TLS_AES_256_GCM_SHA384],
check_certificate_status: false,
process_new_session_ticket: lambda do |nst, rms, cs|
return if Time.now.to_i - nst.timestamp > nst.ticket_lifetime
settings2[:ticket] = nst.ticket
settings2[:resumption_master_secret] = rms
settings2[:psk_cipher_suite] = cs
settings2[:ticket_nonce] = nst.ticket_nonce
settings2[:ticket_age_add] = nst.ticket_age_add
settings2[:ticket_timestamp] = nst.timestamp
end
}
# REQUEST
socket = TCPSocket.new("ssltest.louis.info", 443)
client = TTTLS13::Client.new(socket, "ssltest.louis.info", settings1)
client.connect
client.write("GET / HTTP/1.1\r\n")
client.write("Host: ssltest.louis.info\r\n")
client.write("\r\n\r\n")
client.read
client.close
socket.close
sleep(1)
# RESUMPTION
socket = TCPSocket.new("ssltest.louis.info", 443)
client = TTTLS13::Client.new(socket, "ssltest.louis.info", settings2)
client.early_data("HEAD / HTTP/1.1\r\nHost: ssltest.louis.info\r\n\r\n\r\n")
client.connect
p client.read
p client.read
p client.read
p client.read
Original issue: https://github.com/thekuwayama/tttls1.3/issues/48
It turned out that the Connection: close header must be present in the request. It must be the remote server implementation specific.

Binance websocket API sends opcode 8 (connection close) 1 second after successful connect & transfer

I'm having a problem with Binance's websocket API closing the connection almost immediately. The connect and websocket upgrade succeeds, and there's (usually) some of the expected data sent back, but the remote end sends websocket opcode 8 (connection close) less than a second after subscribing. Here's some example debug output; note the time between the initial connect and then reconnect is <= 1 second.
*** Connecting to server: stream.binance.com at 2021-05-25 16:06:48 UTC+0
websocket: connecting to ssl://stream.binance.com:9443 with path /ws/btcusdt#trade
websocket: connected
websocket: sent upgrade request
websocket: successful upgrade
Sending message to server: '{"method":"SUBSCRIBE","params":["btcusdt#trade"],"id":1}'
{"e":"trade","E":1621958809390,"s":"BTCUSDT","t":871186927,"p":"37950.99000000","q":"0.08750200","b":6146242615,"a":6146242639,"T":1621958809389,"m":true,"M":true}
{"e":"trade","E":1621958809390,"s":"BTCUSDT","t":871186928,"p":"37948.01000000","q":"0.27946700","b":6146242632,"a":6146242639,"T":1621958809389,"m":true,"M":true}
errstr=Remote sent opcode 8 (close)
*** Connecting to server: stream.binance.com at 2021-05-25 16:06:49 UTC+0
websocket: connecting to ssl://stream.binance.com:9443 with path /ws/btcusdt#trade
websocket: connected
websocket: sent upgrade request
websocket: successful upgrade
Sending message to server: '{"method":"SUBSCRIBE","params":["btcusdt#trade"],"id":1}'
{"e":"trade","E":1621958810172,"s":"BTCUSDT","t":871186935,"p":"37945.47000000","q":"0.00561300","b":6146242792,"a":6146242898,"T":1621958810171,"m":true,"M":true}
{"e":"trade","E":1621958810172,"s":"BTCUSDT","t":871186936,"p":"37945.43000000","q":"0.01233600","b":6146242721,"a":6146242898,"T":1621958810171,"m":true,"M":true}
{"e":"trade","E":1621958810183,"s":"BTCUSDT","t":871186937,"p":"37945.43000000","q":"0.00140200","b":6146242721,"a":6146242907,"T":1621958810183,"m":true,"M":true}
{"e":"trade","E":1621958810199,"s":"BTCUSDT","t":871186938,"p":"37945.36000000","q":"0.00484000","b":6146242644,"a":6146242914,"T":1621958810199,"m":true,"M":true}
{"e":"trade","E":1621958810203,"s":"BTCUSDT","t":871186939,"p":"37945.36000000","q":"0.08219200","b":6146242644,"a":6146242916,"T":1621958810203,"m":true,"M":true}
{"e":"trade","E":1621958810204,"s":"BTCUSDT","t":871186940,"p":"37945.36000000","q":"0.04644700","b":6146242644,"a":6146242917,"T":1621958810204,"m":true,"M":true}
errstr=Remote sent opcode 8 (close)
I'm using a simple PHP websocket client, slightly modified to output additional debug info: https://github.com/paragi/PHP-websocket-client , however the debug output above should be sufficient to show the subscribe process, and all other relevant parameters.
The same client works fine on another exchange's API.
I temporarily modified the client code to ignore opcode 8, but in that instance Binance enforces the close by closing the TCP socket.
I also tried this on a completely different server (located in another country, different ASN) in case it was my local IP/ASN that was the issue. Same problem.
Any ideas? I am new to websockets, and the Binance API, so perhaps I've done something silly. Thanks for any assistance.
EDIT: Here is minimal code to demonstrate the error. Requires https://github.com/paragi/PHP-websocket-client.git
Note: if there's no data returned, try running it a few times. Results are not consistent.
require_once("PHP-websocket-client/websocket_client.php");
// git clone https://github.com/paragi/PHP-websocket-client.git
$server = "stream.binance.com";
$serverport = 9443;
$streamname = "btcusdt#trade";
$t = array("method" => "SUBSCRIBE", "params" => array("$streamname"), "id" => 1);
$uri = "/ws/$streamname";
$message = json_encode($t) . "\n";
echo "\n*** Connecting to server: $server at " . gmdate("Y-m-d H:i:s") . " UTC+0\n";
$sp = websocket_open($server, $serverport,'',$errstr, 30, true, false, $uri);
if ($sp) {
echo "Sending message to server: '" . trim($message) . "' \n";
websocket_write($sp,$message);
while (1) {
$r = websocket_read($sp,$errstr);
echo "$r\n";
if ($r == "") {
echo "errstr=$errstr\n";
die;
}
}
}
?>

Crystal Lang Websocket server

I need help with Crystal Lang websockets, I want to know how to upgrade my connection on websocket. I want make simple websocket server
hope this help
require "http/server"
SOCKETS = [] of HTTP::WebSocket
ws_handler = HTTP::WebSocketHandler.new do |socket|
puts "Socket opened"
SOCKETS << socket
socket.on_message do |message|
SOCKETS.each { |socket| socket.send "Echo back from server: #{message}" }
end
socket.on_close do
puts "Socket closed"
end
end
server = HTTP::Server.new([ws_handler])
address = server.bind_tcp "0.0.0.0", 3000
puts "Listening on http://#{address}"
server.listen
https://medium.com/#muhammadtriwibowo/simple-websocket-using-crystal-13b6f67eba61
if you are looking for ready to use something, then you can use Shivneri framework created by me - which provides a javascript library and MVC based approach to create a socket server.
How to create a web socket end point
class ChatController < Shivneri::WebSocketController
#[On("message")]
def receive_message(data : String)
# send message to caller
clients.current.emit("message", "Received message is #{data}")
# send message to all clients
clients.emit("message", "Someone sent message #{data}")
end
end
How to connect to web socket end point using javascript
Shivneri framework provides a javascript library shivneri-ws-client-javascript to help you create a real time web application
var socket = new shivneriWsClient.Instance();
socket.on("message", function(data){
console.log("data", data);
});
await socket.init(`<web-socket-url>`);
// emit event to server
socket.emit("message","Successfully connected")
It provides many functionalities like grouping of clients, events when client is connected & disconnected etc.
For more information, take a look at shivneri websocket doc - https://shivneriforcrystal.com/tutorial/websocket/

Ruby Tor Switching Ip - "general SOCKS server failure" unless I spawn a new process to telnet into Tor control port

When trying to switch Tor IP address using telnet, I am running into SOCKSError::ServerFailure: general SOCKS server failure when trying to connect to the Tor control port via telnet. However if I spawn a new process to do the telnetting, I have no problem. I dont want to spawn a new process because its ugly. I was hoping someone could help me find why I have this problem and a more rubust solution?
To reproduce:
start tor:
tor --SocksPort 9350 --ControlPort 53500 --CookieAuthentication 0 --HashedControlPassword <passwordhash> --DataDirectory /tmp/tor_data/9350
Then run this ruby code which uses socksify to setup a socks server, then uses the tor gem Tor::Controller.connect block which connects to the Tor control port via telnet to switch Tor endpoint:
require 'socksify'
require 'terminator'
require 'tor'
TCPSocket::socks_server = "127.0.0.1"
TCPSocket::socks_port = "9350"
Tor::Controller.connect(:port => 53500) do |tor| #<- error
tor.authenticate("")
tor.signal("newnym")
end
Error at Tor::Controller.connect call:
SOCKSError::ServerFailure: general SOCKS server failure
If I replace the Tor::Controller.connect block with this (spawning a new process to do the telnetting), I have success:
telnet_pid = nil
begin
Terminator.terminate :seconds => 20 do
cmd = "bundle exec ruby -e \"require 'tor'\" -e " +
"\"Tor::Controller.connect(:port => 53500)" +
"{|tor| tor.authenticate(''); tor.signal('newnym')}\""
telnet_pid = Process.spawn(cmd)
Process.wait telnet_pid
end
rescue Terminator.error
puts 'Telnet process to switch Tor endpoint timed out!'
Process.kill('TERM', telnet_pid) if telnet_pid
end
I realised that the socks server was routing all TCP requests through the SOCKS server including my telnet requests. If I disable the socks server while telnetting then re-enable it again it works:
TCPSocket::socks_server = nil
TCPSocket::socks_port = nil
Tor::Controller.connect(:port => 53500) do |tor| #<- error
tor.authenticate("")
tor.signal("newnym")
end
TCPSocket::socks_server = "127.0.0.1"
TCPSocket::socks_port = "9350"

Resources