Python socket.io - how to connect over HTTPS? - python

I am trying to send socket.io message to a nodejs server over HTTPS. After googling around for the socketIO on Python; I have written the below simple code to make a test.
Although I receive no compile error, I receive nothing from the server. When I analyzed via wireshark packetsniffer; I noticed that HTTPS connection is not even established (target server has the valid signed certificate and I can access the server via JS based frontend without any problem)..
from socketIO_client import SocketIO, LoggingNamespace
NODEJS_URL = "https://targetserver.com"
def on_message_response(*args):
print('message', args)
user_value = "test1"
message_value = "hello"
with SocketIO(NODEJS_URL, 443, LoggingNamespace) as socketIO:
socketIO.emit('message', { 'user': user_value, 'message': message_value}, on_message_response)
socketIO.wait_for_callbacks(seconds=1)
Do you have any idea why Python socketIO connection might not be successful?

Related

websockets closing unexpectedly

Update 13. Apr: dart code works consistently
Background:
My TV (Samsung 2019 RU7000) offers a secure websocket connection with which json packets can be send to remote control it. For example
"method":"ms.remote.control",
"params":{ "Cmd": "Click", "DataOfCmd":"KEY_MUTE" }
can be send to mute the TV.
To enable access, a handshake is done. The client connects to wss://ip:8002/api/v2/samsung.remote.control?name=value where the value of name is a base64 encoded string. After a successful connection, a popup window appears on the TV which needs to be accepted. If the user accepts the request, the TV sends a JSON response containing a token:
"data":{
"clients":[ ... ],
"id":"...",
"token":"28852140"
},
"event":"ms.channel.connect"
This token is used for authenticating connections with the same name by attaching &token=value to the URL. Sending commands before the user accepts the popup is ignored.
What works
Command line approaches with wscat and curl are working. The TV shows a pop-up and sends a response:
$ wscat -n -c https://192.168.0.227:8002/api/v2/channels/samsung.remote.control?name=aW9Ccm9rZXI=
Connected (press CTRL+C to quit)
< {"data":{"clients":[...], "id":"...", "token":"57940060"}, "event":"ms.channel.connect"}
dart - dart.io.WebSocket
The following code triggers the popup and gets a response as well.
WebSocket ws = await WebSocket.connect(url,
compression: CompressionOptions.compressionOff);
ws.pingInterval = Duration(seconds: 10000);
ws.listen(print, onError: print, onDone: () {
print("done");
print(ws.closeCode);
print(ws.closeReason);
});
await Future.delayed(Duration(seconds: 30));
ws.close();
Problem
I want to use python to connect to the websocket, authenticate my session and send remote control commands. This does not work for the following implementations.
python - websocket liris
The popup does not appear and there is no response from the TV. The socket does not close.
from websocket import create_connection
from ssl import CERT_NONE
sock = create_connection(url, sslopt={"cert_reqs": CERT_NONE})
print(sock.recv())
python - websockets aaugustin
The pop-up does not appear as well and it seems the connection is closed by the TV.
from websockets.client import connect, WebSocketClientProtocol
import ssl
import asyncio
async def connect():
async with connect(url, ssl=ssl.CERT_NONE) as websocket:
res = await websocket.recv()
print(res)
asyncio.get_event_loop().run_until_complete(connect())
The wireshark log shows that it's sending an HTTP GET asking for a websocket upgrade. The TV responds by closing the connection with FIN and RST.
Observations
Connections to publicly available websocket servers do not share the same issues I am experiencing.
Many popular remote controls available for python have the two websocket libraries as the implemented solution. None of these were working for me.
Comparing the packets sent by the python implementations and the command line tools do not show a noticeable difference to me.
wscat sends an 80 byte long packet to the TV every four seconds. I suppose this is ping/pong.
How can I investigate further?
What about trying Tornado's websocket implementation?
async def samsung_ws():
ws_req = HTTPRequest("wss://ip:8002/api/v2/samsung.remote.control?name=value", validate_cert=False)
ws = await websocket_connect(ws_req)
ws.write_message("hello")
while True:
msg = await ws.read_message()
if not msg:
break
print(msg)
Depending on how the webserver is built, you might need to request the webpage before connecting to the websocket because in your wscat example I noticed you are using https:// instead of wss://.
Can you post a sample of a dart run (with the url used)?
Um.... I have some experience on websockets connection using python websockets.
In article, you indicate that your websockets connections will automatically disconnected from the server.
I think is the mechanism call "ping-pong" in the websockets module cause this problem.
The mechanism default status is true, that means you will send a ping sign to the websocket server periodically, if the server doesn't send pong back to you, module will consider that server have been shutdown.So you need to do is just set the "ping-pong"
status to False.
async def connect():
async with connect(url, ssl=ssl.CERT_NONE,close_timeout = None,ping_interval = None) as websocket:
res = await websocket.recv()
print(res)
asyncio.get_event_loop().run_until_complete(connect())
This is all my personal opinion, you can try this out.
Why not using a higher level Python module like requests?
You could try the following:
import requests
params = (
('name', 'aW9Ccm9rZXI='),
)
response = requests.get('https://192.168.0.227:8002/api/v2/channels/samsung.remote.control', params=params)

