websocket

package module
v0.0.0-...-d107793 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 24, 2023 License: Apache-2.0 Imports: 15 Imported by: 0

README

WebSocket implementation for Go

License Go Report Card GoDoc

Description

A simple websocket protocol implementation for Golang.

Installation

go get -u github.com/RommHui/websocket

Examples

0x01 New

use the method New to connect to the websocket server through a URL

package main

import (
    "bytes"
    "fmt"
    "github.com/RommHui/websocket"
)

func main() {
    ws, err := websocket.New("http://example.com/ws")
    if err != nil {
        panic(err)
    }
    defer ws.Close()
    
    err = ws.Ping() // try to detect if the connection is normal.
    if err != nil {
        panic(err)
    }
    
    err = ws.Send("Hello World") // send a text to server.
    if err != nil {
        panic(err)
    }
    
    message, err := ws.ReadMessage() // receive a message from server.
    if err != nil {
        panic(err)
    }
    buf := &bytes.Buffer{}
    _, err = buf.ReadFrom(message)
    if err != nil {
        panic(err)
    }
    fmt.Println(buf.String())
}
0x02 SendMessage

use the method SendMessage to send a frame package as you wish

package main

import (
    "bytes"
    "github.com/RommHui/websocket"
)

func main() {
    ws, err := websocket.New("http://example.com/ws")
    if err != nil {
        panic(err)
    }
    defer ws.Close()

    err = ws.SendMessage(&websocket.Message{
        Reader: bytes.NewBufferString("Hello"),
        OpCode: websocket.TextFrame,
    })
    if err != nil {
        panic(err)
    }
}
0x03 Pair

use the method Pair to pair a websocket connection from client

package main

import (
    "github.com/RommHui/websocket"
    "net/http"
)

func main() {
    http.HandleFunc("/ws", func(writer http.ResponseWriter, request *http.Request) {
        ws,err := websocket.Pair(writer,request)
        if err != nil {
            return
        }
        defer ws.Close()
        
        err = ws.Send("Hi")
        if err != nil {
            return
        }
    })
}
0x04 NewWebSocket

use NewWebSocket can using WebSocket protocol as a independence application layer protocol to transmit data

// server
package main

import (
    "github.com/RommHui/websocket"
    "net"
)

func main() {
    listener, err := net.Listen("tcp", "0.0.0.0:8080")
    if err != nil {
        panic(err)
    }
    defer listener.Close()

    for {
        clientConn, acceptErr := listener.Accept()
        if acceptErr != nil {
            panic(acceptErr)
        }
        go func(conn net.Conn) {
            ws := websocket.NewWebSocket(conn,conn,true)
            defer ws.Close()
            err := ws.Ping()
            if err != nil {
                return
            }
            err = ws.Send("HI")
            if err != nil {
                return
            }
        }(clientConn)
    }
}
// client
package main

import (
    "bytes"
    "fmt"
    "github.com/RommHui/websocket"
    "net"
)

func main() {
    conn, err := net.Dial("tcp", "127.0.0.1:8080")
    if err != nil {
        panic(err)
    }

    ws := websocket.NewWebSocket(conn, conn, true)
    defer ws.Close()

    message, err := ws.ReadMessage()
    if err != nil {
        panic(err)
    }

    buf := &bytes.Buffer{}
    _, err = buf.ReadFrom(message)
    if err != nil {
        panic(err)
    }
    fmt.Println(buf.String())
}
0x05 Connect

use the method Connect to addition some authorization information to the http request

package main

import (
    "context"
    "github.com/RommHui/websocket"
    "net/http"
)

func main() {
    request, err := http.NewRequest("GET", "http://example.com/ws", nil)
    if err != nil {
        panic(err)
    }
    request.Header.Set("authorization", "Bearer a1b2c3d4")
    request.Header.Set("user-agent", "testing websocket client")

    ws,err := websocket.Connect(context.Background(),request)
    if err != nil {
        panic(err)
    }
    defer ws.Close()
    
    err = ws.Send("Hello World")
    if err != nil {
        panic(err)
    }
}
0x06 ServerPair

use ServerPair to enable websocket support in your raw TCP listening program

package main

import (
	"github.com/RommHui/websocket"
	"net"
)

func main() {
    listener, err := net.Listen("tcp", "0.0.0.0:8080")
    if err != nil {
        panic(err)
    }
    defer listener.Close()

    for {
        clientConn, acceptErr := listener.Accept()
        if acceptErr != nil {
            panic(acceptErr)
        }
        go func(conn net.Conn) {
            ws,err := websocket.ServerPair(conn,conn)
            if err != nil {
                return
			}
            defer ws.Close()
            err = ws.Ping()
            if err != nil {
                return
            }
            err = ws.Send("HI")
            if err != nil {
                return
            }
        }(clientConn)
    }
}
0x07 Two in One

use the method NewWebSocket to make tow HTTP request as a websocket connection

package main

import (
    "github.com/RommHui/websocket"
    "io"
    "net/http"
)

func write(id string, url string) (io.WriteCloser, error) {
    reader, writer := io.Pipe()
    req, err := http.NewRequest("PUT", url, reader)
    if err != nil {
        return nil, err
    }
    req.Header.Set("connection", id)
    _, err = http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    return writer, nil
}

func read(id string, url string) (io.ReadCloser, error) {
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        return nil, err
    }
    req.Header.Set("connection", id)
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    return resp.Body, nil
}

func main() {
    writer, err := write("123456", "http://127.0.0.1:8080")
    if err != nil {
        panic(err)
    }
    reader, err := read("123456", "http://127.0.0.1:8080")
    if err != nil {
        panic(err)
    }
    ws := websocket.NewWebSocket(writer,reader,false)
    defer ws.Close()
    
    err = ws.Ping()
    if err != nil {
        panic(err)
    }
}

