Go의 WebSocket에서 HTTP 프록시 대칭 이동(섹션 1)

21885 단어 webdevgowebsocket

핵심 배달

  • 웹소켓의 역방향 HTTP 에이전트는 프록시 서버로 웹소켓 프로토콜을'터널'로 사용하여 TCP 통신을 서버에서 클라이언트로 전달한다.
  • 은 고 프로젝트에서 gorilla/websocket은 웹소켓을 실현하는 데 광범위하게 사용된다.
  • WebSocket 디자인은 HTTP 작업에 사용됩니다.HTTP와의 호환성을 실현하기 위해 웹소켓은 HTTP Upgrade 헤더를 사용하여 HTTP 프로토콜에서 웹소켓 프로토콜로 변경할 수 있습니다.
  • 웹소켓의 신분 검증은 어려운 디자인 문제로 많은 선택이 있다.
  • WebSocket의 역방향 HTTP 프록시


    에이전트는 두 가지 유형이 있는데, 정방향 에이전트와 반방향 에이전트이다.
    전달 에이전트(또는 게이트웨이, 터널 또는 "에이전트")는 클라이언트 그룹에 대한 에이전트 서비스를 제공합니다.사용자는 웹 서비스 또는 API 서버에 액세스할 때 IP 주소를 숨길 수 있습니다.계약서에서 reverse proxy은 기한부 대리와 상반된다.그것은 클라이언트가 서버에서 자원을 검색하는 것을 대표하는 프록시 서버이다.
    그 밖에 본고는 웹소켓의 역방향 HTTP 에이전트를 중점적으로 소개했다. 간단하게 말하면 웹소켓 프로토콜을'터널'로 사용하여 TCP 통신을 서버에서 클라이언트에게 전달한다.

    바둑의 사례 연구


    WebSocket에서 역방향 에이전트를 실현한 예는 많지 않다.자바스크립트에서 mhzed/wstunnel은 모두가 알고 있고 Haskell에서 erebe/wstunnel은 모두가 알고 있다.
    Go에서 inconshreveable/ngrokcoyove/goflyway은 모두가 알고 있다. 특히 ngrok은 SaaS 서비스로 개발자들에게 인기가 많다.
    이 글에서 우리는 기본 개념을 중점적으로 이해하고 원형과 더욱 간단한 root-gg/wsp을 읽을 것이다.wsp는 root-gg에서 개발한 것으로 웹소켓의 역방향 HTTP 에이전트로 외부에서 내부 API를 안전하게 호출하는 것이 목적이다.그것은 생산에서 사용하기 어렵지만 웹소켓의 역방향 에이전트를 설명하는 디자인은 좋은 학습 자료이다.
    그러나 Go 1.6이 발표된 이래로 유지보수 업무가 중단되었기 때문에 저는 코드 hgsgtk/wsp에 따라 이 글을 계속 쓸 것입니다. 저는 2021년에 Go의 상황을 위해 코드 root-gg을 수정했습니다. (감사합니다. hgsgtk )

    wsp /


    Websocket의 HTTP 터널


    http://localhost:8081 wsp 프레젠테이션


    만약 네가 그것이 운행하는 것을 본다면, 너는 그것의 작업 원리에 대해 대체적으로 이해하기 쉬울 것이다. 그래서 여기에 간단한 설명이 있다.이 프레젠테이션에서는 다음 네 가지 시스템 구성 요소에 대해 설명합니다.
  • 내부 API(http://localhost:8080): 리버스 프록시
  • 을 통해 요청할 서버
  • wsp 클라이언트: 내부 API
  • 의 네트워크에서 실행
  • wsp 서버(): 네트워크 외부에서
  • 실행
  • 어플리케이션: 내부 API
  • 과 통신 시작

    WebSocket 통신을 통해 엔드포인트 /hello에 내부 API 요청을 전송합니다.
    $ curl -H 'X-PROXY-DESTINATION: http://localhost:8081/hello' http://127.0.0.1:8080/request
    
    HTTP 통신은 다음 라우트를 통해 트렁킹됩니다.
    app -> wsp server -(WebSocket)-> wsp client -> internal API
    
    다음은 응용 프로그램에서 HTTP 요청을 보낼 때의 터미널 이미지입니다.


    내부 네트워크는 외부로부터 들어오는 요청을 제한하거나 글로벌 IP가 없기 때문에 외부 서버는 내부 네트워크에서 시작하는 클라이언트에 연결할 수 없습니다.예를 들어 클라이언트가 로컬 PC에서 시작하면 외부 서버에서 클라이언트에게 요청을 보낼 수 없습니다.

    따라서 기점은 내부 네트워크의 wsp 클라이언트입니다.이 디자인에서는 wsp 클라이언트에서 wsp 서버로의 WebSocket 연결을 요청하는 것부터 시작합니다.

    아래의 몇 가지는 본문에서 해석할 것이다.
  • WebSocket 서버 시작
  • 나는 두 번째 부분과 이후에 나머지 몇 가지를 설명할 것이다.
  • WebSocket 연결 구축
  • 연결 유지
  • Trunking Application에서 WebSocket
  • 피어에 대한 TCP 접속
  • WebSocket 데이터의 TCP 연결을 "내부 API"
  • 으로 트렁크

    듀플렉스 WebSocket 서버 시작


    첫 번째 단계는 웹소켓 서버를 시작하여 웹소켓 클라이언트의 요청을 기다리는 것입니다.
    한편, 웹소켓은 웹에서 저비용 RFC 6455 통신을 하는 메커니즘으로 이 협의는 Wikipedia으로 표준화되었다.다음 도표는 에서 인용하여 클라이언트와 서버 간의 웹소켓을 이용한 통신을 설명한다.
    cmd/wsp_server/main.go
    wsp로 말하자면, 그것은 이렇게 시작되었다.
    $ ./wsp_server -config examples/wsp_server.cfg
    {
      "Host": "127.0.0.1",
      "Port": 8080,
      "Timeout": 1000,
      "IdleTimeout": 60000,
      "Whitelist": [],
      "Blacklist": [],
      "SecretKey": ""
    }
    
    비밀번호를 읽어 봅시다.우선main 함수(server.NewServer)로 입구점이다.
    package main
    
    import (
        // (omit)
    )
    
    func main() {
        // (omit)
    
        server := server.NewServer(config)
        server.Start()
    
        // (omit: shutdown)
    }
    
    Server 함수는 WebSocket의 역방향 HTTP 에이전트를 나타내는 websocket.Upgrader struct의 포인터를 반환합니다.
    type Server struct {
        Config *Config
    
        upgrader websocket.Upgrader
    
        pools []*Pool
        lock  sync.RWMutex
        done  chan struct{}
    
        dispatcher chan *ConnectionRequest
    
        server *http.Server
    }
    
    중요한 필드는 HTTP 연결을 WebSocket 연결로 업그레이드하는 매개 변수를 지정하는 upgrader입니다.Go 프로젝트에서 gorilla/websocket은 웹소켓을 실현하는 데 광범위하게 사용되고 이 라이브러리를 사용하는 많은 예시를 인터넷에서 찾을 수 있다.
    웹소켓은 HTTP에서 작업하도록 설계되었습니다.HTTP와의 호환성을 실현하기 위해 웹소켓은 HTTP Upgrade 헤더를 사용하여 HTTP 프로토콜에서 웹소켓 프로토콜로 변경할 수 있습니다.
    Upgrader 구조의 필드는 다음과 같습니다.
    type Upgrader struct {
        HandshakeTimeout time.Duration
        ReadBufferSize, WriteBufferSize int
        Subprotocols []string
        Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
        CheckOrigin func(r *http.Request) bool
        EnableCompression bool
    }
    
    WebSocket 매개 변수나gorilla/WebSocket 내부 구현에 대한 자세한 정보는 본문을 참조하십시오.NewServer 함수에서 기본 매개 변수를 사용하여 websocket.Upgrader 형식의 값을 만듭니다.
    func NewServer(config *Config) (server *Server) {
        // (omit)
    
        server.upgrader = websocket.Upgrader{}
    
        // (omit)
        return
    }
    
    그런 다음 server.Start 함수를 호출하여 WebSocket 서버를 시작합니다.
    func (server *Server) Start() {
        // (omit)
    
        r := http.NewServeMux()
        r.HandleFunc("/request", server.request)
    
        // (omit)
    
        server.server = &http.Server{
            Addr:    server.Config.GetAddr(),
            Handler: r,
        }
        go func() { log.Fatal(server.server.ListenAndServe()) }()
    
    이 코드에서는 지정된 주소에 공개 엔드포인트 /request의 HTTP 서버를 설정합니다./request에 매핑되는 프로세스는 다음과 같습니다(code).
    func (server *Server) register(w http.ResponseWriter, r *http.Request) {
        secretKey := r.Header.Get("X-SECRET-KEY")
        if secretKey != server.Config.SecretKey {
            wsp.ProxyErrorf(w, "Invalid X-SECRET-KEY")
            return
        }
    
        ws, err := server.upgrader.Upgrade(w, r, nil)
        if err != nil {
            wsp.ProxyErrorf(w, "HTTP upgrade error : %v", err)
            return
        }
    
        // (omit)
    }
    
    WebSocket 서버를 실현하는 데는 몇 가지 지식이 있기 때문에 한 줄 한 줄 설명하겠습니다.

    WebSocket 인증


    첫 번째 행은 다음과 같습니다.
    secretKey := r.Header.Get("X-SECRET-KEY")
    if secretKey != server.Config.SecretKey {
        wsp.ProxyErrorf(w, "Invalid X-SECRET-KEY")
        return
    }
    
    WebSocket 프로토콜은 인증 또는 인증을 처리하지 않습니다.그러나 프로토콜이 준비되지 않았기 때문에 누구나 신분 검증 없이 웹소켓 서버에 접근할 수 있다는 것이 문제다.
    비즈니스 요구사항은 일반적으로 연결된 WebSocket 클라이언트를 인증하고 등록된 고객 기록과 연결시켜야 한다.
    몇 가지 모델이 신분 검증을 실현할 수 있는데 Heroku Dev Center - WebSocket Security은'표'를 바탕으로 하는 신분 검증 시스템 모델을 묘사했다.
  • 클라이언트는 라이센스 "티켓"을 얻기 위해 서버에 연락합니다.
  • 서버 스토리지 어음
  • 및 클라이언트
  • 에게 반환
  • 클라이언트가 WebSocket 연결을 열고 이 "티켓"을 초기 핸드셰이크의 일부로 보냅니다.
  • 서버는 이 티켓을 비교하여 사용자의 조건(소스 IP)을 확인할 수 있습니다.
  • Armin Ronacher의 게시물 Websockets 101의 인증 및 IP 섹션을 참조하십시오.
    클라이언트에서 서버로의 "어음"의 위치와 방식에 대해 몇 가지 옵션이 있습니다.간단한 신분 검증의 일반적인 방법은 HTTP 헤더에 관련 티켓을 설정하는 것이다.예를 들어 Amazon API Gateway은 API 클라이언트가 API 키를 X-API-Key 헤더로 전달하도록 요구하는 옵션을 제공합니다.
    예시 코드는 유사한 개념을 실현했다.그것은 처음 악수를 할 때 머리 X-SECRET-KEY의 값을 검증한다.
    secretKey := r.Header.Get("X-SECRET-KEY")
    
    또한 귀하의 업무 수요에 따라'벌금 고지서'를 어떻게 발급하는지 선택할 수 있는 몇 가지 옵션이 있습니다.한 가지 아이디어는 관리 UI에 '티켓' (키에 접근하거나 영패에 접근하는 것) 을 미리 발표하는 것이다.또는 악수를 하기 전에 웹소켓 서버가 어디에 연결되는지 물어보는 과정이 포함된다면 미리 보낸 '방문 영패' 를 통해 일회용 '티켓' 을 보내는 것이 좋다.
    "티켓"기반 인증 시스템에서 서버는 처음 악수를 할 때만 인증을 하지만 메시지마다 인증을 한다는 생각도 있다.클레어 토마스의 게시물을 보십시오.

    핸드셰이크(HTTP 업그레이드)


    인증 후 HTTP 연결을 WebSocket 연결로 업그레이드합니다.
    ws, err := server.upgrader.Upgrade(w, r, nil)
    if err != nil {
        wsp.ProxyErrorf(w, "HTTP upgrade error : %v", err)
        return
    }
    
    websocket.Upgrader.Upgrade 함수는 이 점을 실현했다.간단하게 말하면 클라이언트로부터의 악수 요청이 유효한지 확인하고 유효하면 성공적인 악수 응답을 되돌려줍니다.
    i, e. 유효한 악수 요청
    GET /register HTTP/1.1
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
    Origin: http://example.com
    Sec-WebSocket-Version: 13
    
    성공적인 악수 응답
    HTTP/1.1 101 Switching Protocols
    Upgrade: websocket
    Connection: Upgrade
    Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
    
    Upgrader.Upgrade 함수의 내부 구현에 대해 알고 싶으면 을 참조하십시오.

    결론


    이 글은 웹소켓 서버를 어떻게 시작하는지, 웹소켓 연결을 구축하는 웹소켓 규범과 안전 주의사항을 설명한다.
    나는 두 번째 부분과 이후에 나머지 몇 가지를 설명할 것이다.
  • WebSocket 연결 설정()

  • 연결 유지
  • Trunking Application에서 WebSocket
  • 피어에 대한 TCP 접속
  • WebSocket 데이터의 TCP 연결을 "내부 API"
  • 으로 트렁크

    좋은 웹페이지 즐겨찾기