grpc client dns resolution failed when trying to access grpc server on same network

I'm trying to call a GRPC server running on a .Net Core project from a Python client.
When running against localhost:5001 it works fine, but running against the actual IP of the machine from within the same network like 192.168.1.230:5001 it doesn't work and I get an error DNS resolution failed.
I've downloaded the SSL cert and am at the moment reading it as a file from the client. It works when running against localhost so I don't think that is the problem.
Is there a better way to do this kind of testing of having clients run on separate devices but on the same network as the server? Hosting the GRPC server outside during development doesn't really seem like the best solution.
Python code:
import grpc
import datamessage_pb2 as datamessage
import datamessage_pb2_grpc as datamessageService
def main():
print("Calling grpc server")
with open("localhost.cer", "rb") as file:
cert = file.read()
credentials = grpc.ssl_channel_credentials(cert)
channel = grpc.secure_channel("https://192.168.1.230:5001", credentials)
# channel = grpc.secure_channel("localhost:5001", credentials)
stub = datamessageService.StationDataHandlerStub(channel)
request = datamessage.StationDataModel(
temperature=22.3, humidity=13.3, soilMoisture=35.0)
result = stub.RegisterNewStationData(request)
print(result)
main()
Server settings in Program.cs:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseUrls("https://*:5001");
webBuilder.UseStartup<Startup>();
});
Settings in firewall:
Traceback:
grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with:
status = StatusCode.UNAVAILABLE
details = "DNS resolution failed"
debug_error_string = "{"created":"#1576101634.549000000","description":"Failed to pick subchannel","file":"src/core/ext/filters/client_channel/client_channel.cc","file_line":3934,"referenced_errors":[{"created":"#1576101634.549000000","description":"Resolver transient failure","file":"src/core/ext/filters/client_channel/resolving_lb_policy.cc","file_line":262,"referenced_errors":[{"created":"#1576101634.549000000","description":"DNS resolution failed","file":"src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc","file_line":202,"grpc_status":14,"referenced_errors":[{"created":"#1576101634.549000000","description":"OS Error","file":"src/core/lib/iomgr/resolve_address_windows.cc","file_line":96,"os_error":"No such host is known.\r\n","syscall":"getaddrinfo","wsa_error":11001}]}]}]}"
In Python gRPC client, calling channel without protocol (https:) is required. So, I called gRPC service in dotnet core framework with following and it worked. Note, dotnet gRPC server was listening on https://localhost:5001.
with open('localhost.crt', 'rb') as f:
credentials = grpc.ssl_channel_credentials(f.read())
with grpc.secure_channel('localhost:5001', credentials) as channel:
stub = pb2_grpc.GreeterStub(channel)
request = pb2.HelloRequest(name = "GreeterPythonClient")
response = stub.SayHello(request)

Detect when Websocket is disconnected, with Python Bottle / gevent-websocket