Documentation

Index

Constants

View Source
const (
	OPEN uint8 = iota + 1
	CLOSING
	CLOSED
)

Variables

View Source
var (
	ErrClosedStatus = errors.New("WebSocket is already in CLOSING or CLOSED state")
)
View Source
var ErrHijackResponseWriterFailed = errors.New("hijack the http.ResponseWriter failed")
View Source
var ErrPreviousMessageNotReadToCompletion = errors.New("previous message not read to completion")
View Source
var OpCodeName = []string{
	ContinuationFrame:        "ContinuationFrame",
	TextFrame:                "TextFrame",
	BinaryFrame:              "BinaryFrame",
	ReservedNonControlFrame1: "ReservedNonControlFrame1",
	ReservedNonControlFrame2: "ReservedNonControlFrame2",
	ReservedNonControlFrame3: "ReservedNonControlFrame3",
	ReservedNonControlFrame4: "ReservedNonControlFrame4",
	ReservedNonControlFrame5: "ReservedNonControlFrame5",
	ConnectionClose:          "ConnectionClose",
	Ping:                     "Ping",
	Pong:                     "Pong",
	ReservedControlFrame1:    "ReservedControlFrame1",
	ReservedControlFrame2:    "ReservedControlFrame2",
	ReservedControlFrame3:    "ReservedControlFrame3",
	ReservedControlFrame4:    "ReservedControlFrame4",
	ReservedControlFrame5:    "ReservedControlFrame5",
}

Functions

This section is empty.

Types

type Frame

type Frame struct {
	Payload *io.LimitedReader
	Fin     bool
	Mask    bool
	OpCode  OpCode
}

func (*Frame) Decode

func (f *Frame) Decode(ctx context.Context, reader io.Reader) error

Decode 用于从 io.Reader 中反序列化到 Frame

func (*Frame) Encode

func (f *Frame) Encode() io.Reader

Encode 用于从 Frame 中把数据序列化

func (*Frame) String

func (f *Frame) String() string

type Message

type Message struct {
	io.Reader
	OpCode OpCode
}

type OpCode

type OpCode byte
const (
	ContinuationFrame OpCode = iota
	TextFrame
	BinaryFrame
	ReservedNonControlFrame1
	ReservedNonControlFrame2
	ReservedNonControlFrame3
	ReservedNonControlFrame4
	ReservedNonControlFrame5
	ConnectionClose
	Ping
	Pong
	ReservedControlFrame1
	ReservedControlFrame2
	ReservedControlFrame3
	ReservedControlFrame4
	ReservedControlFrame5
)

func (OpCode) String

func (o OpCode) String() string

type WebSocket

type WebSocket interface {
	// Send 发送文本数据
	Send(text string) error

	// Ping 是用来发送一个 ping 帧,判断连接情况。
	// 如果有错误,代表连接有问题。
	// 否则就代表连接一切正常。
	Ping() error

	// Close 用于关闭 WebSocket 对象的流
	Close() error

	// Status 用于获取 WebSocket 对象的状态
	Status() uint8

	// ReadMessage 用于接收 Message 数据
	ReadMessage() (*Message, error)

	// SendMessage 用于发送 Message 数据
	SendMessage(message *Message) error
}

func Connect

func Connect(ctx context.Context, request *http.Request) (WebSocket, error)

Connect 使用一个 HTTP 请求来创建 WebSocket 对象。 可以通过设置环境变量 ALL_PROXY 来使用代理服务器。 传入 HTTP 请求的方法,可以用于需要验证的 WebSocket 连接,自定义添加验证信息到请求头中。

func ConnectWithDialer

func ConnectWithDialer(ctx context.Context, dialer func(context.Context, string, string) (net.Conn, error), request *http.Request) (WebSocket, error)

ConnectWithDialer 传入自定义 dialer,然后创建一个 WebSocket 。 这个函数主要考虑是用于自定义代理方法来连接目标 WebSocket。

func New

func New(url string) (WebSocket, error)

New 使用 url 链接来创建一个 WebSocket 对象。 可以通过设置环境变量 ALL_PROXY 来使用代理服务器。

例子1:wss://ws.postman-echo.com/raw/ 例子2:http://example.com/ws

func NewWebSocket

func NewWebSocket(writer io.WriteCloser, reader io.ReadCloser, mask bool) WebSocket

NewWebSocket 使用 io.WriteCloser 和 io.ReadCloser 创建一个 WebSocket 对象。 io.WriteCloser 是输出流,io.ReadCloser 是输入流,不一定要同一条双向流的 io.WriteCloser 和 io.ReadCloser。 这样的好处就是,可以使用 2 条单向的流,模拟成 1 条双向的流。 使用 NewWebSocket 这个函数,就可以单独的去使用 WebSocket 协议,无需经过 HTTP 的 Connection Upgrade 到 WebSocket ,也就是可以让一条纯 TCP 连接去使用。

func Pair

func Pair(w http.ResponseWriter, req *http.Request) (WebSocket, error)

Pair 用于 HTTP 服务端接收一个 WebSocket 对象

使用例子:

http.HandleFunc("/ws",func(w http.ResponseWriter,request *http.Request){
	ws,err := websocket.Pair(w,request)
	if err != nil {
		return
	}
	fmt.Println(ws)
})
http.ListenAndServe("0.0.0.0:8080")

func ServerPair

func ServerPair(writer io.WriteCloser, reader io.ReadCloser) (WebSocket, error)

ServerPair 用于传入 io.WriteCloser 和 io.ReadCloser 来创建 WebSocket。 可以用于自己编写的 WEB 服务来创建一个 WebSocket 对象。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL