파운드 : WebSocket을 처리하도록 구성


3

WebSocket 요청을 처리 하도록 파운드 를 구성 할 수 있습니까? 그렇지 않다면 리버스 프록시에 대한 가장 좋은 대안은 무엇입니까? 파운드 또는 이와 동등한 경량 리버스 프록시를 사용하고 싶습니다.


이미 웹 소켓을 지원하지 않습니까? 웹 소켓은 기존 프록시가이를 처리 할 수 ​​있도록 일반 HTTP 통신을 모방합니다.
John Dvorak

@JanDvorak-파운드가 지원할 수 있지만 작동하지 않는 것 같습니다. 내 실험 (프록시의 양쪽에서 tcpdump 캡처로 구성)에서 파운드는 프로토콜 업그레이드를 허용하고 페이로드의 처음 몇 바이트를 클라이언트로 다시 보낸 다음 전송을 중단 한 것으로 나타났습니다. 나는 그것이 효과가 없다는 것을 결정하는 것 이상으로 실험하지 않았다. Pound의 소스 코드를 통해 뛰어 다니는 해결책이 필요했습니다. :-P
ghoti

답변:


1

파운드는 프로토콜 업그레이드를 지원하는 코드를 포함하고있는 것으로 보이지만 작동하지 못했습니다. 포럼과 파운드 메일 링리스트에 다양한 사람들이 없습니다.

exratione.com 에는 파운드를 포함하여 SSL 뒤에 웹 소켓을로드 밸런싱하는 여러 옵션을 설명 하는 매우 자세한 게시물 이 있습니다. 이 게시물의 결론 (2012 년 초부터 시작)은 좋은 해결책 이 없다는 입니다.

그 게시물 이후, nginx는 websocket 프록시 지원을 추가했을 수 있으므로 살펴볼 가치가 있습니다. nginx는 구성 측면에서 조금 더 관련이 있으며 IIRC는 고정 세션 관리와 관련하여 몇 가지 제한 사항이 있지만 SSL을 지원하는 안정적이고 빠른 역방향 프록시입니다.

웹 소켓 연결에 SSL이 필요하지 않은 경우 간단한 TCP로드 밸런서를 시도 할 수 있습니다. 거기에서 선택하는 많은 사람들이있다 - HAProxy가 아니라 리눅스 사람들의 사랑을 받고 있지만, 간단하고 높은 품질의 대안처럼 존재 , 오픈 BSD의 relayd (또는 FreeBSD의 포트 ) 등

단일 백엔드 서버 앞에 리버스 프록시 만 필요하고로드 밸런스를 수행 할 필요가없는 경우 stunnel을 사용하여 프론트 엔드 HTTPS / WSS 연결을 수신하고 내부 백엔드에 연결할 수 있습니다. 다음은 샘플 stunnel 구성 입니다. 또는 pen 앞에서 stunnel을 사용할 수는 있지만 실험해야 할 것입니다.이 작업을 수행하지 않았으며 작동하는지 말할 수 없습니다. (시도하면 결과를 알려주십시오!)

최신 정보:

HAProxy 1.5.0은 2014 년 6 월 19 일에 릴리스되었습니다.이 버전에는 연결의 양쪽에 기본 SSL 지원이 포함되어 있습니다. 이는 현재 WebSocket 프록시에 대한 "선호"솔루션입니다. 구성은 매우 쉽습니다.

frontend http-in
    ...
    bind 192.0.2.1:80     # if you want
    bind 192.0.2.1:443 ssl crt /etc/ssl/yadda.pem
    use_backend ws if { hdr(Upgrade) -i WebSocket }

backend ws
    server node1 192.168.1.111:8000
    server node2 192.168.1.112:8000

또는 ACL을 사용하여 호스트 이름을 통해이를 수행 할 수 있습니다.

frontend http-in
    ...
    acl is_ws hdr_end(host) -i ws.example.com
    use_backend ws if is_ws

1

@ghoti의 대답은 잘 작동했으며 제안 된대로 stunnel을 계속 사용하지만이 문제는 그럼에도 불구 하고이 문제를 해결 했으므로 더 자세히 설명하지 않고 몇 가지 실험을했다고 주장한 @JanDvorak의 의견을 확장 할 것입니다.

https://gist.github.com/jkp/3136208 에서 파생 된 다음 간단한 파이썬 웹 소켓 서버를 사용했습니다.

import struct
import SocketServer
from base64 import b64encode
from hashlib import sha1
from mimetools import Message
from StringIO import StringIO