I'm using the gevent-websocket module with Bottle Python framework.
When a client closes the browser, this code
$(window).on('beforeunload', function() { ws.close(); });
helps to close the websocket connection properly.
But if the client's network connection is interrupted, no "close" information can be sent to the server.
Then, often, even 1 minute later, the server still believes the client is connected, and the websocket is still open on the server.
Question: How to detect properly that a websocket is closed because the client is disconnected from network?
Is there a websocket KeepAlive feature available in Python/Bottle/gevent-websocket?
One answer from Web Socket: cannot detect client connection on internet disconnect suggests to use a heartbeat/ping packet every x seconds to tell the server "I'm still alive". The other answer suggests using a setKeepAlive(true). feature. Would this feature be available in gevent-websocket?
Example server code, taken from here:
from bottle import get, template, run
from bottle.ext.websocket import GeventWebSocketServer
from bottle.ext.websocket import websocket
users = set()
#get('/')
def index():
return template('index')
#get('/websocket', apply=[websocket])
def chat(ws):
users.add(ws)
while True:
msg = ws.receive()
if msg is not None:
for u in users:
u.send(msg)
else:
break
users.remove(ws)
run(host='127.0.0.1', port=8080, server=GeventWebSocketServer)
First you need to add a timeout to the receive() method.
with gevent.Timeout(1.0, False):
msg = ws.receive()
Then the loop will not block, if you send even an empty packet and the client doesn't respond, WebsocketError will be thrown and you can close the socket.

Python requests returns “cannot connect to proxy & error 10061”

I have developed a desktop client using PyQt4, it connect to my web service by requests lib. You know, requests maybe one of the most useful http client, I think it should be no problem. My desktop client works all right until something strange happened.
I use the following code to send request to my server.
response = requests.get(url, headers = self.getHeaders(), timeout=600, proxies = {}, verify = False)
where header only includes auth token.
def getHeaders(self, additional = None):
headers = {
'Auth-Token' : HttpBasicClient.UserAuthToken,
}
if additional is not None:
headers.update(additional)
return headers
I cannot connect to my web service, all the http request pop the same error "'Cannot connect to proxy.', error(10061, '')". For example:
GET Url: http:// api.fangcloud.com/api/v1/user/timestamp
HTTPSConnectionPool(host='api.fangcloud.com', port=443): Max retries exceeded with url: /api/v1/user/timestamp (Caused by ProxyError('Cannot connect to proxy.', error(10061, '')))
this API does nothing but return the timestamp of my server. When I copy the url into Chrome in same machine with same environment, it returns correct response. However, my desktop client can only returns error. Is it anything wrong with requests lib?
I googled this problem of connection error 10061 ("No connection could be made because the target machine actively refused it"). This maybe caused by TCP connect rejection of web server.
The client sends a SYN packet to the server targeting the port (80 for HTTP). A server that is running a service on port 80 will respond with a SYN ACK, but if it is not, it will respond with a RST ACK. Your client reaches the server, but not the intended service. This is one way a server could “actively refuse” a connection attempt.
But why? My client works all right before and Chrome still works. I use no proxy on my machine. Is there anything I miss?
I notice there is a white space in URL, is that correct?
I tested in my ipython with requests.. that the response was:
{
"timestamp": 1472760770,
"success": true
}
For HTTP and HTTPS.

Tornado WebSocketHandler won't respond to SSL request

I have a Tornado HTTPServer initialized like so:
ssl_options = {
"certfile": "mycert.crt",
"keyfile": "mykey.key"
}
server = tornado.httpserver.HTTPServer(application, xheaders=True,
ssl_options=ssl_options)
It has a WebSocketHandler with an open() method.
When I attempt to open a secure websocket connection via Javascript from the Chrome 30 console, like
var sock = new WebSocket("wss://localhost:9001/mywebsocket");
the connection does not open successfully, there is no log output, and the WebSocketHandler's open() is not called.
How can I open and maintain a secure (SSL) WebSocket connection to a Javascript client in Tornado?
Attempting an insecure connection, like
var sock = new WebSocket("ws://localhost:9001/mywebsocket");
results in the following error output in the Tornado log:
2013-10-08 13:59:55,305 tornado.general 820 : SSL Error on 8 ('192.168.149.27', 62851): [Errno 1] _ssl.c:490: error:1407609C:SSL routines:SSL23_GET_CLIENT_HELLO:http request
Also in this case, the connection is not opened successfully and open() isn't called.
Additional info: The number after the IP in the error message (62851 in the above example) increases with every request, secure or otherwise. I don't know what that number is but it does indicate that the request is at least getting to the server.
Also, removing ssl_options from the constructor and making insecure (ws://) requests to the server fixes the issue.
The certificate being used is self-signed. To communicate with an HTTP endpoint of the server via curl, I had to use the --insecure flag.
If you're starting the connection from the javascript console, the browser doesn't have a chance to show you the self-signed certificate warning and give you a chance to accept it. If you go to https://localhost:9001 first and accept the certificate there, does it work?

Categories