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

IIS reverse proxy #1192

Closed
ramezanifar opened this issue Jul 14, 2022 · 6 comments
Closed

IIS reverse proxy #1192

ramezanifar opened this issue Jul 14, 2022 · 6 comments

Comments

@ramezanifar
Copy link

ramezanifar commented Jul 14, 2022

My goal is to run a websocket server in local host and the clients access it via Windows Web Server IIS acting as a reverse proxy server.
Server runs at localhost:5005 as shown below:

#!/usr/bin/env python

import asyncio
import websockets

async def echo(websocket):
    async for message in websocket:
        print(message)
        await websocket.send(message)

async def main():
    async with websockets.serve(echo, "localhost", 5005):
        await asyncio.Future()  # run forever

asyncio.run(main())

Then in IIS:

  • In Application Request Routing Cache --> Server Proxy Setting --> Enable proxy, I checked.
  • Under Sites --> Default Web Site, I added an application named app
  • Using URL Rewrite, I added the following rule:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="ReverseProxy" stopProcessing="true">
                    <match url="(.*)" />
                    <action type="Rewrite" url="http://localhost:5005" logRewrittenUrl="true" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

Now a client should be able to connect from ws://localhost/app. However connection fails and I get this exception in server:

connection handler failed
Traceback (most recent call last):
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 945, in transfer_data
    message = await self.read_message()
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 1015, in read_message
    frame = await self.read_data_frame(max_size=self.max_size)
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 1090, in read_data_frame
    frame = await self.read_frame(max_size)
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 1149, in read_frame
    extensions=self.extensions,
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\framing.py", line 70, in read
    data = await reader(2)
  File "C:\Program Files\Python37\lib\asyncio\streams.py", line 677, in readexactly
    raise IncompleteReadError(incomplete, n)
asyncio.streams.IncompleteReadError: 0 bytes read on a total of 2 expected bytes
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\server.py", line 232, in handler
    await self.ws_handler(self)
  File "D:/Robotic Framework/RoboticApps/UI_Framework/server.py", line 7, in echo
    async for message in websocket:
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 482, in __aiter__
    yield await self.recv()
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 553, in recv
    await self.ensure_open()
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 921, in ensure_open
    raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedError: no close frame received or sent

To make sure if IIS settings are correct, I used another websocket library named "simple-websocket-server" and it worked fine. Here is the server implementation:

from simple_websocket_server import WebSocketServer, WebSocket


class SimpleEcho(WebSocket):
    def handle(self):
        # echo message back to client
        self.send_message(self.data)

    def connected(self):
        print(self.address, 'connected')

    def handle_close(self):
        print(self.address, 'closed')


server = WebSocketServer('localhost', 5005, SimpleEcho)
server.serve_forever()

Can anyone tell me what the issue is?
I tried both an html client and a python client (unsuccessful with websockets module, successful with simple-websocket-server module).
Thank you

@aaugustin
Copy link
Member

I'm not seeing anything wrong with what you describe.

The exception shown by websockets suggests that IIS closed the TCP connection. However, we don't know why.

Perhaps turning on debug logs would give you a clue?

At worst, you can figure out which side closed the TCP connection with Wireshark, but that's pretty low level.

@ramezanifar
Copy link
Author

ramezanifar commented Jul 15, 2022

Here is the output with debug after a client attempts to connect:

= connection is CONNECTING
< GET / HTTP/1.1
< Cache-Control: no-cache
< Connection: Upgrade
< Pragma: no-cache
< Upgrade: websocket
< Accept-Encoding: gzip, deflate, br, peerdist
< Accept-Language: en-US,en;q=0.9
< Host: localhost:5005
< Max-Forwards: 10
< User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
< Origin: null
< Sec-WebSocket-Version: 13
< Sec-WebSocket-Key: /C9ah/z+wAo4mnoACSz2IA==
< Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
< X-Original-URL: /app
< X-Forwarded-For: [::1]:52798
< X-ARR-LOG-ID: 8c59749a-4e5d-43cf-8a5a-c4421cc4ecc8
< X-P2P-PeerDist: Version=1.1
< X-P2P-PeerDistEx: MinContentInformation=1.0, MaxContentInformation=2.0
> HTTP/1.1 101 Switching Protocols
> Upgrade: websocket
> Connection: Upgrade
> Sec-WebSocket-Accept: W9Sttf7fHX3aB9zd4b/PVGt0Ldg=
> Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=12; client_max_window_bits=12
> Date: Fri, 15 Jul 2022 19:14:32 GMT
> Server: Python/3.7 websockets/10.3
connection open
= connection is OPEN
! failing connection with code 1006
= connection is CLOSED
x half-closing TCP connection
connection handler failed
Traceback (most recent call last):
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 945, in transfer_data
    message = await self.read_message()
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 1015, in read_message
    frame = await self.read_data_frame(max_size=self.max_size)
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 1090, in read_data_frame
    frame = await self.read_frame(max_size)
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 1149, in read_frame
    extensions=self.extensions,
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\framing.py", line 70, in read
    data = await reader(2)
  File "C:\Program Files\Python37\lib\asyncio\streams.py", line 677, in readexactly
    raise IncompleteReadError(incomplete, n)
asyncio.streams.IncompleteReadError: 0 bytes read on a total of 2 expected bytes
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\server.py", line 232, in handler
    await self.ws_handler(self)
  File "D:/Robotic Framework/RoboticApps/UI_Framework/server.py", line 12, in echo
    async for message in websocket:
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 482, in __aiter__
    yield await self.recv()
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 553, in recv
    await self.ensure_open()
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 921, in ensure_open
    raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedError: no close frame received or sent
connection closed

Does this give any useful information?

Please note that IIS proxy works well with two different websocket servers that I tried (python simple-websockets-server module , and a Node.js server). That is why I think my IIS setting configurations are fine.
Thanks

@ramezanifar
Copy link
Author

I asked this question in stack overflow. Some people tried it and got the same results. There could be potentially an issue in the module.
In the documentation there is a guide for using nginx as proxy server. That would be really helpful if one of the developers provide a guide for IIS.
Thanks

@aaugustin
Copy link
Member

aaugustin commented Jul 20, 2022

... the minor issue being that "one of the developers" is probably "me" and I don't have a Windows install around nor knowledge of IIS :-(

Typically, cases of "$SETUP works with $LIBRARY but not websockets" happen because websockets provides more features than $LIBRARY, breaking $SETUP.

Let's try the most frequent offender -- could you add compression=None here:

    ...
    async with websockets.serve(echo, "localhost", 5005, compression=None):
        ...

and see if that helps?

This old stackoverflow question suggests that IIS disliskes compression on WebSocket connections.

@ramezanifar
Copy link
Author

ramezanifar commented Jul 21, 2022

Thanks @aaugustin for the solution. With that parameter set to off, it works just fine.
I had come across this instruction saying how to enable compression in IIS. Evidently it does not help. With your hint about IIS not liking to compress, I found this that also says ARR3.0 doesn't support WebSocket compression.
At this time, ARR3.0 (Application Request Routing) which is installed and added to IIS10, is the last version so we should probably wait for the future releases.
For other users seeking a guide to set the IIS as a reverse proxy, there is pictorial guide here.
@aaugustin If you like to answer the question in stack overflow, it is here.
Thank you very much

@aaugustin
Copy link
Member

Good to hear that it's resolved!

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

No branches or pull requests

2 participants