class WebSocketsHandler(SocketServer.StreamRequestHandler):
    magic = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'

    def setup(self):
        SocketServer.StreamRequestHandler.setup(self)
        print "connection established", self.client_address

    def handle(self):
        data = self.request.recv(1024).strip()
        headers = Message(StringIO(data.split('\r\n', 1)[1]))
        if headers.get("Upgrade", None) != "websocket":
            return
        print 'Handshaking...'
        key = headers['Sec-WebSocket-Key']
        digest = b64encode(sha1(key + self.magic).hexdigest().decode('hex'))
        response = 'HTTP/1.1 101 Switching Protocols\r\n'
        response += 'Upgrade: websocket\r\n'
        response += 'Connection: Upgrade\r\n'
        response += 'Sec-WebSocket-Accept: %s\r\n\r\n' % digest
        self.request.send(response)

        length = ord(self.rfile.read(2)[1]) & 127
        if length == 126:
            length = struct.unpack(">H", self.rfile.read(2))[0]
        elif length == 127:
            length = struct.unpack(">Q", self.rfile.read(8))[0]
        masks = [ord(byte) for byte in self.rfile.read(4)]
        decoded = ""
        for char in self.rfile.read(length):
            decoded += chr(ord(char) ^ masks[len(decoded) % 4])

        print decoded

        self.request.send(chr(129))
        length = len(decoded)
        if length <= 125:
            self.request.send(chr(length))
        elif length >= 126 and length <= 65535:
            self.request.send(126)
            self.request.send(struct.pack(">H", length))
        else:
            self.request.send(127)
            self.request.send(struct.pack(">Q", length))
        self.request.send(decoded)

        self.finish()

if __name__ == "__main__":
    server = SocketServer.TCPServer(
        ("localhost", 9000), WebSocketsHandler)
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        print "Got ^C"
        server.server_close();
        print "bye!"

그리고 http://www.websocket.org/echo.html 에서 빌린 다음 html과 결합했습니다 .

<!DOCTYPE html>
<meta charset="utf-8" />
<title>WebSocket Test</title>
<script language="javascript" type="text/javascript">
  var wsUri = "ws://localhost:9000/";
  var output;
  function init() {
    output = document.getElementById("output");
    testWebSocket();
  }
  function testWebSocket() {
    websocket = new WebSocket(wsUri);
    websocket.onopen = function(evt) { onOpen(evt) };
    websocket.onclose = function(evt) { onClose(evt) };
    websocket.onmessage = function(evt) { onMessage(evt) };
    websocket.onerror = function(evt) { onError(evt) };
  }
  function onOpen(evt) {
    writeToScreen("CONNECTED");
    doSend("WebSocket rocks");
  }
  function onClose(evt) {
    writeToScreen("DISCONNECTED");
  }
  function onMessage(evt) {
    writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
    websocket.close();
  }
  function onError(evt) {
    writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
  }
  function doSend(message) {
    writeToScreen("SENT: " + message); 
    websocket.send(message);
  }
  function writeToScreen(message) {
    var pre = document.createElement("p");
    pre.style.wordWrap = "break-word";
    pre.innerHTML = message;
    output.appendChild(pre);
  }
  window.addEventListener("load", init, false);
</script>
<h2>WebSocket Test</h2>
<div id="output"></div>
</html>

이것은 잘 작동 했으므로 다음 구성 항목으로 파운드를 중간에 넣었습니다.

ListenHTTP
    Address 127.0.0.1
    Port    9999
    Service
            BackEnd
                    Address 127.0.0.1
                    Port    9000
            End
    End
End

HTML의 포트를 9000에서 9999로 변경했습니다. 변경 후 작동이 중지되었습니다.

wireshark로 트래픽을 분석 한 결과 프로토콜 전환을위한 HTTP 101 요청이 올바르게 전달된다는 것을 알았습니다. 그러나 후속 웹 소켓 패킷은 절대 파운드로 전달되지 않습니다. 이것은 중간에 파운드가있는 메시지를 print수신하지 않는 파이썬 서버 스크립트 의 출력으로 확인됩니다 WebSocket rocks.

pound이 WebSocket 메시지를 수신 할 때마다 메시지를 삭제하고 대신 e414 headers: request URI too longsyslog에 씁니다 . 파운드 소스 코드를 보면 파운드가 HTTP 헤더를 구문 분석하려고하기 때문입니다. 이를 위해 먼저 WebSocket 메시지에서 찾을 수없는 EOL을 검색하여 메시지를 유효하지 않은 것으로 삭제합니다.

따라서 OP의 질문에 대한 대답은 실제로 파운드는 WebSocket을 수행 할 수없는 것 같습니다.

이 문제에 대해 파운드리스트에 이메일을 썼습니다 : http://www.apsis.ch/pound/pound_list/archive/2014/2014-01/1388844924000

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.