├── async_sendfile └── test.go ├── bench ├── client │ └── client.go └── server │ └── server.go ├── echo ├── client │ └── client.go └── server │ ├── server.go │ └── server_test.go ├── fixedbufferpool └── fixedbufferpool.go ├── go.mod ├── go.sum ├── http ├── client │ └── client.go ├── nbclient │ └── client.go ├── server │ └── server.go ├── server_tls │ ├── server.crt │ ├── server.go │ └── server.key └── stdserver │ └── server.go ├── http_1m ├── client │ └── client.go ├── fastserver │ └── server.go ├── server │ └── server.go └── stdserver │ └── server.go ├── http_trailer ├── client │ └── client.go └── server │ └── server.go ├── http_with_other_frameworks ├── client │ └── client.go ├── echo_server │ └── echo_server.go ├── fiber_server │ └── fiber_server.go ├── gin_server │ └── gin_server.go ├── go-chi_server │ └── go-chi_server.go └── iris_server │ └── iris_server.go ├── iomod └── iomod.go ├── lt_et └── lt_et.go ├── netstd ├── client │ └── client.go └── server │ └── server.go ├── shutdown └── server.go ├── sticky ├── main.go └── proxy │ └── proxy.go ├── tls ├── client │ └── client.go ├── client_std │ └── client.go ├── server │ └── server.go └── test │ └── tls_test.go ├── tls_bench ├── client │ └── client.go ├── client_std │ └── client.go └── server │ └── server.go ├── tls_sticky_packet ├── client │ └── client.go └── server │ └── server.go ├── traffic_forward_tcp └── proxy.go ├── traffic_forward_udp └── proxy.go ├── udp ├── client │ └── client.go ├── client_std │ └── client.go ├── server │ └── server.go └── server_std │ └── server.go ├── websocket ├── client │ └── client.go ├── data_uploader │ ├── client │ │ └── client.go │ └── server │ │ └── server.go ├── nbioclient │ └── client.go ├── server │ └── server.go ├── server_manual_frame_assembly │ ├── README.md │ └── server.go └── server_sticky │ └── server.go ├── websocket_1m ├── client │ └── client.go ├── nbioclient │ └── client.go ├── nhooyr │ └── server.go ├── server_nbio │ └── server.go └── server_net │ └── server.go ├── websocket_1m_tls ├── client │ └── client.go ├── nbioclient │ └── client.go ├── server_nbio │ └── server.go └── server_net │ ├── server.crt │ ├── server.go │ └── server.key ├── websocket_both_tls_nontls ├── client │ └── client.go └── server │ └── server.go ├── websocket_proxy ├── app_client │ └── client.go ├── app_server │ └── server.go ├── proxy_server │ └── server.go └── proxy_ws2tcp │ └── server.go ├── websocket_tls ├── client │ └── client.go ├── client_macfoundation │ ├── build.sh │ ├── input.txt │ ├── main.mm │ └── run.sh ├── client_websocketpp │ ├── Makefile │ └── client.cpp ├── nbioclient │ └── client.go ├── server │ └── server.go ├── server_manual_frame_assembly │ ├── README.md │ └── server.go └── sticky │ ├── client │ └── client.go │ └── server │ └── server.go └── websocket_transfer ├── client_nb └── client.go ├── client_std └── client.go ├── client_tls_nb └── client_tls.go ├── client_tls_std └── client_tls.go └── server └── server.go /async_sendfile/test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "context" 7 | "io" 8 | "log" 9 | "net" 10 | "net/http" 11 | "os" 12 | "time" 13 | 14 | "github.com/lesismal/nbio/nbhttp" 15 | ) 16 | 17 | var ( 18 | ioPath = "/xxx" 19 | videoPath = "/mv.mp4" 20 | filePath = "." + videoPath 21 | fileSize = 500627803 22 | defaultBuf = make([]byte, fileSize) 23 | 24 | done = make(chan struct{}) 25 | ) 26 | 27 | func init() { 28 | // mempool.DefaultMemPool = mempool.NewSTD() 29 | for i := 0; i < len(defaultBuf); i++ { 30 | defaultBuf[i] = byte(i) ^ 0x5C 31 | } 32 | } 33 | 34 | func client() { 35 | defer func() { 36 | close(done) 37 | }() 38 | 39 | conn, err := net.Dial("tcp", "localhost:8080") 40 | if err != nil { 41 | panic(err) 42 | } 43 | reqIO, err := http.NewRequest("Get", "http://localhost:8080"+ioPath, nil) 44 | if err != nil { 45 | panic(err) 46 | } 47 | reqVideo, err := http.NewRequest("Get", "http://localhost:8080"+videoPath, nil) 48 | if err != nil { 49 | panic(err) 50 | } 51 | reqIO2, err := http.NewRequest("Get", "http://localhost:8080"+ioPath, nil) 52 | if err != nil { 53 | panic(err) 54 | } 55 | err = reqIO.Write(conn) 56 | if err != nil { 57 | panic(err) 58 | } 59 | err = reqVideo.Write(conn) 60 | if err != nil { 61 | panic(err) 62 | } 63 | err = reqIO2.Write(conn) 64 | if err != nil { 65 | panic(err) 66 | } 67 | 68 | reader := bufio.NewReader(conn) 69 | 70 | time.Sleep(time.Second / 2) 71 | res, err := http.ReadResponse(reader, reqIO) 72 | if err != nil { 73 | panic(err) 74 | } 75 | if res.Body != nil { 76 | body, err := io.ReadAll(res.Body) 77 | if err != nil { 78 | panic(err) 79 | } 80 | if !bytes.Equal(body, defaultBuf) { 81 | panic("not equal") 82 | } 83 | } 84 | 85 | res, err = http.ReadResponse(reader, reqVideo) 86 | if err != nil { 87 | panic(err) 88 | } 89 | if res.Body != nil { 90 | body, err := io.ReadAll(res.Body) 91 | if err != nil { 92 | panic(err) 93 | } 94 | os.WriteFile(filePath, body, 0777) 95 | defer os.RemoveAll(filePath) 96 | } 97 | 98 | res, err = http.ReadResponse(reader, reqIO2) 99 | if err != nil { 100 | panic(err) 101 | } 102 | if res.Body != nil { 103 | body, err := io.ReadAll(res.Body) 104 | if err != nil { 105 | panic(err) 106 | } 107 | if !bytes.Equal(body, defaultBuf) { 108 | panic("not equal") 109 | } 110 | } 111 | log.Println("==== success") 112 | } 113 | 114 | var fileHandler = http.FileServer(http.Dir("./static")) 115 | 116 | type handler struct{} 117 | 118 | func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 119 | if r.URL.Path == "/xxx" { 120 | w.Write(defaultBuf) 121 | } else { 122 | fileHandler.ServeHTTP(w, r) 123 | } 124 | } 125 | 126 | func main() { 127 | engine := nbhttp.NewEngine(nbhttp.Config{ 128 | Network: "tcp", 129 | Addrs: []string{":8080"}, 130 | Handler: &handler{}, 131 | }) 132 | err := engine.Start() 133 | if err != nil { 134 | log.Fatalf("engine.Start failed: %v", err) 135 | } 136 | 137 | err = os.MkdirAll("./static", 0777) 138 | if err != nil { 139 | log.Fatalf("MkdirAll failed: %v", err) 140 | } 141 | err = os.WriteFile("./static"+videoPath, defaultBuf, 0777) 142 | if err != nil { 143 | log.Fatalf("WriteFile failed: %v", err) 144 | } 145 | defer os.RemoveAll("./static") 146 | 147 | time.AfterFunc(time.Second/10, client) 148 | 149 | <-done 150 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 151 | defer cancel() 152 | engine.Shutdown(ctx) 153 | } 154 | -------------------------------------------------------------------------------- /bench/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | 9 | "github.com/lesismal/nbio" 10 | ) 11 | 12 | var ( 13 | addr = "localhost:8888" 14 | ) 15 | 16 | func main() { 17 | var ( 18 | wg sync.WaitGroup 19 | qps int64 20 | bufsize = 64 //1024 * 8 21 | clientNum = 128 22 | totalRead int64 23 | totalWrite int64 24 | ) 25 | 26 | g := nbio.NewEngine(nbio.Config{}) 27 | defer g.Stop() 28 | 29 | g.OnData(func(c *nbio.Conn, data []byte) { 30 | atomic.AddInt64(&qps, 1) 31 | atomic.AddInt64(&totalRead, int64(len(data))) 32 | atomic.AddInt64(&totalWrite, int64(len(data))) 33 | c.Write(append([]byte{}, data...)) 34 | }) 35 | 36 | err := g.Start() 37 | if err != nil { 38 | fmt.Printf("Start failed: %v\n", err) 39 | } 40 | 41 | for i := 0; i < clientNum; i++ { 42 | wg.Add(1) 43 | go func() { 44 | data := make([]byte, bufsize) 45 | c, err := nbio.Dial("tcp", addr) 46 | if err != nil { 47 | fmt.Printf("Dial failed: %v\n", err) 48 | } 49 | g.AddConn(c) 50 | c.Write([]byte(data)) 51 | atomic.AddInt64(&totalWrite, int64(len(data))) 52 | }() 53 | } 54 | 55 | go func() { 56 | for { 57 | time.Sleep(time.Second) 58 | fmt.Printf("qps: %v, total read: %.1f M, total write: %.1f M\n", atomic.SwapInt64(&qps, 0), float64(atomic.SwapInt64(&totalRead, 0))/1024/1024, float64(atomic.SwapInt64(&totalWrite, 0))/1024/1024) 59 | } 60 | }() 61 | 62 | wg.Wait() 63 | } 64 | -------------------------------------------------------------------------------- /bench/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/lesismal/nbio" 7 | ) 8 | 9 | // func onData(c *nbio.Conn, data []byte) { 10 | // c.Write(append([]byte{}, data...)) 11 | // } 12 | 13 | func main() { 14 | g := nbio.NewEngine(nbio.Config{ 15 | Network: "tcp", 16 | Addrs: []string{"localhost:8888"}, 17 | EpollMod: nbio.EPOLLET, 18 | }) 19 | 20 | // g.OnData(onData) 21 | g.OnRead(func(c *nbio.Conn) { 22 | buf := make([]byte, 1024) 23 | n, err := c.Read(buf) 24 | if err != nil { 25 | return 26 | } 27 | c.Write(buf[:n]) 28 | }) 29 | err := g.Start() 30 | if err != nil { 31 | fmt.Printf("nbio.Start failed: %v\n", err) 32 | return 33 | } 34 | 35 | g.Wait() 36 | } 37 | -------------------------------------------------------------------------------- /echo/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "math/rand" 8 | "time" 9 | 10 | "github.com/lesismal/nbio" 11 | "github.com/lesismal/nbio/logging" 12 | ) 13 | 14 | func main() { 15 | var ( 16 | ret []byte 17 | buf = make([]byte, 1024*1024*4) 18 | addr = "localhost:8888" 19 | ctx, _ = context.WithTimeout(context.Background(), 60*time.Second) 20 | ) 21 | 22 | logging.SetLevel(logging.LevelInfo) 23 | 24 | rand.Read(buf) 25 | 26 | engine := nbio.NewEngine(nbio.Config{}) 27 | 28 | done := make(chan int) 29 | engine.OnData(func(c *nbio.Conn, data []byte) { 30 | ret = append(ret, data...) 31 | if len(ret) == len(buf) { 32 | if bytes.Equal(buf, ret) { 33 | close(done) 34 | } 35 | } 36 | }) 37 | 38 | err := engine.Start() 39 | if err != nil { 40 | fmt.Printf("Start failed: %v\n", err) 41 | } 42 | defer engine.Stop() 43 | 44 | // net.Dial also can be used here 45 | c, err := nbio.Dial("tcp", addr) 46 | if err != nil { 47 | panic(err) 48 | } 49 | engine.AddConn(c) 50 | c.Write(buf) 51 | 52 | select { 53 | case <-ctx.Done(): 54 | logging.Error("timeout") 55 | case <-done: 56 | logging.Info("success") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /echo/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/lesismal/nbio" 7 | ) 8 | 9 | func main() { 10 | engine := nbio.NewEngine(nbio.Config{ 11 | Network: "tcp", 12 | Addrs: []string{":8888"}, 13 | MaxWriteBufferSize: 6 * 1024 * 1024, 14 | }) 15 | 16 | engine.OnData(func(c *nbio.Conn, data []byte) { 17 | c.Write(append([]byte{}, data...)) 18 | }) 19 | 20 | err := engine.Start() 21 | if err != nil { 22 | fmt.Printf("nbio.Start failed: %v\n", err) 23 | return 24 | } 25 | defer engine.Stop() 26 | 27 | <-make(chan int) 28 | } 29 | -------------------------------------------------------------------------------- /echo/server/server_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "log" 10 | "net" 11 | "testing" 12 | "time" 13 | 14 | "github.com/lesismal/nbio" 15 | ) 16 | 17 | const ( 18 | addr = ":9099" 19 | ) 20 | 21 | var ( 22 | buf = make([]byte, 6*1024*1024) 23 | ) 24 | 25 | func init() { 26 | rand.Read(buf) 27 | } 28 | 29 | func writeComplete(c *nbio.Conn, data []byte) (int, error) { 30 | offset := 0 31 | msgLen := len(data) 32 | for { 33 | n, err := c.Write(data[offset:]) 34 | fmt.Printf("write %d %s\n", n, err) 35 | offset += n 36 | if err != nil || offset == msgLen { 37 | return offset, err 38 | } 39 | time.Sleep(time.Millisecond * 500) 40 | } 41 | 42 | } 43 | 44 | func server(ready chan error) error { 45 | g := nbio.NewEngine(nbio.Config{ 46 | Network: "tcp", 47 | Addrs: []string{addr}, 48 | MaxWriteBufferSize: 6 * 1024 * 1024, 49 | }) 50 | 51 | g.OnOpen(func(c *nbio.Conn) { 52 | _, err := writeComplete(c, buf) 53 | if err != nil { 54 | fmt.Printf("write failed: %s\n", err) 55 | } 56 | }) 57 | g.OnClose(func(c *nbio.Conn, err error) { 58 | g.Stop() 59 | }) 60 | 61 | err := g.Start() 62 | if err != nil { 63 | return fmt.Errorf("nbio.Start failed: %w", err) 64 | } 65 | ready <- err 66 | defer g.Stop() 67 | 68 | g.Wait() 69 | return nil 70 | } 71 | 72 | func client(msgLen int) error { 73 | var ( 74 | ret []byte 75 | addr = addr 76 | ) 77 | c, err := net.Dial("tcp", addr) 78 | if err != nil { 79 | fmt.Println(err) 80 | return err 81 | } 82 | 83 | i := 0 84 | line := make([]byte, 60000) 85 | for { 86 | 87 | n, err := c.Read(line) 88 | if err != nil && !errors.Is(err, io.EOF) { 89 | return fmt.Errorf("error read: %d %w", n, err) 90 | } 91 | if errors.Is(err, io.EOF) { 92 | time.Sleep(time.Second * 5) 93 | } 94 | i++ 95 | ret = append(ret, line[:n]...) 96 | fmt.Printf("client received %d %d %d of %d\n", i, n, len(ret), len(buf)) 97 | if len(ret) == len(buf) { 98 | if bytes.Equal(buf, ret) { 99 | return nil 100 | } 101 | return fmt.Errorf("ret, does not match buf") 102 | } 103 | 104 | } 105 | } 106 | func Test_main(t *testing.T) { 107 | ready := make(chan error) 108 | go func() { 109 | err := server(ready) 110 | if err != nil { 111 | log.Fatal(err) 112 | } 113 | }() 114 | 115 | err := <-ready 116 | if err != nil { 117 | t.Fatal(err) 118 | } 119 | 120 | err = client(1024 * 1024 * 4) 121 | if err != nil { 122 | t.Fatal(err) 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /fixedbufferpool/fixedbufferpool.go: -------------------------------------------------------------------------------- 1 | package fixedbufferpool 2 | 3 | import ( 4 | "os" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type Pool struct { 10 | pool sync.Pool 11 | buffers chan struct{} 12 | messageSize int 13 | getTimeout time.Duration 14 | } 15 | 16 | func NewFixedBufferPool(buffers, messageSize int, getTimeout time.Duration) *Pool { 17 | rtn := &Pool{ 18 | buffers: make(chan struct{}, buffers), 19 | messageSize: messageSize, 20 | getTimeout: getTimeout, 21 | pool: sync.Pool{ 22 | New: func() interface{} { 23 | buf := make([]byte, messageSize) 24 | return &buf 25 | }, 26 | }, 27 | } 28 | for i := 0; i < buffers; i++ { 29 | rtn.buffers <- struct{}{} 30 | } 31 | return rtn 32 | } 33 | 34 | func (p *Pool) Put(in []byte) { 35 | if cap(in) != p.messageSize { 36 | panic("buffer added to pool wasn't original from the pool") 37 | } 38 | select { 39 | case p.buffers <- struct{}{}: 40 | p.pool.Put(&in) 41 | default: 42 | panic("more buffers added to the pool than were originally created") 43 | } 44 | } 45 | 46 | func (p *Pool) Get() ([]byte, error) { 47 | // don't waste cycles building a timer if there is a buffer available 48 | select { 49 | case <-p.buffers: 50 | return (*(p.pool.Get().(*[]byte)))[0:0], nil 51 | default: 52 | } 53 | t := time.NewTimer(p.getTimeout) 54 | select { 55 | case <-p.buffers: 56 | t.Stop() 57 | return (*(p.pool.Get().(*[]byte)))[0:0], nil 58 | case <-t.C: 59 | return nil, os.ErrDeadlineExceeded 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/lesismal/nbio-examples 2 | 3 | go 1.21.5 4 | 5 | require ( 6 | github.com/gin-gonic/gin v1.9.1 7 | github.com/go-chi/chi/v5 v5.0.11 8 | github.com/gofiber/fiber/v3 v3.0.0-beta.3 9 | github.com/gorilla/websocket v1.5.1 10 | github.com/kataras/iris/v12 v12.2.10 11 | github.com/labstack/echo v3.3.10+incompatible 12 | github.com/lesismal/llib v1.1.13 13 | github.com/lesismal/nbio v1.5.12 14 | github.com/valyala/fasthttp v1.55.0 15 | nhooyr.io/websocket v1.8.10 16 | ) 17 | 18 | require ( 19 | github.com/BurntSushi/toml v1.3.2 // indirect 20 | github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect 21 | github.com/CloudyKit/jet/v6 v6.2.0 // indirect 22 | github.com/Joker/jade v1.1.3 // indirect 23 | github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect 24 | github.com/andybalholm/brotli v1.1.0 // indirect 25 | github.com/aymerick/douceur v0.2.0 // indirect 26 | github.com/bytedance/sonic v1.9.1 // indirect 27 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 28 | github.com/fatih/structs v1.1.0 // indirect 29 | github.com/flosch/pongo2/v4 v4.0.2 // indirect 30 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 31 | github.com/gin-contrib/sse v0.1.0 // indirect 32 | github.com/go-playground/locales v0.14.1 // indirect 33 | github.com/go-playground/universal-translator v0.18.1 // indirect 34 | github.com/go-playground/validator/v10 v10.14.0 // indirect 35 | github.com/goccy/go-json v0.10.2 // indirect 36 | github.com/gofiber/utils/v2 v2.0.0-beta.4 // indirect 37 | github.com/golang/snappy v0.0.4 // indirect 38 | github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 // indirect 39 | github.com/google/uuid v1.6.0 // indirect 40 | github.com/gorilla/css v1.0.0 // indirect 41 | github.com/iris-contrib/schema v0.0.6 // indirect 42 | github.com/josharian/intern v1.0.0 // indirect 43 | github.com/json-iterator/go v1.1.12 // indirect 44 | github.com/kataras/blocks v0.0.8 // indirect 45 | github.com/kataras/golog v0.1.11 // indirect 46 | github.com/kataras/pio v0.0.13 // indirect 47 | github.com/kataras/sitemap v0.0.6 // indirect 48 | github.com/kataras/tunnel v0.0.4 // indirect 49 | github.com/klauspost/compress v1.17.9 // indirect 50 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 51 | github.com/labstack/gommon v0.4.2 // indirect 52 | github.com/leodido/go-urn v1.2.4 // indirect 53 | github.com/mailgun/raymond/v2 v2.0.48 // indirect 54 | github.com/mailru/easyjson v0.7.7 // indirect 55 | github.com/mattn/go-colorable v0.1.13 // indirect 56 | github.com/mattn/go-isatty v0.0.20 // indirect 57 | github.com/microcosm-cc/bluemonday v1.0.26 // indirect 58 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 59 | github.com/modern-go/reflect2 v1.0.2 // indirect 60 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 61 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 62 | github.com/schollz/closestmatch v2.1.0+incompatible // indirect 63 | github.com/sirupsen/logrus v1.8.1 // indirect 64 | github.com/tdewolff/minify/v2 v2.20.14 // indirect 65 | github.com/tdewolff/parse/v2 v2.7.8 // indirect 66 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 67 | github.com/ugorji/go/codec v1.2.11 // indirect 68 | github.com/valyala/bytebufferpool v1.0.0 // indirect 69 | github.com/valyala/fasttemplate v1.2.2 // indirect 70 | github.com/valyala/tcplisten v1.0.0 // indirect 71 | github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect 72 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect 73 | github.com/yosssi/ace v0.0.5 // indirect 74 | golang.org/x/arch v0.3.0 // indirect 75 | golang.org/x/crypto v0.31.0 // indirect 76 | golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect 77 | golang.org/x/net v0.26.0 // indirect 78 | golang.org/x/sys v0.28.0 // indirect 79 | golang.org/x/text v0.21.0 // indirect 80 | golang.org/x/time v0.5.0 // indirect 81 | google.golang.org/protobuf v1.32.0 // indirect 82 | gopkg.in/ini.v1 v1.67.0 // indirect 83 | gopkg.in/yaml.v3 v3.0.1 // indirect 84 | ) 85 | -------------------------------------------------------------------------------- /http/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | func get() { 11 | url := "http://localhost:8888/echo" 12 | method := "GET" 13 | client := &http.Client{} 14 | for i := 0; i < 5; i++ { 15 | req, err := http.NewRequest(method, url, nil) 16 | if err != nil { 17 | panic(err) 18 | } 19 | res, err := client.Do(req) 20 | if err != nil { 21 | panic(err) 22 | } 23 | defer res.Body.Close() 24 | body, err := ioutil.ReadAll(res.Body) 25 | fmt.Println("get:", string(body)) 26 | } 27 | } 28 | 29 | func post() { 30 | url := "http://localhost:8888/echo" 31 | method := "POST" 32 | client := &http.Client{} 33 | 34 | for i := 0; i < 5; i++ { 35 | payload := strings.NewReader("hello") 36 | req, err := http.NewRequest(method, url, payload) 37 | if err != nil { 38 | panic(err) 39 | } 40 | res, err := client.Do(req) 41 | if err != nil { 42 | panic(err) 43 | } 44 | defer res.Body.Close() 45 | body, err := ioutil.ReadAll(res.Body) 46 | fmt.Println("post:", string(body)) 47 | } 48 | } 49 | 50 | func main() { 51 | get() 52 | post() 53 | } 54 | -------------------------------------------------------------------------------- /http/nbclient/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "net" 8 | "net/http" 9 | _ "net/http/pprof" 10 | "sync/atomic" 11 | "time" 12 | 13 | "github.com/lesismal/nbio/nbhttp" 14 | ) 15 | 16 | var ( 17 | success uint64 = 0 18 | failed uint64 = 0 19 | 20 | sleepTime = flag.Int("s", 1, "sleep time for each loop in a goroutine") 21 | ) 22 | 23 | func main() { 24 | flag.Parse() 25 | 26 | engine := nbhttp.NewEngine(nbhttp.Config{}) 27 | 28 | err := engine.Start() 29 | if err != nil { 30 | fmt.Printf("nbio.Start failed: %v\n", err) 31 | return 32 | } 33 | defer engine.Stop() 34 | 35 | cli := &nbhttp.Client{ 36 | Engine: engine, 37 | Timeout: time.Second * 3, 38 | MaxConnsPerHost: 5, 39 | } 40 | 41 | for i := 0; i < 100; i++ { 42 | idx := i 43 | go func() { 44 | count := 0 45 | 46 | func() { 47 | var doRequest func() 48 | doRequest = func() { 49 | var err error 50 | var req *http.Request 51 | req, err = http.NewRequest("GET", "http://localhost:8888/echo", nil) 52 | if err != nil { 53 | log.Fatal(err) 54 | } 55 | 56 | begin := time.Now() 57 | cli.Do(req, func(res *http.Response, conn net.Conn, err error) { 58 | if err != nil { 59 | atomic.AddUint64(&failed, 1) 60 | fmt.Println("Do failed:", err) 61 | return 62 | } 63 | atomic.AddUint64(&success, 1) 64 | if err == nil && res.Body != nil { 65 | defer res.Body.Close() 66 | } 67 | count++ 68 | 69 | if *sleepTime > 0 { 70 | log.Printf("request success %v: %v, %v%v, time used: %v us\n", idx, count, req.URL.Host, req.URL.Path, time.Since(begin).Microseconds()) 71 | time.AfterFunc(time.Second*time.Duration(*sleepTime), func() { 72 | doRequest() 73 | }) 74 | } else { 75 | doRequest() 76 | } 77 | }) 78 | } 79 | doRequest() 80 | }() 81 | }() 82 | } 83 | 84 | <-make(chan int) 85 | } 86 | -------------------------------------------------------------------------------- /http/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "runtime" 9 | "sync/atomic" 10 | "time" 11 | 12 | "github.com/lesismal/nbio/nbhttp" 13 | ) 14 | 15 | var ( 16 | qps uint64 = 0 17 | total uint64 = 0 18 | ) 19 | 20 | func onEcho(w http.ResponseWriter, r *http.Request) { 21 | // time.Sleep(time.Second * 5) 22 | data, _ := io.ReadAll(r.Body) 23 | if len(data) > 0 { 24 | w.Write(data) 25 | } else { 26 | w.Write([]byte(time.Now().Format("20060102 15:04:05"))) 27 | } 28 | atomic.AddUint64(&qps, 1) 29 | } 30 | 31 | func main() { 32 | mux := &http.ServeMux{} 33 | mux.HandleFunc("/echo", onEcho) 34 | 35 | svr := nbhttp.NewEngine(nbhttp.Config{ 36 | Network: "tcp", 37 | Addrs: []string{"localhost:8888"}, 38 | Handler: mux, 39 | }) // pool.Go) 40 | 41 | err := svr.Start() 42 | if err != nil { 43 | fmt.Printf("nbio.Start failed: %v\n", err) 44 | return 45 | } 46 | defer svr.Stop() 47 | 48 | ticker := time.NewTicker(time.Second) 49 | for i := 1; true; i++ { 50 | <-ticker.C 51 | n := atomic.SwapUint64(&qps, 0) 52 | total += n 53 | fmt.Printf("running for %v seconds, NumGoroutine: %v, qps: %v, total: %v\n", i, runtime.NumGoroutine(), n, total) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /http/server_tls/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDazCCAlOgAwIBAgIUAtGajmdRu86Si+4JqDS9R0xjCJ8wDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAzMjAwMzMyMDZaFw0yMjAz 5 | MjAwMzMyMDZaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 7 | AQUAA4IBDwAwggEKAoIBAQCodlskKbfkv5CJbxmum3G78YZq3XujcCp8yzaLJ7iB 8 | GDu8myaEAbv6YmWuSGuJuScomnRx6MTi9lVwbtPlPw7LI24n+wk+FbkMwSyO+DpH 9 | 18w3q0+WouoPou6kqooV9PY6UkzW1dE4b9yN2X2SpUfAisTkqCRP++kV7SdMMjTR 10 | jKdtZzEwfLD5TEGTgFf6/ertVJXjIIDhCMV016xWuvumi0kTLgRVAk71q1fulkBa 11 | si2qyfr3dwWgDeOmUwV7escK0Tvlr8wWumMoeA0MKH+6KXkW96tQuAujy250TJ0A 12 | 0Z9Y8MwKV7/plsjWIjkfwgU+p0BCIDO8BC02WfFd9IGjAgMBAAGjUzBRMB0GA1Ud 13 | DgQWBBTOg4YQWasdScACK36FUXwklGXIlTAfBgNVHSMEGDAWgBTOg4YQWasdScAC 14 | K36FUXwklGXIlTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCl 15 | uWCs702luYPw1fFUXvcUDsa80IBsUMUr/Pcz+xAhj8CLPKNHUNrR1klXT6U4O0LW 16 | 7N/XYXxY3lhevjPO8SsP3mEuP9cB8S4P0O452Y+cWySrI0pDhaPv1T8mfUIBxg5K 17 | PZsmxv7NHMCj+QOgljKFapmP6RJG3t9nQefljPwBNaNxvBL7ST6rZ/TT9WlLg4EE 18 | vYoG/Mv3AtxP9tKzjrXJTJOmNAyuaQhC7YhVNg72E9jqryQw2A1iAAvgtBpTJ6If 19 | zHZkMACp5DIR4sVOAyBaM27gRb8vpiJs+1LDCwN681UHe4Rjbrl81ruhVfAUT1eX 20 | pdwdlCM6U9/oz11spqL+ 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /http/server_tls/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net/http" 8 | "runtime" 9 | "sync/atomic" 10 | "time" 11 | 12 | "github.com/lesismal/llib/std/crypto/tls" 13 | "github.com/lesismal/nbio/nbhttp" 14 | ) 15 | 16 | var ( 17 | qps uint64 = 0 18 | total uint64 = 0 19 | ) 20 | 21 | // visit: https://localhost:8888/echo 22 | func onEcho(w http.ResponseWriter, r *http.Request) { 23 | data, _ := io.ReadAll(r.Body) 24 | if len(data) > 0 { 25 | w.Write(data) 26 | } else { 27 | w.Write([]byte(time.Now().Format("20060102 15:04:05"))) 28 | } 29 | atomic.AddUint64(&qps, 1) 30 | } 31 | 32 | func main() { 33 | cert, err := tls.LoadX509KeyPair("server.crt", "server.key") 34 | if err != nil { 35 | log.Fatalf("tls.X509KeyPair failed: %v", err) 36 | } 37 | tlsConfig := &tls.Config{ 38 | Certificates: []tls.Certificate{cert}, 39 | InsecureSkipVerify: true, 40 | } 41 | 42 | mux := &http.ServeMux{} 43 | mux.HandleFunc("/echo", onEcho) 44 | 45 | svr := nbhttp.NewEngine(nbhttp.Config{ 46 | Network: "tcp", 47 | AddrsTLS: []string{"localhost:8888"}, 48 | TLSConfig: tlsConfig, 49 | Handler: mux, 50 | }) 51 | 52 | err = svr.Start() 53 | if err != nil { 54 | fmt.Printf("nbio.Start failed: %v\n", err) 55 | return 56 | } 57 | defer svr.Stop() 58 | 59 | ticker := time.NewTicker(time.Second) 60 | for i := 1; true; i++ { 61 | <-ticker.C 62 | n := atomic.SwapUint64(&qps, 0) 63 | total += n 64 | fmt.Printf("running for %v seconds, NumGoroutine: %v, qps: %v, total: %v\n", i, runtime.NumGoroutine(), n, total) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /http/server_tls/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCodlskKbfkv5CJ 3 | bxmum3G78YZq3XujcCp8yzaLJ7iBGDu8myaEAbv6YmWuSGuJuScomnRx6MTi9lVw 4 | btPlPw7LI24n+wk+FbkMwSyO+DpH18w3q0+WouoPou6kqooV9PY6UkzW1dE4b9yN 5 | 2X2SpUfAisTkqCRP++kV7SdMMjTRjKdtZzEwfLD5TEGTgFf6/ertVJXjIIDhCMV0 6 | 16xWuvumi0kTLgRVAk71q1fulkBasi2qyfr3dwWgDeOmUwV7escK0Tvlr8wWumMo 7 | eA0MKH+6KXkW96tQuAujy250TJ0A0Z9Y8MwKV7/plsjWIjkfwgU+p0BCIDO8BC02 8 | WfFd9IGjAgMBAAECggEBAIjyDg1LzK4r/Dd9FYeX3dFG+lqNSiEG4cMo/2IIHDj1 9 | ljowdhMBxeo5aydOv/zpgyfCx76B1uwaDEKmFaNaq4//cWEW9eB+kDwKHUrsB5S3 10 | nM/0d3KhZVzous8pw68tv6yRkt1iW/5hEQ5PK4G20ql3JbAr77kFs+mbLEMI3V8q 11 | gpySk4djFYSYj85rBsW5xA8lJ1Lh0/pVTgFRGN6owXgcQVw8/RK4p3T0uShwljRV 12 | 7+c0VXfhlj26ZhGQLP00nU5zAmKgC8NJ9VaPvZtMvTcevtLHHZU0Sl9DLEPA217e 13 | wxxsNLjMkfKQix5QLcNjtnoXQajprE9r4nJv5lbz/kECgYEA0FY5/eH7rvSw4CRO 14 | OZBjfQVNDbU/6OOZxCEi4GoAHGXRUKftC0H4IIGSjLa19umSYGm6+D9LojldtoLv 15 | 2Ihp4qK17r5AEs1oWyvugjTcWXui1l1Vs3HEcD0CO2FpRJUzZgC67ga+W1a+6SD4 16 | Z9SE+sKf+sf7Ncvnxk0HnifBZNkCgYEAzwDHXm2q0+PTYz76C+JTfxq/NkK0/M0z 17 | 61NxA5bEcX+qc8ZZNDLSi+Cdp/ZEgorDj4f2Z6pQ4KpcbtC/OvRnWplZNQY2II/t 18 | yty9ucNkjqHgsb+U71XcXwz/sT5NWf9vFd39UIajs39wCSclL1ylMFJA6lT5lffW 19 | Mvw5My0nnNsCgYAXbbrzzBgLosTJUAvj+VLW2mPB6OIZBI9kOP29Eu6UQvb5BQlp 20 | PK/0p92dKWbpL3cglINMK4IL6juZrLvgM+cEb5vaD1cRRjw4FIar6dnlzjuPs8tR 21 | dLfMj2/S0a+O4OB41hgvcF6z0tuBSA3nT0TtFjn7b8XWVOnpqPv0UzAycQKBgF/6 22 | uXY0HTf/87yZr7Cg8RwHF8+d4HMy5jbfyo/icRU7H5psxmGoiu0maZM+YYextXkw 23 | jnSEiNmSxCbxjSlVtzJP1gf66E/yzEv6S9H9IfvsXNWtwe2y/unnigrxm0X2ZNhb 24 | xJ94viB1H0pvJxOtvj4IdWbxnYQGlP+w6QiskChvAoGAQJPb45tawHcmp0M5LGdC 25 | gMyOsCgANK+46H+88Mzz7vwk7vcUTmHwhxyFe3HYohg1mfJXNuRinB0LdDuHefLJ 26 | LkiZ9tn7X8Fn/93g0DCHZczk5cYCRP+Xpln1myA8gOSWwwJvfJ5Gx7c1gmCGqU7g 27 | /KfN+L/ICiRDdCRNfuGMnbU= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /http/stdserver/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "runtime" 9 | "sync/atomic" 10 | "time" 11 | ) 12 | 13 | var ( 14 | qps uint64 = 0 15 | total uint64 = 0 16 | ) 17 | 18 | func onEcho(w http.ResponseWriter, r *http.Request) { 19 | data, _ := io.ReadAll(r.Body) 20 | if len(data) > 0 { 21 | w.Write(data) 22 | } else { 23 | w.Write([]byte(time.Now().Format("20060102 15:04:05"))) 24 | } 25 | atomic.AddUint64(&qps, 1) 26 | } 27 | 28 | func serve(addrs []string) { 29 | for _, v := range addrs { 30 | go func(addr string) { 31 | mux := &http.ServeMux{} 32 | mux.HandleFunc("/echo", onEcho) 33 | 34 | server := http.Server{ 35 | Addr: addr, 36 | Handler: mux, 37 | } 38 | server.ListenAndServe() 39 | }(v) 40 | } 41 | } 42 | 43 | func main() { 44 | go func() { 45 | if err := http.ListenAndServe(":6060", nil); err != nil { 46 | panic(err) 47 | } 48 | }() 49 | 50 | addrs := []string{"localhost:8888"} 51 | serve(addrs) 52 | 53 | ticker := time.NewTicker(time.Second) 54 | for i := 1; true; i++ { 55 | <-ticker.C 56 | n := atomic.SwapUint64(&qps, 0) 57 | total += n 58 | fmt.Printf("running for %v seconds, NumGoroutine: %v, qps: %v, total: %v\n", i, runtime.NumGoroutine(), n, total) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /http_1m/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "net" 7 | "runtime" 8 | "sync/atomic" 9 | "time" 10 | ) 11 | 12 | var ( 13 | connected uint64 = 0 14 | success uint64 = 0 15 | failed uint64 = 0 16 | totalSuccess uint64 = 0 17 | totalFailed uint64 = 0 18 | 19 | numClient = flag.Int("c", 500000, "client num") 20 | numGoroutine = flag.Int("g", 1000, "goroutine num") 21 | ) 22 | 23 | func main() { 24 | flag.Parse() 25 | 26 | connNum := *numClient 27 | goroutineNum := *numGoroutine 28 | 29 | for i := 0; i < goroutineNum; i++ { 30 | go loop(addrs[i%len(addrs)], connNum/goroutineNum) 31 | } 32 | 33 | ticker := time.NewTicker(time.Second) 34 | for i := 1; true; i++ { 35 | <-ticker.C 36 | nSuccess := atomic.SwapUint64(&success, 0) 37 | nFailed := atomic.SwapUint64(&failed, 0) 38 | totalSuccess += nSuccess 39 | totalFailed += nFailed 40 | fmt.Printf("running for %v seconds, online: %v, NumGoroutine: %v, success: %v, totalSuccess: %v, failed: %v, totalFailed: %v\n", i, connected, runtime.NumGoroutine(), nSuccess, totalSuccess, failed, totalFailed) 41 | } 42 | } 43 | 44 | func loop(addr string, connNum int) { 45 | conns := make([]net.Conn, connNum) 46 | for i := 0; i < connNum; i++ { 47 | for { 48 | conn, err := net.Dial("tcp", addr) 49 | if err == nil { 50 | conns[i] = conn 51 | atomic.AddUint64(&connected, 1) 52 | break 53 | } 54 | time.Sleep(time.Second / 10) 55 | } 56 | } 57 | for { 58 | for i := 0; i < connNum; i++ { 59 | post(conns[i], addr) 60 | } 61 | } 62 | } 63 | 64 | func post(conn net.Conn, addr string) { 65 | reqData := []byte(fmt.Sprintf("POST /echo HTTP/1.1\r\nHost: %v\r\nContent-Length: 5\r\nAccept-Encoding: gzip\r\n\r\nhello", addr)) 66 | resData := make([]byte, 1024) 67 | n, err := conn.Write(reqData) 68 | if err != nil || n < len(reqData) { 69 | atomic.AddUint64(&failed, 1) 70 | fmt.Println("write failed:", n, err) 71 | return 72 | } 73 | // time.Sleep(time.Second / 10) 74 | // n, err = io.ReadFull(conn, resData) 75 | n, err = conn.Read(resData) 76 | if err != nil || string(resData[n-5:n]) != "hello" { 77 | atomic.AddUint64(&failed, 1) 78 | fmt.Println("read failed:", n, err) 79 | } 80 | atomic.AddUint64(&success, 1) 81 | } 82 | 83 | var addrs = []string{ 84 | "localhost:28001", 85 | "localhost:28002", 86 | "localhost:28003", 87 | "localhost:28004", 88 | "localhost:28005", 89 | "localhost:28006", 90 | "localhost:28007", 91 | "localhost:28008", 92 | "localhost:28009", 93 | "localhost:28010", 94 | 95 | "localhost:28011", 96 | "localhost:28012", 97 | "localhost:28013", 98 | "localhost:28014", 99 | "localhost:28015", 100 | "localhost:28016", 101 | "localhost:28017", 102 | "localhost:28018", 103 | "localhost:28019", 104 | "localhost:28020", 105 | 106 | "localhost:28021", 107 | "localhost:28022", 108 | "localhost:28023", 109 | "localhost:28024", 110 | "localhost:28025", 111 | "localhost:28026", 112 | "localhost:28027", 113 | "localhost:28028", 114 | "localhost:28029", 115 | "localhost:28030", 116 | 117 | "localhost:28031", 118 | "localhost:28032", 119 | "localhost:28033", 120 | "localhost:28034", 121 | "localhost:28035", 122 | "localhost:28036", 123 | "localhost:28037", 124 | "localhost:28038", 125 | "localhost:28039", 126 | "localhost:28040", 127 | 128 | "localhost:28041", 129 | "localhost:28042", 130 | "localhost:28043", 131 | "localhost:28044", 132 | "localhost:28045", 133 | "localhost:28046", 134 | "localhost:28047", 135 | "localhost:28048", 136 | "localhost:28049", 137 | "localhost:28050", 138 | } 139 | -------------------------------------------------------------------------------- /http_1m/fastserver/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | _ "net/http/pprof" 7 | "runtime" 8 | "sync/atomic" 9 | "time" 10 | 11 | "github.com/valyala/fasthttp" 12 | ) 13 | 14 | var ( 15 | qps uint64 = 0 16 | total uint64 = 0 17 | ) 18 | 19 | func onEcho(ctx *fasthttp.RequestCtx) { 20 | data := ctx.Request.Body() 21 | if len(data) > 0 { 22 | ctx.Write(data) 23 | } else { 24 | ctx.Write([]byte(time.Now().Format("20060102 15:04:05"))) 25 | } 26 | atomic.AddUint64(&qps, 1) 27 | } 28 | 29 | func serve(addrs []string) { 30 | for _, v := range addrs { 31 | go func(addr string) { 32 | server := &fasthttp.Server{ 33 | Handler: onEcho, 34 | } 35 | server.ListenAndServe(addr) 36 | }(v) 37 | } 38 | } 39 | 40 | func main() { 41 | go func() { 42 | if err := http.ListenAndServe(":6060", nil); err != nil { 43 | panic(err) 44 | } 45 | }() 46 | 47 | // addrs := []string{"localhost:8888"} 48 | serve(addrs) 49 | 50 | ticker := time.NewTicker(time.Second) 51 | for i := 1; true; i++ { 52 | <-ticker.C 53 | n := atomic.SwapUint64(&qps, 0) 54 | total += n 55 | fmt.Printf("running for %v seconds, NumGoroutine: %v, qps: %v, total: %v\n", i, runtime.NumGoroutine(), n, total) 56 | } 57 | } 58 | 59 | var addrs = []string{ 60 | "localhost:28001", 61 | "localhost:28002", 62 | "localhost:28003", 63 | "localhost:28004", 64 | "localhost:28005", 65 | "localhost:28006", 66 | "localhost:28007", 67 | "localhost:28008", 68 | "localhost:28009", 69 | "localhost:28010", 70 | 71 | "localhost:28011", 72 | "localhost:28012", 73 | "localhost:28013", 74 | "localhost:28014", 75 | "localhost:28015", 76 | "localhost:28016", 77 | "localhost:28017", 78 | "localhost:28018", 79 | "localhost:28019", 80 | "localhost:28020", 81 | 82 | "localhost:28021", 83 | "localhost:28022", 84 | "localhost:28023", 85 | "localhost:28024", 86 | "localhost:28025", 87 | "localhost:28026", 88 | "localhost:28027", 89 | "localhost:28028", 90 | "localhost:28029", 91 | "localhost:28030", 92 | 93 | "localhost:28031", 94 | "localhost:28032", 95 | "localhost:28033", 96 | "localhost:28034", 97 | "localhost:28035", 98 | "localhost:28036", 99 | "localhost:28037", 100 | "localhost:28038", 101 | "localhost:28039", 102 | "localhost:28040", 103 | 104 | "localhost:28041", 105 | "localhost:28042", 106 | "localhost:28043", 107 | "localhost:28044", 108 | "localhost:28045", 109 | "localhost:28046", 110 | "localhost:28047", 111 | "localhost:28048", 112 | "localhost:28049", 113 | "localhost:28050", 114 | } 115 | -------------------------------------------------------------------------------- /http_1m/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "runtime" 9 | "sync/atomic" 10 | "time" 11 | 12 | "github.com/lesismal/nbio/nbhttp" 13 | ) 14 | 15 | var ( 16 | qps uint64 = 0 17 | total uint64 = 0 18 | ) 19 | 20 | func onEcho(w http.ResponseWriter, r *http.Request) { 21 | data, _ := io.ReadAll(r.Body) 22 | if len(data) > 0 { 23 | w.Write(data) 24 | } else { 25 | w.Write([]byte(time.Now().Format("20060102 15:04:05"))) 26 | } 27 | atomic.AddUint64(&qps, 1) 28 | } 29 | 30 | func main() { 31 | go func() { 32 | if err := http.ListenAndServe("localhost:6060", nil); err != nil { 33 | panic(err) 34 | } 35 | }() 36 | 37 | mux := &http.ServeMux{} 38 | mux.HandleFunc("/echo", onEcho) 39 | 40 | svr := nbhttp.NewEngine(nbhttp.Config{ 41 | Network: "tcp", 42 | Addrs: addrs, 43 | MaxLoad: 1000000, 44 | NPoller: runtime.NumCPU() * 2, 45 | Handler: mux, 46 | }) 47 | 48 | err := svr.Start() 49 | if err != nil { 50 | fmt.Printf("nbio.Start failed: %v\n", err) 51 | return 52 | } 53 | defer svr.Stop() 54 | 55 | ticker := time.NewTicker(time.Second) 56 | for i := 1; true; i++ { 57 | <-ticker.C 58 | n := atomic.SwapUint64(&qps, 0) 59 | total += n 60 | fmt.Printf("running for %v seconds, NumGoroutine: %v, qps: %v, total: %v\n", i, runtime.NumGoroutine(), n, total) 61 | } 62 | } 63 | 64 | var addrs = []string{ 65 | "localhost:28001", 66 | "localhost:28002", 67 | "localhost:28003", 68 | "localhost:28004", 69 | "localhost:28005", 70 | "localhost:28006", 71 | "localhost:28007", 72 | "localhost:28008", 73 | "localhost:28009", 74 | "localhost:28010", 75 | 76 | "localhost:28011", 77 | "localhost:28012", 78 | "localhost:28013", 79 | "localhost:28014", 80 | "localhost:28015", 81 | "localhost:28016", 82 | "localhost:28017", 83 | "localhost:28018", 84 | "localhost:28019", 85 | "localhost:28020", 86 | 87 | "localhost:28021", 88 | "localhost:28022", 89 | "localhost:28023", 90 | "localhost:28024", 91 | "localhost:28025", 92 | "localhost:28026", 93 | "localhost:28027", 94 | "localhost:28028", 95 | "localhost:28029", 96 | "localhost:28030", 97 | 98 | "localhost:28031", 99 | "localhost:28032", 100 | "localhost:28033", 101 | "localhost:28034", 102 | "localhost:28035", 103 | "localhost:28036", 104 | "localhost:28037", 105 | "localhost:28038", 106 | "localhost:28039", 107 | "localhost:28040", 108 | 109 | "localhost:28041", 110 | "localhost:28042", 111 | "localhost:28043", 112 | "localhost:28044", 113 | "localhost:28045", 114 | "localhost:28046", 115 | "localhost:28047", 116 | "localhost:28048", 117 | "localhost:28049", 118 | "localhost:28050", 119 | } 120 | -------------------------------------------------------------------------------- /http_1m/stdserver/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "runtime" 9 | "sync/atomic" 10 | "time" 11 | ) 12 | 13 | var ( 14 | qps uint64 = 0 15 | total uint64 = 0 16 | ) 17 | 18 | func onEcho(w http.ResponseWriter, r *http.Request) { 19 | data, _ := io.ReadAll(r.Body) 20 | if len(data) > 0 { 21 | w.Write(data) 22 | } else { 23 | w.Write([]byte(time.Now().Format("20060102 15:04:05"))) 24 | } 25 | atomic.AddUint64(&qps, 1) 26 | } 27 | 28 | func serve(addrs []string) { 29 | for _, v := range addrs { 30 | go func(addr string) { 31 | mux := &http.ServeMux{} 32 | mux.HandleFunc("/echo", onEcho) 33 | 34 | server := http.Server{ 35 | Addr: addr, 36 | Handler: mux, 37 | } 38 | server.ListenAndServe() 39 | }(v) 40 | } 41 | } 42 | 43 | func main() { 44 | go func() { 45 | if err := http.ListenAndServe(":6060", nil); err != nil { 46 | panic(err) 47 | } 48 | }() 49 | 50 | // addrs := []string{"localhost:8888"} 51 | serve(addrs) 52 | 53 | ticker := time.NewTicker(time.Second) 54 | for i := 1; true; i++ { 55 | <-ticker.C 56 | n := atomic.SwapUint64(&qps, 0) 57 | total += n 58 | fmt.Printf("running for %v seconds, NumGoroutine: %v, qps: %v, total: %v\n", i, runtime.NumGoroutine(), n, total) 59 | } 60 | } 61 | 62 | var addrs = []string{ 63 | "localhost:28001", 64 | "localhost:28002", 65 | "localhost:28003", 66 | "localhost:28004", 67 | "localhost:28005", 68 | "localhost:28006", 69 | "localhost:28007", 70 | "localhost:28008", 71 | "localhost:28009", 72 | "localhost:28010", 73 | 74 | "localhost:28011", 75 | "localhost:28012", 76 | "localhost:28013", 77 | "localhost:28014", 78 | "localhost:28015", 79 | "localhost:28016", 80 | "localhost:28017", 81 | "localhost:28018", 82 | "localhost:28019", 83 | "localhost:28020", 84 | 85 | "localhost:28021", 86 | "localhost:28022", 87 | "localhost:28023", 88 | "localhost:28024", 89 | "localhost:28025", 90 | "localhost:28026", 91 | "localhost:28027", 92 | "localhost:28028", 93 | "localhost:28029", 94 | "localhost:28030", 95 | 96 | "localhost:28031", 97 | "localhost:28032", 98 | "localhost:28033", 99 | "localhost:28034", 100 | "localhost:28035", 101 | "localhost:28036", 102 | "localhost:28037", 103 | "localhost:28038", 104 | "localhost:28039", 105 | "localhost:28040", 106 | 107 | "localhost:28041", 108 | "localhost:28042", 109 | "localhost:28043", 110 | "localhost:28044", 111 | "localhost:28045", 112 | "localhost:28046", 113 | "localhost:28047", 114 | "localhost:28048", 115 | "localhost:28049", 116 | "localhost:28050", 117 | } 118 | -------------------------------------------------------------------------------- /http_trailer/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | func main() { 11 | payload := strings.NewReader("hello") 12 | req, err := http.NewRequest("POST", "http://localhost:8888/echo", payload) 13 | if err != nil { 14 | panic(err) 15 | } 16 | req.ContentLength = -1 17 | req.Trailer = http.Header{ 18 | "Trailer_key_01": []string{"Trailer_value_01"}, 19 | "Trailer_key_02": []string{"Trailer_value_02"}, 20 | } 21 | resp, err := http.DefaultClient.Do(req) 22 | if err != nil { 23 | panic(err) 24 | } 25 | fmt.Println("status:", resp.Status) 26 | body, err := io.ReadAll(resp.Body) 27 | if err != nil { 28 | panic(err) 29 | } 30 | fmt.Println("body:", string(body)) 31 | fmt.Println("trailer:", resp.Trailer) 32 | } 33 | -------------------------------------------------------------------------------- /http_trailer/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | _ "net/http/pprof" 9 | "os" 10 | "os/signal" 11 | "sync/atomic" 12 | "time" 13 | 14 | "github.com/lesismal/nbio/nbhttp" 15 | ) 16 | 17 | var ( 18 | qps uint64 = 0 19 | total uint64 = 0 20 | ) 21 | 22 | func onEcho(w http.ResponseWriter, r *http.Request) { 23 | w.Header().Add("Trailer", "Trailer_key_01") 24 | w.Header().Add("Trailer", "Trailer_key_02") 25 | w.Header().Add("Trailer_key_01", "Trailer_value_01") 26 | w.Header().Add("Trailer_key_02", "Trailer_value_02") 27 | data, err := io.ReadAll(r.Body) 28 | if err != nil { 29 | panic(err) 30 | } 31 | if len(data) > 0 { 32 | w.Write(data) 33 | } else { 34 | w.Write([]byte(time.Now().Format("20060102 15:04:05"))) 35 | } 36 | atomic.AddUint64(&qps, 1) 37 | fmt.Println("Trailer:", r.Trailer, r.Header) 38 | } 39 | 40 | func main() { 41 | go func() { 42 | if err := http.ListenAndServe("localhost:6060", nil); err != nil { 43 | panic(err) 44 | } 45 | }() 46 | 47 | mux := &http.ServeMux{} 48 | mux.HandleFunc("/echo", onEcho) 49 | 50 | svr := nbhttp.NewEngine(nbhttp.Config{ 51 | Network: "tcp", 52 | Addrs: []string{"localhost:8888"}, 53 | Handler: mux, 54 | }) // pool.Go) 55 | 56 | err := svr.Start() 57 | if err != nil { 58 | fmt.Printf("nbio.Start failed: %v\n", err) 59 | return 60 | } 61 | 62 | interrupt := make(chan os.Signal, 1) 63 | signal.Notify(interrupt, os.Interrupt) 64 | <-interrupt 65 | 66 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 67 | defer cancel() 68 | svr.Shutdown(ctx) 69 | } 70 | -------------------------------------------------------------------------------- /http_with_other_frameworks/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "net/url" 9 | 10 | "github.com/gorilla/websocket" 11 | ) 12 | 13 | func main() { 14 | httpTest("http://localhost:8080/hello") 15 | websocketTest("localhost:8080") 16 | } 17 | 18 | func httpTest(url string) { 19 | client := &http.Client{} 20 | req, err := http.NewRequest("GET", url, nil) 21 | if err != nil { 22 | log.Fatalf("NewRequest failed: %v", err) 23 | } 24 | 25 | res, err := client.Do(req) 26 | if err != nil { 27 | log.Fatalf("client.Do failed: %v", err) 28 | } 29 | defer res.Body.Close() 30 | 31 | body, err := ioutil.ReadAll(res.Body) 32 | if err != nil { 33 | log.Fatalf("read body failed: %v", err) 34 | } 35 | 36 | log.Printf("http hello success, body: '%v'", string(body)) 37 | } 38 | 39 | func websocketEcho(c *websocket.Conn, messageType int, data []byte) { 40 | err := c.WriteMessage(messageType, data) 41 | if err != nil { 42 | log.Fatalf("WriteMessage failed: %v", err) 43 | } 44 | 45 | receiveType, message, err := c.ReadMessage() 46 | if err != nil { 47 | log.Fatalf("ReadMessage failed: %v", err) 48 | } 49 | 50 | if receiveType != messageType { 51 | log.Fatalf("invalid messageType") 52 | } 53 | 54 | if !bytes.Equal(data, message) { 55 | log.Fatalf("invalid data") 56 | } 57 | 58 | messageTypeString := map[int]string{ 59 | websocket.TextMessage: "TextMessage", 60 | websocket.BinaryMessage: "BinaryMessage", 61 | } 62 | log.Printf("websocket echo success, messageType: [%v], data: '%v'", messageTypeString[messageType], string(data)) 63 | } 64 | 65 | func websocketTest(addr string) { 66 | u := url.URL{Scheme: "ws", Host: addr, Path: "/ws"} 67 | c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) 68 | if err != nil { 69 | log.Fatalf("dial failed: %v", err) 70 | } 71 | defer c.Close() 72 | 73 | data := []byte("hello world") 74 | for i := 0; i < 5; i++ { 75 | websocketEcho(c, websocket.TextMessage, data) 76 | websocketEcho(c, websocket.BinaryMessage, data) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /http_with_other_frameworks/echo_server/echo_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net/http" 7 | "os" 8 | "os/signal" 9 | "time" 10 | 11 | "github.com/labstack/echo" 12 | "github.com/lesismal/nbio/nbhttp" 13 | "github.com/lesismal/nbio/nbhttp/websocket" 14 | ) 15 | 16 | var upgrader = websocket.NewUpgrader() 17 | 18 | func init() { 19 | upgrader.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 20 | // echo 21 | c.WriteMessage(messageType, data) 22 | }) 23 | upgrader.OnClose(func(c *websocket.Conn, err error) { 24 | log.Println("OnClose:", c.RemoteAddr().String(), err) 25 | }) 26 | } 27 | 28 | func onHello(c echo.Context) error { 29 | return c.String(http.StatusOK, "hello world") 30 | } 31 | 32 | func onWebsocket(c echo.Context) error { 33 | w := c.Response() 34 | r := c.Request() 35 | 36 | conn, err := upgrader.Upgrade(w, r, nil) 37 | if err != nil { 38 | return err 39 | } 40 | log.Println("OnOpen:", conn.RemoteAddr().String()) 41 | return nil 42 | } 43 | 44 | func main() { 45 | e := echo.New() 46 | 47 | e.GET("/hello", onHello) 48 | e.GET("/ws", onWebsocket) 49 | 50 | engine := nbhttp.NewEngine(nbhttp.Config{ 51 | Network: "tcp", 52 | Addrs: []string{"localhost:8080"}, 53 | Handler: e, 54 | }) 55 | 56 | err := engine.Start() 57 | if err != nil { 58 | log.Fatalf("nbio.Start failed: %v\n", err) 59 | } 60 | 61 | log.Println("serving [labstack/echo] on [nbio]") 62 | 63 | interrupt := make(chan os.Signal, 1) 64 | signal.Notify(interrupt, os.Interrupt) 65 | <-interrupt 66 | 67 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 68 | defer cancel() 69 | engine.Shutdown(ctx) 70 | } 71 | -------------------------------------------------------------------------------- /http_with_other_frameworks/fiber_server/fiber_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net/http" 7 | "os" 8 | "os/signal" 9 | "strings" 10 | "time" 11 | 12 | "github.com/gofiber/fiber/v3" 13 | "github.com/gofiber/fiber/v3/middleware/adaptor" 14 | "github.com/lesismal/nbio/nbhttp" 15 | "github.com/lesismal/nbio/nbhttp/websocket" 16 | ) 17 | 18 | var upgrader = websocket.NewUpgrader() 19 | 20 | func init() { 21 | upgrader.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 22 | // echo 23 | c.WriteMessage(messageType, data) 24 | }) 25 | upgrader.OnClose(func(c *websocket.Conn, err error) { 26 | log.Println("OnClose:", c.RemoteAddr().String(), err) 27 | }) 28 | } 29 | 30 | func FiberOnHello(c fiber.Ctx) error { 31 | c.SendString("hello world") 32 | return nil 33 | } 34 | 35 | func HTTPOnWebsocket(w http.ResponseWriter, r *http.Request) { 36 | conn, err := upgrader.Upgrade(w, r, nil) 37 | if err != nil { 38 | panic(err) 39 | } 40 | log.Println("OnOpen:", conn.RemoteAddr().String()) 41 | } 42 | 43 | func main() { 44 | router := fiber.New() 45 | router.Get("/hello", FiberOnHello) 46 | fiberHandler := adaptor.FiberApp(router) 47 | 48 | engine := nbhttp.NewEngine(nbhttp.Config{ 49 | Network: "tcp", 50 | Addrs: []string{"localhost:8080"}, 51 | 52 | // For HTTP, use fiber. 53 | // For Webosocket, use http.Handler; 54 | Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 55 | if strings.HasPrefix(r.URL.Path, "/ws") { 56 | HTTPOnWebsocket(w, r) 57 | } else { 58 | fiberHandler(w, r) 59 | } 60 | }), 61 | }) 62 | 63 | err := engine.Start() 64 | if err != nil { 65 | log.Fatalf("nbio.Start failed: %v\n", err) 66 | } 67 | 68 | log.Println("serving [gin-gonic/gin] on [nbio]") 69 | 70 | interrupt := make(chan os.Signal, 1) 71 | signal.Notify(interrupt, os.Interrupt) 72 | <-interrupt 73 | 74 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 75 | defer cancel() 76 | engine.Shutdown(ctx) 77 | } 78 | -------------------------------------------------------------------------------- /http_with_other_frameworks/gin_server/gin_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net/http" 7 | "os" 8 | "os/signal" 9 | "time" 10 | 11 | "github.com/gin-gonic/gin" 12 | "github.com/lesismal/nbio/nbhttp" 13 | "github.com/lesismal/nbio/nbhttp/websocket" 14 | ) 15 | 16 | var upgrader = websocket.NewUpgrader() 17 | 18 | func init() { 19 | upgrader.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 20 | // echo 21 | c.WriteMessage(messageType, data) 22 | }) 23 | upgrader.OnClose(func(c *websocket.Conn, err error) { 24 | log.Println("OnClose:", c.RemoteAddr().String(), err) 25 | }) 26 | } 27 | 28 | func onHello(c *gin.Context) { 29 | c.String(http.StatusOK, "hello world") 30 | } 31 | 32 | func onWebsocket(c *gin.Context) { 33 | w := c.Writer 34 | r := c.Request 35 | conn, err := upgrader.Upgrade(w, r, nil) 36 | if err != nil { 37 | panic(err) 38 | } 39 | log.Println("OnOpen:", conn.RemoteAddr().String()) 40 | } 41 | 42 | func main() { 43 | router := gin.New() 44 | 45 | router.GET("/hello", onHello) 46 | router.GET("/ws", onWebsocket) 47 | 48 | engine := nbhttp.NewEngine(nbhttp.Config{ 49 | Network: "tcp", 50 | Addrs: []string{"localhost:8080"}, 51 | Handler: router, 52 | }) 53 | 54 | err := engine.Start() 55 | if err != nil { 56 | log.Fatalf("nbio.Start failed: %v\n", err) 57 | } 58 | 59 | log.Println("serving [gin-gonic/gin] on [nbio]") 60 | 61 | interrupt := make(chan os.Signal, 1) 62 | signal.Notify(interrupt, os.Interrupt) 63 | <-interrupt 64 | 65 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 66 | defer cancel() 67 | engine.Shutdown(ctx) 68 | } 69 | -------------------------------------------------------------------------------- /http_with_other_frameworks/go-chi_server/go-chi_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net/http" 7 | "os" 8 | "os/signal" 9 | "time" 10 | 11 | "github.com/go-chi/chi/v5" 12 | "github.com/lesismal/nbio/nbhttp" 13 | "github.com/lesismal/nbio/nbhttp/websocket" 14 | ) 15 | 16 | var upgrader = websocket.NewUpgrader() 17 | 18 | func init() { 19 | upgrader.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 20 | // echo 21 | c.WriteMessage(messageType, data) 22 | }) 23 | upgrader.OnClose(func(c *websocket.Conn, err error) { 24 | log.Println("OnClose:", c.RemoteAddr().String(), err) 25 | }) 26 | } 27 | 28 | func onHello(hrw http.ResponseWriter, req *http.Request) { 29 | hrw.WriteHeader(http.StatusOK) 30 | hrw.Write([]byte("hello world")) 31 | } 32 | 33 | func onWebsocket(hrw http.ResponseWriter, req *http.Request) { 34 | conn, err := upgrader.Upgrade(hrw, req, nil) 35 | if err != nil { 36 | panic(err) 37 | } 38 | log.Println("OnOpen:", conn.RemoteAddr().String()) 39 | } 40 | 41 | func main() { 42 | router := chi.NewRouter() 43 | 44 | router.Get("/hello", onHello) 45 | router.Get("/ws", onWebsocket) 46 | 47 | engine := nbhttp.NewEngine(nbhttp.Config{ 48 | Network: "tcp", 49 | Addrs: []string{"localhost:8080"}, 50 | }) 51 | engine.Handler = router 52 | 53 | err := engine.Start() 54 | if err != nil { 55 | log.Fatalf("nbio.Start failed: %v\n", err) 56 | } 57 | 58 | log.Println("serving [go-chi/chi] on [nbio]") 59 | 60 | interrupt := make(chan os.Signal, 1) 61 | signal.Notify(interrupt, os.Interrupt) 62 | <-interrupt 63 | 64 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 65 | defer cancel() 66 | engine.Shutdown(ctx) 67 | } 68 | -------------------------------------------------------------------------------- /http_with_other_frameworks/iris_server/iris_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "os/signal" 8 | "time" 9 | 10 | "github.com/kataras/iris/v12" 11 | "github.com/lesismal/nbio/nbhttp" 12 | "github.com/lesismal/nbio/nbhttp/websocket" 13 | ) 14 | 15 | var upgrader = websocket.NewUpgrader() 16 | 17 | func init() { 18 | upgrader.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 19 | // echo 20 | c.WriteMessage(messageType, data) 21 | }) 22 | upgrader.OnClose(func(c *websocket.Conn, err error) { 23 | log.Println("OnClose:", c.RemoteAddr().String(), err) 24 | }) 25 | } 26 | 27 | func onHello(ctx iris.Context) { 28 | ctx.WriteString("hello world") 29 | } 30 | 31 | func onWebsocket(ctx iris.Context) { 32 | conn, err := upgrader.Upgrade(ctx.ResponseWriter(), ctx.Request(), nil) 33 | if err != nil { 34 | panic(err) 35 | } 36 | log.Println("OnOpen:", conn.RemoteAddr().String()) 37 | } 38 | 39 | func main() { 40 | app := iris.New() 41 | app.Get("/hello", onHello) 42 | app.Get("/ws", onWebsocket) 43 | 44 | err := app.Build() 45 | if err != nil { 46 | panic(err) 47 | } 48 | 49 | engine := nbhttp.NewEngine(nbhttp.Config{ 50 | Network: "tcp", 51 | Addrs: []string{"localhost:8080"}, 52 | Handler: app, 53 | }) 54 | 55 | err = engine.Start() 56 | if err != nil { 57 | log.Fatalf("nbio.Start failed: %v\n", err) 58 | } 59 | 60 | log.Println("serving [go-chi/chi] on [nbio]") 61 | 62 | interrupt := make(chan os.Signal, 1) 63 | signal.Notify(interrupt, os.Interrupt) 64 | <-interrupt 65 | 66 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 67 | defer cancel() 68 | engine.Shutdown(ctx) 69 | } 70 | -------------------------------------------------------------------------------- /lt_et/lt_et.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/lesismal/nbio" 7 | "github.com/lesismal/nbio/logging" 8 | "time" 9 | ) 10 | 11 | func LTServer() { 12 | engine := nbio.NewEngine(nbio.Config{ 13 | Network: "tcp", 14 | Addrs: []string{":8899"}, 15 | ReadBufferSize: 2, 16 | MaxConnReadTimesPerEventLoop: 1, 17 | MaxWriteBufferSize: 2, 18 | }) 19 | 20 | engine.OnData(func(c *nbio.Conn, data []byte) { 21 | fmt.Printf("【LT】read data len: %d, value: %s\n", len(data), string(data)) 22 | }) 23 | err := engine.Start() 24 | if err != nil { 25 | fmt.Printf("nbio.start failed: %v\n", err) 26 | return 27 | } 28 | defer engine.Stop() 29 | time.Sleep(3 * time.Second) 30 | go Client("localhost:8899", "hello") 31 | 32 | <-make(chan int) 33 | } 34 | 35 | // ETServer In ETMod 36 | func ETServer() { 37 | engine := nbio.NewEngine(nbio.Config{ 38 | Network: "tcp", 39 | Addrs: []string{":8888"}, 40 | ReadBufferSize: 2, 41 | MaxConnReadTimesPerEventLoop: 1, 42 | MaxWriteBufferSize: 2, 43 | EpollMod: nbio.EPOLLET, 44 | }) 45 | 46 | engine.OnData(func(c *nbio.Conn, data []byte) { 47 | fmt.Printf("【ET】read data len: %d, value: %s\n", len(data), string(data)) 48 | }) 49 | err := engine.Start() 50 | if err != nil { 51 | fmt.Printf("nbio.start failed: %v\n", err) 52 | return 53 | } 54 | defer engine.Stop() 55 | time.Sleep(3 * time.Second) 56 | go Client("localhost:8888", "world") 57 | 58 | <-make(chan int) 59 | } 60 | 61 | func Client(addr string, data string) { 62 | ctx, _ := context.WithTimeout(context.Background(), 60*time.Second) 63 | engine := nbio.NewEngine(nbio.Config{}) 64 | done := make(chan int) 65 | 66 | err := engine.Start() 67 | if err != nil { 68 | fmt.Printf("start failed: %v\n", err) 69 | } 70 | defer engine.Stop() 71 | 72 | c, err := nbio.Dial("tcp", addr) 73 | if err != nil { 74 | panic(err) 75 | } 76 | engine.AddConn(c) 77 | time.Sleep(2 * time.Second) 78 | c.Write([]byte(data)) 79 | 80 | select { 81 | case <-ctx.Done(): 82 | logging.Error("timeout") 83 | case <-done: 84 | logging.Info("success") 85 | } 86 | } 87 | 88 | // The difference of ET and LT 89 | // In LT mode, Kernel will register epitem to wq again after trigger event, but In ET mode, Kernel will not register 90 | // epitem to wq 91 | 92 | // How nbio handle 93 | // when socket data first come, io poller for per conn will only register Read Event(linux call EPOLLIN, bsd call EVFILT_READ) 94 | // When Read event come 95 | // In LT mode, nbio will read MaxConnReadTimesPerEventLoop * ReadBufferSize size。left data will be read in next Read Event 96 | // In ET mode, nbio will change MaxConnReadTimesPerEventLoop to (1<<31 - 1), this indicates nbio will read all the data 97 | 98 | // When Write event come and Kernel can't read all the data 99 | // In LT / ET, nbio will both register the Write event(linux call EPOLLOUT, bsd call EVFILT_WRITE)。and when next Write event arrive 100 | // nbio will call flush() to write left data and finally reset the Read Event 101 | func main() { 102 | logging.SetLevel(logging.LevelDebug) 103 | fmt.Println("start to handle LT server") 104 | go LTServer() 105 | time.Sleep(10 * time.Second) 106 | fmt.Println("start to handle ET server") 107 | go ETServer() 108 | <-make(chan int) 109 | } 110 | -------------------------------------------------------------------------------- /netstd/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "math/rand" 8 | "net" 9 | "time" 10 | 11 | "github.com/lesismal/nbio" 12 | "github.com/lesismal/nbio/logging" 13 | ) 14 | 15 | func main() { 16 | var ( 17 | ret []byte 18 | buf = make([]byte, 1024) 19 | addr = "localhost:8888" 20 | ctx, _ = context.WithTimeout(context.Background(), time.Second) 21 | ) 22 | 23 | logging.SetLevel(logging.LevelInfo) 24 | 25 | rand.Read(buf) 26 | 27 | g := nbio.NewEngine(nbio.Config{}) 28 | 29 | done := make(chan int) 30 | g.OnData(func(c *nbio.Conn, data []byte) { 31 | ret = append(ret, data...) 32 | if len(ret) == len(buf) { 33 | if bytes.Equal(buf, ret) { 34 | close(done) 35 | } 36 | } 37 | }) 38 | 39 | err := g.Start() 40 | if err != nil { 41 | fmt.Printf("Start failed: %v\n", err) 42 | } 43 | defer g.Stop() 44 | 45 | c, err := net.Dial("tcp", addr) 46 | if err != nil { 47 | fmt.Printf("Dial failed: %v\n", err) 48 | } 49 | g.AddConn(c) 50 | c.Write(buf) 51 | 52 | select { 53 | case <-ctx.Done(): 54 | logging.Error("timeout") 55 | case <-done: 56 | logging.Info("success") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /netstd/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | 8 | "github.com/lesismal/nbio" 9 | ) 10 | 11 | func main() { 12 | g := nbio.NewEngine(nbio.Config{}) 13 | g.OnOpen(func(c *nbio.Conn) { 14 | log.Println("OnOpen:", c.RemoteAddr().String()) 15 | }) 16 | 17 | g.OnData(func(c *nbio.Conn, data []byte) { 18 | c.Write(append([]byte{}, data...)) 19 | }) 20 | 21 | err := g.Start() 22 | if err != nil { 23 | fmt.Printf("nbio.Start failed: %v\n", err) 24 | return 25 | } 26 | defer g.Stop() 27 | 28 | ln, err := net.Listen("tcp", "localhost:8888") 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | for { 33 | conn, err := ln.Accept() 34 | if err != nil { 35 | log.Println("Accept failed:", err) 36 | continue 37 | } 38 | g.AddConn(conn) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /shutdown/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "runtime" 11 | "time" 12 | 13 | "github.com/lesismal/nbio/nbhttp" 14 | ) 15 | 16 | func onEcho(w http.ResponseWriter, r *http.Request) { 17 | data, _ := io.ReadAll(r.Body) 18 | if len(data) > 0 { 19 | w.Write(data) 20 | } else { 21 | w.Write([]byte(time.Now().Format("20060102 15:04:05"))) 22 | } 23 | } 24 | 25 | func main() { 26 | mux := &http.ServeMux{} 27 | mux.HandleFunc("/echo", onEcho) 28 | svr := nbhttp.NewEngine(nbhttp.Config{ 29 | Network: "tcp", 30 | Addrs: []string{"localhost:8888"}, 31 | MaxLoad: 1000000, 32 | NPoller: runtime.NumCPU() * 2, 33 | Handler: mux, 34 | }) 35 | 36 | err := svr.Start() 37 | if err != nil { 38 | fmt.Printf("nbio.Start failed: %v\n", err) 39 | return 40 | } 41 | 42 | interrupt := make(chan os.Signal, 1) 43 | signal.Notify(interrupt, os.Interrupt) 44 | <-interrupt 45 | 46 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 47 | defer cancel() 48 | svr.Shutdown(ctx) 49 | } 50 | -------------------------------------------------------------------------------- /sticky/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "time" 6 | 7 | "github.com/lesismal/nbio-examples/sticky/proxy" 8 | ) 9 | 10 | var ( 11 | src = flag.String("src", "localhost:8888", "src addr for listening") 12 | dst = flag.String("dst", "localhost:9999", "dst addr for dialing") 13 | interval = flag.Int("interval", 0, "byte sending interval, in nanoseconds") 14 | ) 15 | 16 | func main() { 17 | flag.Parse() 18 | proxy.Run(*src, *dst, time.Duration(*interval), func(max int) int { 19 | return 1 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /sticky/proxy/proxy.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "time" 8 | ) 9 | 10 | func stickyTunnel(clientConn *net.TCPConn, dst string, interval time.Duration, f func(max int) int) { 11 | serverConn, dialErr := net.Dial("tcp", dst) 12 | log.Printf("+ stickyTunnel: [%v -> %v]\n", clientConn.LocalAddr().String(), dst) 13 | if dialErr == nil { 14 | c2sCor := func() { 15 | defer func() { 16 | recover() 17 | }() 18 | 19 | var buf = make([]byte, 4096) 20 | for i := 0; true; i++ { 21 | nread, err := clientConn.Read(buf) 22 | if err != nil { 23 | clientConn.Close() 24 | serverConn.Close() 25 | break 26 | } 27 | tmp := buf[:nread] 28 | for len(tmp) > 0 { 29 | n := f(len(tmp)) 30 | if n == 0 { 31 | n = len(tmp) 32 | } 33 | _, err := serverConn.Write(tmp[:n]) 34 | if err != nil { 35 | clientConn.Close() 36 | serverConn.Close() 37 | return 38 | } 39 | if interval > 0 { 40 | time.Sleep(interval) 41 | } 42 | tmp = tmp[n:] 43 | } 44 | } 45 | } 46 | 47 | s2cCor := func() { 48 | defer func() { 49 | recover() 50 | }() 51 | 52 | var buf = make([]byte, 4096) 53 | 54 | for i := 0; true; i++ { 55 | nread, err := serverConn.Read(buf) 56 | if err != nil { 57 | clientConn.Close() 58 | serverConn.Close() 59 | break 60 | } 61 | 62 | tmp := buf[:nread] 63 | for len(tmp) > 0 { 64 | n := f(len(tmp)) 65 | if n == 0 { 66 | n = len(tmp) 67 | } 68 | _, err := clientConn.Write(tmp[:n]) 69 | if err != nil { 70 | clientConn.Close() 71 | serverConn.Close() 72 | return 73 | } 74 | if interval > 0 { 75 | time.Sleep(interval) 76 | } 77 | tmp = tmp[n:] 78 | } 79 | } 80 | } 81 | 82 | go c2sCor() 83 | go s2cCor() 84 | } else { 85 | clientConn.Close() 86 | } 87 | } 88 | 89 | // Run . 90 | func Run(src string, dst string, interval time.Duration, f func(max int) int) { 91 | tcpAddr, err := net.ResolveTCPAddr("tcp4", src) 92 | if err != nil { 93 | fmt.Println("ResolveTCPAddr Error: ", err) 94 | return 95 | } 96 | 97 | listener, err2 := net.ListenTCP("tcp", tcpAddr) 98 | if err2 != nil { 99 | fmt.Println("ListenTCP Error: ", err2) 100 | return 101 | } 102 | 103 | defer listener.Close() 104 | 105 | fmt.Println(fmt.Sprintf("proxy running on: [%s -> %s]", src, dst)) 106 | for { 107 | conn, err := listener.AcceptTCP() 108 | 109 | if err != nil { 110 | fmt.Println("AcceptTCP Error: ", err2) 111 | } else { 112 | go stickyTunnel(conn, dst, interval, f) 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /tls/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "runtime" 8 | "sync/atomic" 9 | "time" 10 | 11 | "github.com/lesismal/nbio" 12 | "github.com/lesismal/nbio/extension/tls" 13 | ) 14 | 15 | var ( 16 | qps int64 = 0 17 | total int64 = 0 18 | 19 | tlsConfig = &tls.Config{ 20 | InsecureSkipVerify: true, 21 | } 22 | ) 23 | 24 | func main() { 25 | var ( 26 | wbuf = []byte("hello world") 27 | addr = "localhost:8888" 28 | ) 29 | 30 | g := nbio.NewEngine(nbio.Config{}) 31 | 32 | isClient := true 33 | g.OnOpen(tls.WrapOpen(tlsConfig, isClient, func(c *nbio.Conn, tlsConn *tls.Conn) { 34 | log.Println("OnOpen:", c.RemoteAddr().String()) 35 | // tlsConn.Write(wbuf) 36 | })) 37 | g.OnClose(tls.WrapClose(func(c *nbio.Conn, tlsConn *tls.Conn, err error) { 38 | log.Println("OnClose:", c.RemoteAddr().String()) 39 | })) 40 | g.OnData(tls.WrapData(func(c *nbio.Conn, tlsConn *tls.Conn, data []byte) { 41 | if bytes.Equal(wbuf, data) { 42 | tlsConn.Write(wbuf) 43 | atomic.AddInt64(&qps, 1) 44 | } else { 45 | c.Close() 46 | } 47 | })) 48 | 49 | err := g.Start() 50 | if err != nil { 51 | fmt.Printf("Start failed: %v\n", err) 52 | } 53 | defer g.Stop() 54 | 55 | for i := 0; i < 1; i++ { 56 | func() { 57 | // step 1: make a tls.Conn by tls.Dial 58 | tlsConn, err := tls.Dial("tcp", addr, tlsConfig) 59 | if err != nil { 60 | log.Fatalf("Dial failed: %v\n", err) 61 | } 62 | // step 2: 63 | // add tls.Conn.conn to gopher, and get the nbio.Conn. the new nbio.Conn is non-blocking 64 | nbConn, err := nbio.NBConn(tlsConn.Conn()) 65 | if err != nil { 66 | log.Fatalf("AddConn failed: %v\n", err) 67 | } 68 | // step 3: set tls.Conn and nbio.Conn to each other, and add nbio.Conn to the gopher 69 | isNonblock := true 70 | nbConn.SetSession(tlsConn) 71 | tlsConn.ResetConn(nbConn, isNonblock) 72 | g.AddConn(nbConn) 73 | 74 | // step 4: write data here or in the OnOpen handler or anywhere 75 | tlsConn.Write(wbuf) 76 | }() 77 | } 78 | 79 | ticker := time.NewTicker(time.Second) 80 | for i := 1; true; i++ { 81 | <-ticker.C 82 | nSuccess := atomic.SwapInt64(&qps, 0) 83 | total += nSuccess 84 | fmt.Printf("running for %v seconds, NumGoroutine: %v, success: %v, totalSuccess: %v\n", i, runtime.NumGoroutine(), nSuccess, total) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tls/client_std/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "io" 6 | "log" 7 | ) 8 | 9 | func main() { 10 | tlsConfig := &tls.Config{ 11 | InsecureSkipVerify: true, 12 | } 13 | 14 | conn, err := tls.Dial("tcp", "localhost:8888", tlsConfig) 15 | if err != nil { 16 | panic(err) 17 | } 18 | defer conn.Close() 19 | 20 | wbuf := []byte("hello") 21 | n1, err := conn.Write(wbuf) 22 | if err != nil || n1 != len(wbuf) { 23 | log.Fatalf("conn.Write failed: %v, %v", n1, err) 24 | } 25 | 26 | rbuf := make([]byte, len(wbuf)) 27 | n2, err := io.ReadFull(conn, rbuf) 28 | if err != nil { 29 | log.Fatalf("conn.Read failed: %v", err) 30 | } 31 | if n2 != n1 || string(rbuf) != string(wbuf) { 32 | log.Fatalf("conn.Read failed: %v, %v", n2, string(wbuf)) 33 | } 34 | log.Println("response:", string(rbuf)) 35 | } 36 | -------------------------------------------------------------------------------- /tls/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/lesismal/llib/std/crypto/tls" 7 | "github.com/lesismal/nbio" 8 | ntls "github.com/lesismal/nbio/extension/tls" 9 | ) 10 | 11 | func main() { 12 | cert, err := tls.X509KeyPair(rsaCertPEM, rsaKeyPEM) 13 | if err != nil { 14 | log.Fatalf("tls.X509KeyPair failed: %v", err) 15 | } 16 | tlsConfig := &tls.Config{ 17 | Certificates: []tls.Certificate{cert}, 18 | InsecureSkipVerify: true, 19 | } 20 | 21 | g := nbio.NewEngine(nbio.Config{ 22 | Network: "tcp", 23 | Addrs: []string{"localhost:8888"}, 24 | }) 25 | isClient := false 26 | g.OnOpen(ntls.WrapOpen(tlsConfig, isClient, func(c *nbio.Conn, tlsConn *tls.Conn) { 27 | log.Println("OnOpen:", c.RemoteAddr().String()) 28 | })) 29 | g.OnClose(ntls.WrapClose(func(c *nbio.Conn, tlsConn *tls.Conn, err error) { 30 | log.Println("OnClose:", c.RemoteAddr().String()) 31 | })) 32 | g.OnData(ntls.WrapData(func(c *nbio.Conn, tlsConn *tls.Conn, data []byte) { 33 | log.Println("OnData:", c.RemoteAddr().String(), string(data)) 34 | tlsConn.Write(data) 35 | })) 36 | 37 | err = g.Start() 38 | if err != nil { 39 | log.Fatalf("nbio.Start failed: %v\n", err) 40 | return 41 | } 42 | defer g.Stop() 43 | 44 | g.Wait() 45 | } 46 | 47 | var rsaCertPEM = []byte(`-----BEGIN CERTIFICATE----- 48 | MIIDazCCAlOgAwIBAgIUJeohtgk8nnt8ofratXJg7kUJsI4wDQYJKoZIhvcNAQEL 49 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 50 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMDcwODIyNThaFw0zMDEy 51 | MDUwODIyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 52 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 53 | AQUAA4IBDwAwggEKAoIBAQCy+ZrIvwwiZv4bPmvKx/637ltZLwfgh3ouiEaTchGu 54 | IQltthkqINHxFBqqJg44TUGHWthlrq6moQuKnWNjIsEc6wSD1df43NWBLgdxbPP0 55 | x4tAH9pIJU7TQqbznjDBhzRbUjVXBIcn7bNknY2+5t784pPF9H1v7h8GqTWpNH9l 56 | cz/v+snoqm9HC+qlsFLa4A3X9l5v05F1uoBfUALlP6bWyjHAfctpiJkoB9Yw1TJa 57 | gpq7E50kfttwfKNkkAZIbib10HugkMoQJAs2EsGkje98druIl8IXmuvBIF6nZHuM 58 | lt3UIZjS9RwPPLXhRHt1P0mR7BoBcOjiHgtSEs7Wk+j7AgMBAAGjUzBRMB0GA1Ud 59 | DgQWBBQdheJv73XSOhgMQtkwdYPnfO02+TAfBgNVHSMEGDAWgBQdheJv73XSOhgM 60 | QtkwdYPnfO02+TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf 61 | SKVNMdmBpD9m53kCrguo9iKQqmhnI0WLkpdWszc/vBgtpOE5ENOfHGAufHZve871 62 | 2fzTXrgR0TF6UZWsQOqCm5Oh3URsCdXWewVMKgJ3DCii6QJ0MnhSFt6+xZE9C6Hi 63 | WhcywgdR8t/JXKDam6miohW8Rum/IZo5HK9Jz/R9icKDGumcqoaPj/ONvY4EUwgB 64 | irKKB7YgFogBmCtgi30beLVkXgk0GEcAf19lHHtX2Pv/lh3m34li1C9eBm1ca3kk 65 | M2tcQtm1G89NROEjcG92cg+GX3GiWIjbI0jD1wnVy2LCOXMgOVbKfGfVKISFt0b1 66 | DNn00G8C6ttLoGU2snyk 67 | -----END CERTIFICATE----- 68 | `) 69 | 70 | var rsaKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- 71 | MIIEogIBAAKCAQEAsvmayL8MImb+Gz5rysf+t+5bWS8H4Id6LohGk3IRriEJbbYZ 72 | KiDR8RQaqiYOOE1Bh1rYZa6upqELip1jYyLBHOsEg9XX+NzVgS4HcWzz9MeLQB/a 73 | SCVO00Km854wwYc0W1I1VwSHJ+2zZJ2Nvube/OKTxfR9b+4fBqk1qTR/ZXM/7/rJ 74 | 6KpvRwvqpbBS2uAN1/Zeb9ORdbqAX1AC5T+m1soxwH3LaYiZKAfWMNUyWoKauxOd 75 | JH7bcHyjZJAGSG4m9dB7oJDKECQLNhLBpI3vfHa7iJfCF5rrwSBep2R7jJbd1CGY 76 | 0vUcDzy14UR7dT9JkewaAXDo4h4LUhLO1pPo+wIDAQABAoIBAF6yWwekrlL1k7Xu 77 | jTI6J7hCUesaS1yt0iQUzuLtFBXCPS7jjuUPgIXCUWl9wUBhAC8SDjWe+6IGzAiH 78 | xjKKDQuz/iuTVjbDAeTb6exF7b6yZieDswdBVjfJqHR2Wu3LEBTRpo9oQesKhkTS 79 | aFF97rZ3XCD9f/FdWOU5Wr8wm8edFK0zGsZ2N6r57yf1N6ocKlGBLBZ0v1Sc5ShV 80 | 1PVAxeephQvwL5DrOgkArnuAzwRXwJQG78L0aldWY2q6xABQZQb5+ml7H/kyytef 81 | i+uGo3jHKepVALHmdpCGr9Yv+yCElup+ekv6cPy8qcmMBqGMISL1i1FEONxLcKWp 82 | GEJi6QECgYEA3ZPGMdUm3f2spdHn3C+/+xskQpz6efiPYpnqFys2TZD7j5OOnpcP 83 | ftNokA5oEgETg9ExJQ8aOCykseDc/abHerYyGw6SQxmDbyBLmkZmp9O3iMv2N8Pb 84 | Nrn9kQKSr6LXZ3gXzlrDvvRoYUlfWuLSxF4b4PYifkA5AfsdiKkj+5sCgYEAzseF 85 | XDTRKHHJnzxZDDdHQcwA0G9agsNj64BGUEjsAGmDiDyqOZnIjDLRt0O2X3oiIE5S 86 | TXySSEiIkxjfErVJMumLaIwqVvlS4pYKdQo1dkM7Jbt8wKRQdleRXOPPN7msoEUk 87 | Ta9ZsftHVUknPqblz9Uthb5h+sRaxIaE1llqDiECgYATS4oHzuL6k9uT+Qpyzymt 88 | qThoIJljQ7TgxjxvVhD9gjGV2CikQM1Vov1JBigj4Toc0XuxGXaUC7cv0kAMSpi2 89 | Y+VLG+K6ux8J70sGHTlVRgeGfxRq2MBfLKUbGplBeDG/zeJs0tSW7VullSkblgL6 90 | nKNa3LQ2QEt2k7KHswryHwKBgENDxk8bY1q7wTHKiNEffk+aFD25q4DUHMH0JWti 91 | fVsY98+upFU+gG2S7oOmREJE0aser0lDl7Zp2fu34IEOdfRY4p+s0O0gB+Vrl5VB 92 | L+j7r9bzaX6lNQN6MvA7ryHahZxRQaD/xLbQHgFRXbHUyvdTyo4yQ1821qwNclLk 93 | HUrhAoGAUtjR3nPFR4TEHlpTSQQovS8QtGTnOi7s7EzzdPWmjHPATrdLhMA0ezPj 94 | Mr+u5TRncZBIzAZtButlh1AHnpN/qO3P0c0Rbdep3XBc/82JWO8qdb5QvAkxga3X 95 | BpA7MNLxiqss+rCbwf3NbWxEMiDQ2zRwVoafVFys7tjmv6t2Xck= 96 | -----END RSA PRIVATE KEY----- 97 | `) 98 | -------------------------------------------------------------------------------- /tls/test/tls_test.go: -------------------------------------------------------------------------------- 1 | package tlstest 2 | 3 | import ( 4 | "context" 5 | "crypto/tls" 6 | "fmt" 7 | "log" 8 | "sync" 9 | "testing" 10 | "time" 11 | 12 | ltls "github.com/lesismal/llib/std/crypto/tls" 13 | "github.com/lesismal/nbio" 14 | ntls "github.com/lesismal/nbio/extension/tls" 15 | ) 16 | 17 | func client(ctx context.Context, count int) { 18 | tlsConfig := &tls.Config{ 19 | InsecureSkipVerify: true, 20 | } 21 | 22 | conn, err := tls.Dial("tcp", "localhost:8887", tlsConfig) 23 | if err != nil { 24 | panic(err) 25 | } 26 | defer conn.Close() 27 | 28 | for i := 0; i < count; i++ { 29 | wbuf := []byte(fmt.Sprintf("hello %d", i)) 30 | n1, err := conn.Write(wbuf) 31 | if err != nil || n1 != len(wbuf) { 32 | log.Fatalf("conn.Write failed: %v, %v", n1, err) 33 | } 34 | 35 | } 36 | 37 | <-ctx.Done() 38 | } 39 | 40 | func server(ctx context.Context, cancelFunc context.CancelFunc, readCount int) { 41 | cert, err := ltls.X509KeyPair(rsaCertPEM, rsaKeyPEM) 42 | if err != nil { 43 | log.Fatalf("tls.X509KeyPair failed: %v", err) 44 | } 45 | tlsConfig := <ls.Config{ 46 | Certificates: []ltls.Certificate{cert}, 47 | InsecureSkipVerify: true, 48 | } 49 | 50 | g := nbio.NewEngine(nbio.Config{ 51 | Network: "tcp", 52 | Addrs: []string{"localhost:8887"}, 53 | }) 54 | isClient := false 55 | count := 0 56 | g.OnOpen(ntls.WrapOpen(tlsConfig, isClient, func(c *nbio.Conn, tlsConn *ltls.Conn) { 57 | log.Println("OnOpen:", c.RemoteAddr().String()) 58 | })) 59 | g.OnClose(ntls.WrapClose(func(c *nbio.Conn, tlsConn *ltls.Conn, err error) { 60 | log.Println("OnClose:", c.RemoteAddr().String()) 61 | })) 62 | g.OnData(ntls.WrapData(func(c *nbio.Conn, tlsConn *ltls.Conn, data []byte) { 63 | log.Println("OnData:", c.RemoteAddr().String(), string(data)) 64 | count++ 65 | if count == readCount { 66 | cancelFunc() 67 | } 68 | 69 | })) 70 | 71 | err = g.Start() 72 | if err != nil { 73 | log.Fatalf("nbio.Start failed: %v\n", err) 74 | return 75 | } 76 | defer g.Stop() 77 | <-ctx.Done() 78 | log.Println("server shutdown") 79 | g.Wait() 80 | } 81 | 82 | var rsaCertPEM = []byte(`-----BEGIN CERTIFICATE----- 83 | MIIDazCCAlOgAwIBAgIUJeohtgk8nnt8ofratXJg7kUJsI4wDQYJKoZIhvcNAQEL 84 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 85 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMDcwODIyNThaFw0zMDEy 86 | MDUwODIyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 87 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 88 | AQUAA4IBDwAwggEKAoIBAQCy+ZrIvwwiZv4bPmvKx/637ltZLwfgh3ouiEaTchGu 89 | IQltthkqINHxFBqqJg44TUGHWthlrq6moQuKnWNjIsEc6wSD1df43NWBLgdxbPP0 90 | x4tAH9pIJU7TQqbznjDBhzRbUjVXBIcn7bNknY2+5t784pPF9H1v7h8GqTWpNH9l 91 | cz/v+snoqm9HC+qlsFLa4A3X9l5v05F1uoBfUALlP6bWyjHAfctpiJkoB9Yw1TJa 92 | gpq7E50kfttwfKNkkAZIbib10HugkMoQJAs2EsGkje98druIl8IXmuvBIF6nZHuM 93 | lt3UIZjS9RwPPLXhRHt1P0mR7BoBcOjiHgtSEs7Wk+j7AgMBAAGjUzBRMB0GA1Ud 94 | DgQWBBQdheJv73XSOhgMQtkwdYPnfO02+TAfBgNVHSMEGDAWgBQdheJv73XSOhgM 95 | QtkwdYPnfO02+TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf 96 | SKVNMdmBpD9m53kCrguo9iKQqmhnI0WLkpdWszc/vBgtpOE5ENOfHGAufHZve871 97 | 2fzTXrgR0TF6UZWsQOqCm5Oh3URsCdXWewVMKgJ3DCii6QJ0MnhSFt6+xZE9C6Hi 98 | WhcywgdR8t/JXKDam6miohW8Rum/IZo5HK9Jz/R9icKDGumcqoaPj/ONvY4EUwgB 99 | irKKB7YgFogBmCtgi30beLVkXgk0GEcAf19lHHtX2Pv/lh3m34li1C9eBm1ca3kk 100 | M2tcQtm1G89NROEjcG92cg+GX3GiWIjbI0jD1wnVy2LCOXMgOVbKfGfVKISFt0b1 101 | DNn00G8C6ttLoGU2snyk 102 | -----END CERTIFICATE----- 103 | `) 104 | 105 | var rsaKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- 106 | MIIEogIBAAKCAQEAsvmayL8MImb+Gz5rysf+t+5bWS8H4Id6LohGk3IRriEJbbYZ 107 | KiDR8RQaqiYOOE1Bh1rYZa6upqELip1jYyLBHOsEg9XX+NzVgS4HcWzz9MeLQB/a 108 | SCVO00Km854wwYc0W1I1VwSHJ+2zZJ2Nvube/OKTxfR9b+4fBqk1qTR/ZXM/7/rJ 109 | 6KpvRwvqpbBS2uAN1/Zeb9ORdbqAX1AC5T+m1soxwH3LaYiZKAfWMNUyWoKauxOd 110 | JH7bcHyjZJAGSG4m9dB7oJDKECQLNhLBpI3vfHa7iJfCF5rrwSBep2R7jJbd1CGY 111 | 0vUcDzy14UR7dT9JkewaAXDo4h4LUhLO1pPo+wIDAQABAoIBAF6yWwekrlL1k7Xu 112 | jTI6J7hCUesaS1yt0iQUzuLtFBXCPS7jjuUPgIXCUWl9wUBhAC8SDjWe+6IGzAiH 113 | xjKKDQuz/iuTVjbDAeTb6exF7b6yZieDswdBVjfJqHR2Wu3LEBTRpo9oQesKhkTS 114 | aFF97rZ3XCD9f/FdWOU5Wr8wm8edFK0zGsZ2N6r57yf1N6ocKlGBLBZ0v1Sc5ShV 115 | 1PVAxeephQvwL5DrOgkArnuAzwRXwJQG78L0aldWY2q6xABQZQb5+ml7H/kyytef 116 | i+uGo3jHKepVALHmdpCGr9Yv+yCElup+ekv6cPy8qcmMBqGMISL1i1FEONxLcKWp 117 | GEJi6QECgYEA3ZPGMdUm3f2spdHn3C+/+xskQpz6efiPYpnqFys2TZD7j5OOnpcP 118 | ftNokA5oEgETg9ExJQ8aOCykseDc/abHerYyGw6SQxmDbyBLmkZmp9O3iMv2N8Pb 119 | Nrn9kQKSr6LXZ3gXzlrDvvRoYUlfWuLSxF4b4PYifkA5AfsdiKkj+5sCgYEAzseF 120 | XDTRKHHJnzxZDDdHQcwA0G9agsNj64BGUEjsAGmDiDyqOZnIjDLRt0O2X3oiIE5S 121 | TXySSEiIkxjfErVJMumLaIwqVvlS4pYKdQo1dkM7Jbt8wKRQdleRXOPPN7msoEUk 122 | Ta9ZsftHVUknPqblz9Uthb5h+sRaxIaE1llqDiECgYATS4oHzuL6k9uT+Qpyzymt 123 | qThoIJljQ7TgxjxvVhD9gjGV2CikQM1Vov1JBigj4Toc0XuxGXaUC7cv0kAMSpi2 124 | Y+VLG+K6ux8J70sGHTlVRgeGfxRq2MBfLKUbGplBeDG/zeJs0tSW7VullSkblgL6 125 | nKNa3LQ2QEt2k7KHswryHwKBgENDxk8bY1q7wTHKiNEffk+aFD25q4DUHMH0JWti 126 | fVsY98+upFU+gG2S7oOmREJE0aser0lDl7Zp2fu34IEOdfRY4p+s0O0gB+Vrl5VB 127 | L+j7r9bzaX6lNQN6MvA7ryHahZxRQaD/xLbQHgFRXbHUyvdTyo4yQ1821qwNclLk 128 | HUrhAoGAUtjR3nPFR4TEHlpTSQQovS8QtGTnOi7s7EzzdPWmjHPATrdLhMA0ezPj 129 | Mr+u5TRncZBIzAZtButlh1AHnpN/qO3P0c0Rbdep3XBc/82JWO8qdb5QvAkxga3X 130 | BpA7MNLxiqss+rCbwf3NbWxEMiDQ2zRwVoafVFys7tjmv6t2Xck= 131 | -----END RSA PRIVATE KEY----- 132 | `) 133 | 134 | func TestTLS(t *testing.T) { 135 | ctx, cancelFunc := context.WithCancel(context.Background()) 136 | waitGrp := sync.WaitGroup{} 137 | waitGrp.Add(1) 138 | go func() { 139 | server(ctx, cancelFunc, 10) 140 | waitGrp.Done() 141 | }() 142 | time.Sleep(1) 143 | log.Println("done sleep") 144 | client(ctx, 10) 145 | waitGrp.Done() 146 | } 147 | -------------------------------------------------------------------------------- /tls_bench/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "sync" 7 | "sync/atomic" 8 | "time" 9 | 10 | ltls "github.com/lesismal/llib/std/crypto/tls" 11 | "github.com/lesismal/nbio" 12 | "github.com/lesismal/nbio/extension/tls" 13 | ) 14 | 15 | var ( 16 | addr = "localhost:8888" 17 | 18 | configs = []*tls.Config{ 19 | // sth wrong with TLS 1.0 20 | // { 21 | // InsecureSkipVerify: true, 22 | // MaxVersion: ltls.VersionTLS10, 23 | // }, 24 | { 25 | InsecureSkipVerify: true, 26 | MaxVersion: ltls.VersionTLS11, 27 | }, 28 | { 29 | InsecureSkipVerify: true, 30 | MaxVersion: ltls.VersionTLS12, 31 | }, 32 | { 33 | InsecureSkipVerify: true, 34 | MaxVersion: ltls.VersionTLS13, 35 | }, 36 | // SSL is not supported 37 | // { 38 | // InsecureSkipVerify: true, 39 | // MaxVersion: ltls.VersionSSL30, 40 | // }, 41 | } 42 | ) 43 | 44 | // Session . 45 | type Session struct { 46 | Conn *tls.Conn 47 | Buffer []byte 48 | } 49 | 50 | // wrapData . 51 | func wrapData(h func(c *nbio.Conn, tlsConn *tls.Conn, data []byte)) func(c *nbio.Conn, data []byte) { 52 | return func(c *nbio.Conn, data []byte) { 53 | 54 | if isession := c.Session(); isession != nil { 55 | if session, ok := isession.(*Session); ok { 56 | session.Conn.Append(data) 57 | buffer := make([]byte, 2048) 58 | for { 59 | n, err := session.Conn.Read(buffer) 60 | if err != nil { 61 | c.Close() 62 | return 63 | } 64 | if h != nil && n > 0 { 65 | h(c, session.Conn, buffer[:n]) 66 | } 67 | if n < len(buffer) { 68 | return 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } 75 | 76 | func main() { 77 | var ( 78 | wg sync.WaitGroup 79 | qps int64 80 | bufsize = 64 //1024 * 8 81 | clientNum = 128 82 | totalRead int64 83 | totalWrite int64 84 | ) 85 | 86 | g := nbio.NewEngine(nbio.Config{}) 87 | g.OnData(wrapData(func(c *nbio.Conn, tlsConn *tls.Conn, data []byte) { 88 | session := c.Session().(*Session) 89 | session.Buffer = append(session.Buffer, data...) 90 | for len(session.Buffer) >= bufsize { 91 | buf := session.Buffer[:bufsize] 92 | session.Buffer = session.Buffer[bufsize:] 93 | tlsConn.Write(buf) 94 | atomic.AddInt64(&qps, 1) 95 | atomic.AddInt64(&totalRead, int64(bufsize)) 96 | atomic.AddInt64(&totalWrite, int64(bufsize)) 97 | } 98 | })) 99 | 100 | err := g.Start() 101 | if err != nil { 102 | fmt.Printf("Start failed: %v\n", err) 103 | } 104 | defer g.Stop() 105 | 106 | for i := 0; i < clientNum; i++ { 107 | wg.Add(1) 108 | 109 | tlsConfig := configs[i%len(configs)] 110 | go func() { 111 | data := make([]byte, bufsize) 112 | 113 | tlsConn, err := tls.Dial("tcp", addr, tlsConfig) 114 | if err != nil { 115 | log.Fatalf("Dial failed: %v\n", err) 116 | } 117 | 118 | nbConn, err := nbio.NBConn(tlsConn.Conn()) 119 | if err != nil { 120 | log.Fatalf("AddConn failed: %v\n", err) 121 | } 122 | 123 | nbConn.SetSession(&Session{ 124 | Conn: tlsConn, 125 | }) 126 | nonBlock := true 127 | tlsConn.ResetConn(nbConn, nonBlock) 128 | g.AddConn(nbConn) 129 | 130 | tlsConn.Write(data) 131 | atomic.AddInt64(&totalWrite, int64(len(data))) 132 | }() 133 | } 134 | 135 | go func() { 136 | for { 137 | time.Sleep(time.Second) 138 | fmt.Printf("nbio tls clients, qps: %v, total read: %.1f M, total write: %.1f M\n", atomic.SwapInt64(&qps, 0), float64(atomic.SwapInt64(&totalRead, 0))/1024/1024, float64(atomic.SwapInt64(&totalWrite, 0))/1024/1024) 139 | } 140 | }() 141 | 142 | wg.Wait() 143 | } 144 | -------------------------------------------------------------------------------- /tls_bench/client_std/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "crypto/tls" 7 | "fmt" 8 | "io" 9 | "log" 10 | "sync" 11 | "sync/atomic" 12 | "time" 13 | ) 14 | 15 | var ( 16 | addr = "localhost:8888" 17 | 18 | tlsConfig = &tls.Config{ 19 | InsecureSkipVerify: true, 20 | } 21 | ) 22 | 23 | func main() { 24 | var ( 25 | wg sync.WaitGroup 26 | qps int64 27 | bufsize = 64 //1024 * 8 28 | clientNum = 128 29 | totalRead int64 30 | totalWrite int64 31 | ) 32 | 33 | tlsConfigs := []*tls.Config{ 34 | { 35 | InsecureSkipVerify: true, 36 | MaxVersion: tls.VersionTLS10, 37 | }, 38 | { 39 | InsecureSkipVerify: true, 40 | MaxVersion: tls.VersionTLS11, 41 | }, 42 | { 43 | InsecureSkipVerify: true, 44 | MaxVersion: tls.VersionTLS12, 45 | }, 46 | { 47 | InsecureSkipVerify: true, 48 | MaxVersion: tls.VersionTLS13, 49 | }, 50 | // SSL is not supported 51 | // &{ 52 | // InsecureSkipVerify: true, 53 | // MaxVersion: tls.VersionSSL30, 54 | // }, 55 | } 56 | 57 | for i := 0; i < clientNum; i++ { 58 | wg.Add(1) 59 | 60 | tlsConfig := tlsConfigs[i%len(tlsConfigs)] 61 | go func() { 62 | wbuf := make([]byte, bufsize) 63 | rbuf := make([]byte, len(wbuf)) 64 | conn, err := tls.Dial("tcp", "localhost:8888", tlsConfig) 65 | if err != nil { 66 | panic(err) 67 | } 68 | defer conn.Close() 69 | 70 | for { 71 | rand.Read(wbuf) 72 | n1, err := conn.Write(wbuf) 73 | if err != nil || n1 != len(wbuf) { 74 | log.Fatalf("conn.Write failed: %v, %v", n1, err) 75 | } 76 | 77 | n2, err := io.ReadFull(conn, rbuf) 78 | if err != nil || n2 != n1 || !bytes.Equal(wbuf, rbuf) { 79 | log.Fatalf("conn.Read failed: %v", err) 80 | } 81 | atomic.AddInt64(&qps, 1) 82 | atomic.AddInt64(&totalRead, int64(len(rbuf))) 83 | atomic.AddInt64(&totalWrite, int64(len(wbuf))) 84 | } 85 | }() 86 | } 87 | 88 | go func() { 89 | for { 90 | time.Sleep(time.Second) 91 | fmt.Printf("std/tls clients, qps: %v, total read: %.1f M, total write: %.1f M\n", atomic.SwapInt64(&qps, 0), float64(atomic.SwapInt64(&totalRead, 0))/1024/1024, float64(atomic.SwapInt64(&totalWrite, 0))/1024/1024) 92 | } 93 | }() 94 | 95 | wg.Wait() 96 | } 97 | -------------------------------------------------------------------------------- /tls_bench/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/lesismal/llib/std/crypto/tls" 7 | "github.com/lesismal/nbio" 8 | ntls "github.com/lesismal/nbio/extension/tls" 9 | ) 10 | 11 | func main() { 12 | cert, err := tls.X509KeyPair(rsaCertPEM, rsaKeyPEM) 13 | if err != nil { 14 | log.Fatalf("tls.X509KeyPair failed: %v", err) 15 | } 16 | tlsConfig := &tls.Config{ 17 | Certificates: []tls.Certificate{cert}, 18 | InsecureSkipVerify: true, 19 | } 20 | 21 | g := nbio.NewEngine(nbio.Config{ 22 | Network: "tcp", 23 | Addrs: []string{"localhost:8888"}, 24 | }) 25 | isClient := false 26 | g.OnOpen(ntls.WrapOpen(tlsConfig, isClient, func(c *nbio.Conn, tlsConn *tls.Conn) { 27 | log.Println("OnOpen:", c.RemoteAddr().String()) 28 | })) 29 | g.OnClose(ntls.WrapClose(func(c *nbio.Conn, tlsConn *tls.Conn, err error) { 30 | log.Println("OnClose:", c.RemoteAddr().String()) 31 | })) 32 | g.OnData(ntls.WrapData(func(c *nbio.Conn, tlsConn *tls.Conn, data []byte) { 33 | tlsConn.Write(data) 34 | })) 35 | 36 | err = g.Start() 37 | if err != nil { 38 | log.Fatalf("nbio.Start failed: %v\n", err) 39 | return 40 | } 41 | defer g.Stop() 42 | 43 | g.Wait() 44 | } 45 | 46 | var rsaCertPEM = []byte(`-----BEGIN CERTIFICATE----- 47 | MIIDazCCAlOgAwIBAgIUJeohtgk8nnt8ofratXJg7kUJsI4wDQYJKoZIhvcNAQEL 48 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 49 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMDcwODIyNThaFw0zMDEy 50 | MDUwODIyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 51 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 52 | AQUAA4IBDwAwggEKAoIBAQCy+ZrIvwwiZv4bPmvKx/637ltZLwfgh3ouiEaTchGu 53 | IQltthkqINHxFBqqJg44TUGHWthlrq6moQuKnWNjIsEc6wSD1df43NWBLgdxbPP0 54 | x4tAH9pIJU7TQqbznjDBhzRbUjVXBIcn7bNknY2+5t784pPF9H1v7h8GqTWpNH9l 55 | cz/v+snoqm9HC+qlsFLa4A3X9l5v05F1uoBfUALlP6bWyjHAfctpiJkoB9Yw1TJa 56 | gpq7E50kfttwfKNkkAZIbib10HugkMoQJAs2EsGkje98druIl8IXmuvBIF6nZHuM 57 | lt3UIZjS9RwPPLXhRHt1P0mR7BoBcOjiHgtSEs7Wk+j7AgMBAAGjUzBRMB0GA1Ud 58 | DgQWBBQdheJv73XSOhgMQtkwdYPnfO02+TAfBgNVHSMEGDAWgBQdheJv73XSOhgM 59 | QtkwdYPnfO02+TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf 60 | SKVNMdmBpD9m53kCrguo9iKQqmhnI0WLkpdWszc/vBgtpOE5ENOfHGAufHZve871 61 | 2fzTXrgR0TF6UZWsQOqCm5Oh3URsCdXWewVMKgJ3DCii6QJ0MnhSFt6+xZE9C6Hi 62 | WhcywgdR8t/JXKDam6miohW8Rum/IZo5HK9Jz/R9icKDGumcqoaPj/ONvY4EUwgB 63 | irKKB7YgFogBmCtgi30beLVkXgk0GEcAf19lHHtX2Pv/lh3m34li1C9eBm1ca3kk 64 | M2tcQtm1G89NROEjcG92cg+GX3GiWIjbI0jD1wnVy2LCOXMgOVbKfGfVKISFt0b1 65 | DNn00G8C6ttLoGU2snyk 66 | -----END CERTIFICATE----- 67 | `) 68 | 69 | var rsaKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- 70 | MIIEogIBAAKCAQEAsvmayL8MImb+Gz5rysf+t+5bWS8H4Id6LohGk3IRriEJbbYZ 71 | KiDR8RQaqiYOOE1Bh1rYZa6upqELip1jYyLBHOsEg9XX+NzVgS4HcWzz9MeLQB/a 72 | SCVO00Km854wwYc0W1I1VwSHJ+2zZJ2Nvube/OKTxfR9b+4fBqk1qTR/ZXM/7/rJ 73 | 6KpvRwvqpbBS2uAN1/Zeb9ORdbqAX1AC5T+m1soxwH3LaYiZKAfWMNUyWoKauxOd 74 | JH7bcHyjZJAGSG4m9dB7oJDKECQLNhLBpI3vfHa7iJfCF5rrwSBep2R7jJbd1CGY 75 | 0vUcDzy14UR7dT9JkewaAXDo4h4LUhLO1pPo+wIDAQABAoIBAF6yWwekrlL1k7Xu 76 | jTI6J7hCUesaS1yt0iQUzuLtFBXCPS7jjuUPgIXCUWl9wUBhAC8SDjWe+6IGzAiH 77 | xjKKDQuz/iuTVjbDAeTb6exF7b6yZieDswdBVjfJqHR2Wu3LEBTRpo9oQesKhkTS 78 | aFF97rZ3XCD9f/FdWOU5Wr8wm8edFK0zGsZ2N6r57yf1N6ocKlGBLBZ0v1Sc5ShV 79 | 1PVAxeephQvwL5DrOgkArnuAzwRXwJQG78L0aldWY2q6xABQZQb5+ml7H/kyytef 80 | i+uGo3jHKepVALHmdpCGr9Yv+yCElup+ekv6cPy8qcmMBqGMISL1i1FEONxLcKWp 81 | GEJi6QECgYEA3ZPGMdUm3f2spdHn3C+/+xskQpz6efiPYpnqFys2TZD7j5OOnpcP 82 | ftNokA5oEgETg9ExJQ8aOCykseDc/abHerYyGw6SQxmDbyBLmkZmp9O3iMv2N8Pb 83 | Nrn9kQKSr6LXZ3gXzlrDvvRoYUlfWuLSxF4b4PYifkA5AfsdiKkj+5sCgYEAzseF 84 | XDTRKHHJnzxZDDdHQcwA0G9agsNj64BGUEjsAGmDiDyqOZnIjDLRt0O2X3oiIE5S 85 | TXySSEiIkxjfErVJMumLaIwqVvlS4pYKdQo1dkM7Jbt8wKRQdleRXOPPN7msoEUk 86 | Ta9ZsftHVUknPqblz9Uthb5h+sRaxIaE1llqDiECgYATS4oHzuL6k9uT+Qpyzymt 87 | qThoIJljQ7TgxjxvVhD9gjGV2CikQM1Vov1JBigj4Toc0XuxGXaUC7cv0kAMSpi2 88 | Y+VLG+K6ux8J70sGHTlVRgeGfxRq2MBfLKUbGplBeDG/zeJs0tSW7VullSkblgL6 89 | nKNa3LQ2QEt2k7KHswryHwKBgENDxk8bY1q7wTHKiNEffk+aFD25q4DUHMH0JWti 90 | fVsY98+upFU+gG2S7oOmREJE0aser0lDl7Zp2fu34IEOdfRY4p+s0O0gB+Vrl5VB 91 | L+j7r9bzaX6lNQN6MvA7ryHahZxRQaD/xLbQHgFRXbHUyvdTyo4yQ1821qwNclLk 92 | HUrhAoGAUtjR3nPFR4TEHlpTSQQovS8QtGTnOi7s7EzzdPWmjHPATrdLhMA0ezPj 93 | Mr+u5TRncZBIzAZtButlh1AHnpN/qO3P0c0Rbdep3XBc/82JWO8qdb5QvAkxga3X 94 | BpA7MNLxiqss+rCbwf3NbWxEMiDQ2zRwVoafVFys7tjmv6t2Xck= 95 | -----END RSA PRIVATE KEY----- 96 | `) 97 | -------------------------------------------------------------------------------- /tls_sticky_packet/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/tls" 6 | "encoding/hex" 7 | "io" 8 | "log" 9 | "sync" 10 | "sync/atomic" 11 | "time" 12 | ) 13 | 14 | func main() { 15 | wg := sync.WaitGroup{} 16 | 17 | count := int64(0) 18 | total := count 19 | connNum := 10 20 | loopTimes := 10 21 | 22 | configs := []*tls.Config{ 23 | { 24 | InsecureSkipVerify: true, 25 | MaxVersion: tls.VersionTLS10, 26 | }, 27 | { 28 | InsecureSkipVerify: true, 29 | MaxVersion: tls.VersionTLS11, 30 | }, 31 | { 32 | InsecureSkipVerify: true, 33 | MaxVersion: tls.VersionTLS12, 34 | }, 35 | { 36 | InsecureSkipVerify: true, 37 | MaxVersion: tls.VersionTLS13, 38 | }, 39 | // SSL is not supported 40 | // { 41 | // InsecureSkipVerify: true, 42 | // MaxVersion: tls.VersionSSL30, 43 | // }, 44 | } 45 | for i := 0; i < connNum; i++ { 46 | tlsConfig := configs[i%len(configs)] 47 | wg.Add(1) 48 | go func() { 49 | defer wg.Done() 50 | 51 | conn, err := tls.Dial("tcp", "localhost:8888", tlsConfig) 52 | if err != nil { 53 | panic(err) 54 | } 55 | defer conn.Close() 56 | 57 | randBuf := make([]byte, 64) 58 | rand.Read(randBuf) 59 | wbuf := []byte(hex.EncodeToString(randBuf)) 60 | // log.Println("wbuf:", string(wbuf)) 61 | for j := 0; j < loopTimes; j++ { 62 | n1, err := conn.Write(wbuf) 63 | if err != nil || n1 != len(wbuf) { 64 | log.Fatalf("conn.Write failed: %v, %v", n1, err) 65 | } 66 | 67 | rbuf := make([]byte, len(wbuf)) 68 | n2, err := io.ReadFull(conn, rbuf) 69 | if err != nil { 70 | log.Fatalf("conn.Read failed: %v", err) 71 | } 72 | if n2 != n1 || string(rbuf) != string(wbuf) { 73 | log.Fatalf("conn.Read failed: %v, %v", n2, string(wbuf)) 74 | } else { 75 | atomic.AddInt64(&count, 1) 76 | // log.Println("response:", string(rbuf)) 77 | } 78 | time.Sleep(time.Second / 10) 79 | } 80 | }() 81 | } 82 | 83 | ticker := time.NewTicker(time.Second) 84 | for { 85 | <-ticker.C 86 | curr := atomic.SwapInt64(&count, 0) 87 | total += curr 88 | log.Println("request count:", curr, "total:", total) 89 | if total >= int64(connNum*loopTimes) { 90 | break 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tls_sticky_packet/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | // "math/rand" 8 | "net" 9 | "net/http" 10 | _ "net/http/pprof" 11 | "time" 12 | 13 | "github.com/lesismal/llib/std/crypto/tls" 14 | "github.com/lesismal/nbio" 15 | ntls "github.com/lesismal/nbio/extension/tls" 16 | ) 17 | 18 | func init() { 19 | go http.ListenAndServe("localhost:6060", nil) 20 | } 21 | 22 | func main() { 23 | cert, err := tls.X509KeyPair(rsaCertPEM, rsaKeyPEM) 24 | if err != nil { 25 | log.Fatalf("tls.X509KeyPair failed: %v", err) 26 | } 27 | tlsConfig := &tls.Config{ 28 | Certificates: []tls.Certificate{cert}, 29 | InsecureSkipVerify: true, 30 | } 31 | 32 | g := nbio.NewEngine(nbio.Config{ 33 | Network: "tcp", 34 | Addrs: []string{"localhost:9999"}, 35 | }) 36 | 37 | g.OnOpen(ntls.WrapOpen(tlsConfig, false, func(c *nbio.Conn, tlsConn *tls.Conn) { 38 | log.Println("OnOpen:", c.RemoteAddr().String()) 39 | })) 40 | g.OnClose(ntls.WrapClose(func(c *nbio.Conn, tlsConn *tls.Conn, err error) { 41 | log.Println("OnClose:", c.RemoteAddr().String()) 42 | })) 43 | g.OnData(ntls.WrapData(func(c *nbio.Conn, tlsConn *tls.Conn, data []byte) { 44 | log.Printf("OnData: %v, data length: %v\n", c.RemoteAddr().String(), len(data)) 45 | tlsConn.Write(data) 46 | })) 47 | 48 | err = g.Start() 49 | if err != nil { 50 | log.Fatalf("nbio.Start failed: %v\n", err) 51 | return 52 | } 53 | defer g.Stop() 54 | 55 | go runProxy("localhost:8888", "localhost:9999") 56 | 57 | g.Wait() 58 | } 59 | 60 | func stickyTunnel(clientConn *net.TCPConn, serverAddr string) { 61 | serverConn, dialErr := net.Dial("tcp", serverAddr) 62 | log.Printf("+ stickyTunnel: [%v -> %v]\n", clientConn.LocalAddr().String(), serverAddr) 63 | if dialErr == nil { 64 | c2sCor := func() { 65 | defer func() { 66 | recover() 67 | }() 68 | 69 | var buf = make([]byte, 4096) 70 | for i := 0; true; i++ { 71 | nread, err := clientConn.Read(buf) 72 | if err != nil { 73 | clientConn.Close() 74 | serverConn.Close() 75 | break 76 | } 77 | tmp := buf[:nread] 78 | // for len(tmp) > 0 { 79 | // nSend := int(rand.Intn(len(tmp)) + 1) 80 | // sendBuf := tmp[:nSend] 81 | // _, err = serverConn.Write(sendBuf) 82 | // tmp = tmp[nSend:] 83 | // if err != nil { 84 | // clientConn.Close() 85 | // serverConn.Close() 86 | // return 87 | // } 88 | // time.Sleep(time.Second / 1000) 89 | // } 90 | for j := 0; j < len(tmp); j++ { 91 | _, err := serverConn.Write([]byte{tmp[j]}) 92 | if err != nil { 93 | clientConn.Close() 94 | serverConn.Close() 95 | return 96 | } 97 | time.Sleep(time.Second / 1000) 98 | } 99 | } 100 | } 101 | 102 | s2cCor := func() { 103 | defer func() { 104 | recover() 105 | }() 106 | 107 | var buf = make([]byte, 4096) 108 | 109 | for i := 0; true; i++ { 110 | nread, err := serverConn.Read(buf) 111 | if err != nil { 112 | clientConn.Close() 113 | serverConn.Close() 114 | break 115 | } 116 | 117 | tmp := buf[:nread] 118 | // for len(tmp) > 0 { 119 | // nSend := int(rand.Intn(len(tmp)) + 1) 120 | // sendBuf := tmp[:nSend] 121 | // _, err = clientConn.Write(sendBuf) 122 | // tmp = tmp[nSend:] 123 | // if err != nil { 124 | // clientConn.Close() 125 | // serverConn.Close() 126 | // return 127 | // } 128 | // time.Sleep(time.Second / 1000) 129 | // } 130 | for j := 0; j < len(tmp); j++ { 131 | _, err := clientConn.Write([]byte{tmp[j]}) 132 | if err != nil { 133 | clientConn.Close() 134 | serverConn.Close() 135 | return 136 | } 137 | time.Sleep(time.Second / 1000) 138 | } 139 | } 140 | } 141 | 142 | go c2sCor() 143 | go s2cCor() 144 | } else { 145 | clientConn.Close() 146 | } 147 | } 148 | 149 | func runProxy(agentAddr string, serverAddr string) { 150 | tcpAddr, err := net.ResolveTCPAddr("tcp4", agentAddr) 151 | if err != nil { 152 | fmt.Println("ResolveTCPAddr Error: ", err) 153 | return 154 | } 155 | 156 | listener, err2 := net.ListenTCP("tcp", tcpAddr) 157 | if err2 != nil { 158 | fmt.Println("ListenTCP Error: ", err2) 159 | return 160 | } 161 | 162 | defer listener.Close() 163 | 164 | fmt.Println(fmt.Sprintf("proxy running on: [%s -> %s]", agentAddr, serverAddr)) 165 | for { 166 | conn, err := listener.AcceptTCP() 167 | 168 | if err != nil { 169 | fmt.Println("AcceptTCP Error: ", err2) 170 | } else { 171 | go stickyTunnel(conn, serverAddr) 172 | } 173 | } 174 | } 175 | 176 | var rsaCertPEM = []byte(`-----BEGIN CERTIFICATE----- 177 | MIIDazCCAlOgAwIBAgIUJeohtgk8nnt8ofratXJg7kUJsI4wDQYJKoZIhvcNAQEL 178 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 179 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMDcwODIyNThaFw0zMDEy 180 | MDUwODIyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 181 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 182 | AQUAA4IBDwAwggEKAoIBAQCy+ZrIvwwiZv4bPmvKx/637ltZLwfgh3ouiEaTchGu 183 | IQltthkqINHxFBqqJg44TUGHWthlrq6moQuKnWNjIsEc6wSD1df43NWBLgdxbPP0 184 | x4tAH9pIJU7TQqbznjDBhzRbUjVXBIcn7bNknY2+5t784pPF9H1v7h8GqTWpNH9l 185 | cz/v+snoqm9HC+qlsFLa4A3X9l5v05F1uoBfUALlP6bWyjHAfctpiJkoB9Yw1TJa 186 | gpq7E50kfttwfKNkkAZIbib10HugkMoQJAs2EsGkje98druIl8IXmuvBIF6nZHuM 187 | lt3UIZjS9RwPPLXhRHt1P0mR7BoBcOjiHgtSEs7Wk+j7AgMBAAGjUzBRMB0GA1Ud 188 | DgQWBBQdheJv73XSOhgMQtkwdYPnfO02+TAfBgNVHSMEGDAWgBQdheJv73XSOhgM 189 | QtkwdYPnfO02+TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf 190 | SKVNMdmBpD9m53kCrguo9iKQqmhnI0WLkpdWszc/vBgtpOE5ENOfHGAufHZve871 191 | 2fzTXrgR0TF6UZWsQOqCm5Oh3URsCdXWewVMKgJ3DCii6QJ0MnhSFt6+xZE9C6Hi 192 | WhcywgdR8t/JXKDam6miohW8Rum/IZo5HK9Jz/R9icKDGumcqoaPj/ONvY4EUwgB 193 | irKKB7YgFogBmCtgi30beLVkXgk0GEcAf19lHHtX2Pv/lh3m34li1C9eBm1ca3kk 194 | M2tcQtm1G89NROEjcG92cg+GX3GiWIjbI0jD1wnVy2LCOXMgOVbKfGfVKISFt0b1 195 | DNn00G8C6ttLoGU2snyk 196 | -----END CERTIFICATE----- 197 | `) 198 | 199 | var rsaKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- 200 | MIIEogIBAAKCAQEAsvmayL8MImb+Gz5rysf+t+5bWS8H4Id6LohGk3IRriEJbbYZ 201 | KiDR8RQaqiYOOE1Bh1rYZa6upqELip1jYyLBHOsEg9XX+NzVgS4HcWzz9MeLQB/a 202 | SCVO00Km854wwYc0W1I1VwSHJ+2zZJ2Nvube/OKTxfR9b+4fBqk1qTR/ZXM/7/rJ 203 | 6KpvRwvqpbBS2uAN1/Zeb9ORdbqAX1AC5T+m1soxwH3LaYiZKAfWMNUyWoKauxOd 204 | JH7bcHyjZJAGSG4m9dB7oJDKECQLNhLBpI3vfHa7iJfCF5rrwSBep2R7jJbd1CGY 205 | 0vUcDzy14UR7dT9JkewaAXDo4h4LUhLO1pPo+wIDAQABAoIBAF6yWwekrlL1k7Xu 206 | jTI6J7hCUesaS1yt0iQUzuLtFBXCPS7jjuUPgIXCUWl9wUBhAC8SDjWe+6IGzAiH 207 | xjKKDQuz/iuTVjbDAeTb6exF7b6yZieDswdBVjfJqHR2Wu3LEBTRpo9oQesKhkTS 208 | aFF97rZ3XCD9f/FdWOU5Wr8wm8edFK0zGsZ2N6r57yf1N6ocKlGBLBZ0v1Sc5ShV 209 | 1PVAxeephQvwL5DrOgkArnuAzwRXwJQG78L0aldWY2q6xABQZQb5+ml7H/kyytef 210 | i+uGo3jHKepVALHmdpCGr9Yv+yCElup+ekv6cPy8qcmMBqGMISL1i1FEONxLcKWp 211 | GEJi6QECgYEA3ZPGMdUm3f2spdHn3C+/+xskQpz6efiPYpnqFys2TZD7j5OOnpcP 212 | ftNokA5oEgETg9ExJQ8aOCykseDc/abHerYyGw6SQxmDbyBLmkZmp9O3iMv2N8Pb 213 | Nrn9kQKSr6LXZ3gXzlrDvvRoYUlfWuLSxF4b4PYifkA5AfsdiKkj+5sCgYEAzseF 214 | XDTRKHHJnzxZDDdHQcwA0G9agsNj64BGUEjsAGmDiDyqOZnIjDLRt0O2X3oiIE5S 215 | TXySSEiIkxjfErVJMumLaIwqVvlS4pYKdQo1dkM7Jbt8wKRQdleRXOPPN7msoEUk 216 | Ta9ZsftHVUknPqblz9Uthb5h+sRaxIaE1llqDiECgYATS4oHzuL6k9uT+Qpyzymt 217 | qThoIJljQ7TgxjxvVhD9gjGV2CikQM1Vov1JBigj4Toc0XuxGXaUC7cv0kAMSpi2 218 | Y+VLG+K6ux8J70sGHTlVRgeGfxRq2MBfLKUbGplBeDG/zeJs0tSW7VullSkblgL6 219 | nKNa3LQ2QEt2k7KHswryHwKBgENDxk8bY1q7wTHKiNEffk+aFD25q4DUHMH0JWti 220 | fVsY98+upFU+gG2S7oOmREJE0aser0lDl7Zp2fu34IEOdfRY4p+s0O0gB+Vrl5VB 221 | L+j7r9bzaX6lNQN6MvA7ryHahZxRQaD/xLbQHgFRXbHUyvdTyo4yQ1821qwNclLk 222 | HUrhAoGAUtjR3nPFR4TEHlpTSQQovS8QtGTnOi7s7EzzdPWmjHPATrdLhMA0ezPj 223 | Mr+u5TRncZBIzAZtButlh1AHnpN/qO3P0c0Rbdep3XBc/82JWO8qdb5QvAkxga3X 224 | BpA7MNLxiqss+rCbwf3NbWxEMiDQ2zRwVoafVFys7tjmv6t2Xck= 225 | -----END RSA PRIVATE KEY----- 226 | `) 227 | -------------------------------------------------------------------------------- /traffic_forward_tcp/proxy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net" 10 | "sync" 11 | "time" 12 | 13 | "github.com/lesismal/nbio" 14 | ) 15 | 16 | var ( 17 | network = "tcp" 18 | proxyAddr = "127.0.0.1:8080" 19 | serverAddr = "127.0.0.1:18080" 20 | ) 21 | 22 | type Session struct { 23 | mux sync.Mutex 24 | Peer *nbio.Conn 25 | Cache []byte 26 | } 27 | 28 | func main() { 29 | go server() 30 | time.AfterFunc(time.Second, client) 31 | 32 | engine := nbio.NewEngine(nbio.Config{ 33 | Network: network, 34 | Addrs: []string{proxyAddr}, 35 | MaxWriteBufferSize: 6 * 1024 * 1024, 36 | }) 37 | 38 | engine.OnOpen(func(srcConn *nbio.Conn) { 39 | sess := &Session{} 40 | srcConn.SetSession(sess) 41 | // engine.DialAsync(network, dstAddr, func(dstConn *nbio.Conn, err error) { 42 | engine.DialAsyncTimeout(network, serverAddr, time.Second*3, func(dstConn *nbio.Conn, err error) { 43 | if err != nil { 44 | srcConn.Close() 45 | return 46 | } 47 | 48 | dstConn.SetSession(&Session{Peer: srcConn}) 49 | 50 | sess.mux.Lock() 51 | defer sess.mux.Unlock() 52 | sess.Peer = dstConn 53 | if len(sess.Cache) > 0 { 54 | sess.Peer.Write(sess.Cache) 55 | sess.Cache = nil 56 | } 57 | }) 58 | }) 59 | 60 | engine.OnData(func(c *nbio.Conn, data []byte) { 61 | log.Printf("[%v, %v -> %v] onData: %v", c.RemoteAddr().Network(), c.RemoteAddr().String(), c.LocalAddr().String(), len(data)) 62 | sess, _ := c.Session().(*Session) 63 | if sess == nil { 64 | sess = &Session{Cache: append([]byte{}, data...)} 65 | c.SetSession(sess) 66 | } 67 | sess.mux.Lock() 68 | defer sess.mux.Unlock() 69 | if sess.Peer == nil { 70 | sess.Cache = append(sess.Cache, data...) 71 | } else { 72 | sess.Peer.Write(data) 73 | } 74 | }) 75 | 76 | err := engine.Start() 77 | if err != nil { 78 | fmt.Printf("nbio.Start failed: %v\n", err) 79 | return 80 | } 81 | defer engine.Stop() 82 | 83 | <-make(chan int) 84 | } 85 | 86 | func server() { 87 | ln, err := net.Listen(network, serverAddr) 88 | if err != nil { 89 | panic(err) 90 | } 91 | for { 92 | c, err := ln.Accept() 93 | if err == nil { 94 | go func(conn net.Conn) { 95 | buf := make([]byte, 1024) 96 | for { 97 | n, err := conn.Read(buf) 98 | if err != nil { 99 | conn.Close() 100 | return 101 | } 102 | // echo 103 | conn.Write(buf[:n]) 104 | } 105 | }(c) 106 | } 107 | } 108 | } 109 | 110 | func client() { 111 | conn, err := net.Dial(network, proxyAddr) 112 | if err != nil { 113 | panic(err) 114 | } 115 | defer conn.Close() 116 | wbuf := make([]byte, 512) 117 | rbuf := make([]byte, 512) 118 | for i := 0; i < 3; i++ { 119 | rand.Read(wbuf) 120 | n, err := conn.Write(wbuf) 121 | if err != nil || n != len(wbuf) { 122 | conn.Close() 123 | return 124 | } 125 | n, err = io.ReadFull(conn, rbuf) 126 | if err != nil || n != len(wbuf) { 127 | conn.Close() 128 | return 129 | } 130 | if !bytes.Equal(wbuf, rbuf) { 131 | panic("not equal") 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /traffic_forward_udp/proxy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net" 10 | "sync" 11 | "time" 12 | 13 | "github.com/lesismal/nbio" 14 | ) 15 | 16 | var ( 17 | network = "udp" 18 | proxyAddr = "127.0.0.1:8080" 19 | serverAddr = "127.0.0.1:18080" 20 | ) 21 | 22 | type Session struct { 23 | mux sync.Mutex 24 | Peer *nbio.Conn 25 | Cache []byte 26 | } 27 | 28 | func main() { 29 | go server() 30 | time.AfterFunc(time.Second, client) 31 | 32 | engine := nbio.NewEngine(nbio.Config{ 33 | Network: network, 34 | Addrs: []string{proxyAddr}, 35 | MaxWriteBufferSize: 6 * 1024 * 1024, 36 | }) 37 | 38 | engine.OnOpen(func(srcConn *nbio.Conn) { 39 | sess := &Session{} 40 | srcConn.SetSession(sess) 41 | // engine.DialAsync(network, dstAddr, func(dstConn *nbio.Conn, err error) { 42 | engine.DialAsyncTimeout(network, serverAddr, time.Second*3, func(dstConn *nbio.Conn, err error) { 43 | if err != nil { 44 | srcConn.Close() 45 | return 46 | } 47 | 48 | dstConn.SetSession(&Session{Peer: srcConn}) 49 | 50 | sess.mux.Lock() 51 | defer sess.mux.Unlock() 52 | sess.Peer = dstConn 53 | if len(sess.Cache) > 0 { 54 | sess.Peer.Write(sess.Cache) 55 | sess.Cache = nil 56 | } 57 | }) 58 | }) 59 | 60 | engine.OnData(func(c *nbio.Conn, data []byte) { 61 | log.Printf("[%v, %v -> %v] onData: %v", c.RemoteAddr().Network(), c.RemoteAddr().String(), c.LocalAddr().String(), len(data)) 62 | sess, _ := c.Session().(*Session) 63 | if sess == nil { 64 | sess = &Session{Cache: append([]byte{}, data...)} 65 | c.SetSession(sess) 66 | } 67 | sess.mux.Lock() 68 | defer sess.mux.Unlock() 69 | if sess.Peer == nil { 70 | sess.Cache = append(sess.Cache, data...) 71 | } else { 72 | sess.Peer.Write(data) 73 | } 74 | }) 75 | 76 | err := engine.Start() 77 | if err != nil { 78 | fmt.Printf("nbio.Start failed: %v\n", err) 79 | return 80 | } 81 | defer engine.Stop() 82 | 83 | <-make(chan int) 84 | } 85 | 86 | func server() { 87 | addr, err := net.ResolveUDPAddr(network, serverAddr) 88 | if err != nil { 89 | panic(1) 90 | } 91 | conn, err := net.ListenUDP(network, addr) 92 | if err != nil { 93 | panic(err) 94 | } 95 | defer conn.Close() 96 | 97 | buf := make([]byte, 1024) 98 | for { 99 | packLen, remoteAddr, err := conn.ReadFromUDP(buf) 100 | if err == nil { 101 | // echo 102 | conn.WriteToUDP(buf[:packLen], remoteAddr) 103 | } 104 | } 105 | } 106 | 107 | func client() { 108 | addr, err := net.ResolveUDPAddr(network, proxyAddr) 109 | if err != nil { 110 | panic(err) 111 | } 112 | conn, err := net.DialUDP(network, nil, addr) 113 | if err != nil { 114 | panic(err) 115 | } 116 | defer conn.Close() 117 | wbuf := make([]byte, 512) 118 | rbuf := make([]byte, 512) 119 | for i := 0; i < 3; i++ { 120 | rand.Read(wbuf) 121 | n, err := conn.Write(wbuf) 122 | if err != nil || n != len(wbuf) { 123 | conn.Close() 124 | return 125 | } 126 | n, err = io.ReadFull(conn, rbuf) 127 | if err != nil || n != len(wbuf) { 128 | conn.Close() 129 | return 130 | } 131 | if !bytes.Equal(wbuf, rbuf) { 132 | panic("not equal") 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /udp/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "log" 7 | "net" 8 | "time" 9 | 10 | "github.com/lesismal/nbio" 11 | ) 12 | 13 | func main() { 14 | var ( 15 | addr = "127.0.0.1:8888" 16 | request = []byte("hello") 17 | ) 18 | 19 | g := nbio.NewEngine(nbio.Config{}) 20 | 21 | done := make(chan int) 22 | g.OnData(func(c *nbio.Conn, response []byte) { 23 | log.Println("onData:", string(response)) 24 | if !bytes.Equal(request, response) { 25 | log.Panic("not equal") 26 | } 27 | close(done) 28 | 29 | }) 30 | 31 | err := g.Start() 32 | if err != nil { 33 | log.Panic(err) 34 | } 35 | defer g.Stop() 36 | 37 | c, err := net.Dial("udp", addr) 38 | if err != nil { 39 | log.Panic(err) 40 | } 41 | nbc, err := g.AddConn(c) 42 | if err != nil { 43 | log.Panic(err) 44 | } 45 | 46 | nbc.Write(request) 47 | 48 | ctx, cancel := context.WithTimeout(context.Background(), time.Second) 49 | defer cancel() 50 | select { 51 | case <-ctx.Done(): 52 | log.Println("timeout") 53 | case <-done: 54 | log.Println("success") 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /udp/client_std/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "net" 8 | ) 9 | 10 | func main() { 11 | conn, err := net.DialUDP("udp", nil, &net.UDPAddr{ 12 | IP: net.IPv4(127, 0, 0, 1), 13 | Port: 8888, 14 | }) 15 | defer conn.Close() 16 | if err != nil { 17 | return 18 | } 19 | 20 | for i:=0; i<3; i++{ 21 | request := []byte(fmt.Sprintf("hello %d", i)) 22 | response := make([]byte, 1024) 23 | _, err = conn.Write(request) 24 | if packLen, remoteAddr, err := conn.ReadFromUDP(response); err == nil { 25 | log.Println("onData:", string(response[:packLen])) 26 | if !bytes.Equal(request, response[:packLen]) { 27 | log.Panic("not equal") 28 | } 29 | } else { 30 | log.Panic("recv msg failed:", remoteAddr, err) 31 | } 32 | } 33 | 34 | log.Println("success") 35 | } 36 | -------------------------------------------------------------------------------- /udp/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/lesismal/nbio" 9 | ) 10 | 11 | func main() { 12 | engine := nbio.NewEngine(nbio.Config{ 13 | Network: "udp", 14 | Addrs: []string{"127.0.0.1:8080"}, 15 | MaxWriteBufferSize: 6 * 1024 * 1024, 16 | UDPReadTimeout: time.Second, 17 | }) 18 | 19 | // For the same socket connection(same LocalAddr() and RemoteAddr()), 20 | // all the *nbio.Conn passed to users in OnOpen/OnData/OnClose handler is the same pointer 21 | engine.OnOpen(func(c *nbio.Conn) { 22 | log.Printf("onOpen: [%p, %v]", c, c.RemoteAddr().String()) 23 | }) 24 | engine.OnData(func(c *nbio.Conn, data []byte) { 25 | log.Printf("onData: [%p, %v], %v", c, c.RemoteAddr().String(), string(data)) 26 | c.Write(append([]byte{}, data...)) 27 | }) 28 | engine.OnClose(func(c *nbio.Conn, err error) { 29 | log.Printf("onClose: [%p, %v], %v", c, c.RemoteAddr().String(), err) 30 | }) 31 | 32 | err := engine.Start() 33 | if err != nil { 34 | fmt.Printf("nbio.Start failed: %v\n", err) 35 | return 36 | } 37 | defer engine.Stop() 38 | 39 | <-make(chan int) 40 | } 41 | -------------------------------------------------------------------------------- /udp/server_std/server.go: -------------------------------------------------------------------------------- 1 | //server.go 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "log" 7 | "net" 8 | "os" 9 | ) 10 | 11 | func main() { 12 | addrstr := fmt.Sprintf("127.0.0.1:8888") 13 | addr, err := net.ResolveUDPAddr("udp", addrstr) 14 | if err != nil { 15 | fmt.Println("ResolveUDPAddr error:", err) 16 | os.Exit(1) 17 | } 18 | conn, err := net.ListenUDP("udp", addr) 19 | if err != nil { 20 | fmt.Println("listen error:", err) 21 | os.Exit(1) 22 | } 23 | defer conn.Close() 24 | 25 | buf := make([]byte, 1024) 26 | for { 27 | if packLen, remoteAddr, err := conn.ReadFromUDP(buf); err == nil { 28 | log.Printf("onData: [%p, %v], %v", remoteAddr, remoteAddr.String(), string(buf[:packLen])) 29 | conn.WriteToUDP(buf[:packLen], remoteAddr) 30 | } else { 31 | log.Panic(err) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /websocket/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "net/url" 7 | 8 | "github.com/gorilla/websocket" 9 | ) 10 | 11 | func main() { 12 | flag.Parse() 13 | 14 | u := url.URL{Scheme: "ws", Host: "localhost:8080", Path: "/ws"} 15 | c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) 16 | if err != nil { 17 | panic(err) 18 | } 19 | defer c.Close() 20 | 21 | request := "hello" 22 | err = c.WriteMessage(websocket.BinaryMessage, []byte(request)) 23 | if err != nil { 24 | log.Fatalf("write: %v", err) 25 | return 26 | } 27 | 28 | receiveType, response, err := c.ReadMessage() 29 | if err != nil { 30 | log.Println("ReadMessage failed:", err) 31 | return 32 | } 33 | if receiveType != websocket.BinaryMessage { 34 | log.Println("received type != websocket.BinaryMessage") 35 | return 36 | 37 | } 38 | 39 | if string(response) != request { 40 | log.Printf("'%v' != '%v'", len(response), len(request)) 41 | return 42 | } 43 | 44 | log.Println("success echo websocket.BinaryMessage:", request) 45 | } 46 | -------------------------------------------------------------------------------- /websocket/data_uploader/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net/url" 9 | "time" 10 | 11 | "github.com/gorilla/websocket" 12 | ) 13 | 14 | var compression = flag.Bool("compression", false, "allow compression") 15 | var filename1 = flag.String("file1", "", "file to upload") 16 | var filename2 = flag.String("file2", "", "file to upload") 17 | 18 | func main() { 19 | flag.Parse() 20 | 21 | u := url.URL{Scheme: "ws", Host: "localhost:8888", Path: "/ws"} 22 | dialer := websocket.DefaultDialer 23 | if *compression { 24 | dialer.EnableCompression = true 25 | } 26 | 27 | c, _, err := dialer.Dial(u.String(), nil) 28 | if err != nil { 29 | log.Fatal("dial:", err) 30 | } 31 | defer c.Close() 32 | 33 | i := 0 34 | data1, err := ioutil.ReadFile(*filename1) 35 | if err != nil { 36 | panic(fmt.Sprintf("failed to read file1: %v", err)) 37 | } 38 | data2, err := ioutil.ReadFile(*filename2) 39 | if err != nil { 40 | panic(fmt.Sprintf("failed to read file2: %v", err)) 41 | } 42 | for { 43 | i++ 44 | err := c.WriteMessage(websocket.BinaryMessage, data1) 45 | if err != nil { 46 | log.Println("ReadMessage failed:", err) 47 | return 48 | } 49 | err = c.WriteMessage(websocket.BinaryMessage, data2) 50 | if err != nil { 51 | log.Println("ReadMessage failed:", err) 52 | return 53 | } 54 | time.Sleep(time.Second) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /websocket/data_uploader/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net/http" 10 | "os" 11 | "os/signal" 12 | "time" 13 | 14 | "github.com/lesismal/nbio/nbhttp" 15 | "github.com/lesismal/nbio/nbhttp/websocket" 16 | ) 17 | 18 | var compression = flag.Bool("compression", false, "allow compression") 19 | 20 | func newUpgrader() *websocket.Upgrader { 21 | u := websocket.NewUpgrader() 22 | if *compression { 23 | u.EnableCompression(true) 24 | } 25 | var output io.WriteCloser 26 | id := 0 27 | u.OnDataFrame(func(c *websocket.Conn, messageType websocket.MessageType, fin bool, data []byte) { 28 | if output == nil { 29 | var err error 30 | deflateStr := "" 31 | // this no longer works 32 | // if u.CompressionEnabled() { 33 | // deflateStr = ".df" 34 | // } 35 | filename := fmt.Sprintf("data_%d.%d%s", id, time.Now().UnixNano(), deflateStr) 36 | id++ 37 | output, err = os.Create(filename) 38 | if err != nil { 39 | log.Println("failed to create file: ", filename, err) 40 | c.Close() 41 | return 42 | } 43 | } 44 | n, err := output.Write(data) 45 | if err != nil { 46 | log.Println("error writing to file: ", err) 47 | c.Close() 48 | return 49 | } 50 | if n != len(data) { 51 | log.Println("failed to write all frame data to file") 52 | c.Close() 53 | return 54 | } 55 | if fin { 56 | output.Close() 57 | output = nil 58 | } 59 | }) 60 | 61 | u.OnClose(func(c *websocket.Conn, err error) { 62 | fmt.Println("OnClose:", c.RemoteAddr().String(), err) 63 | if output != nil { 64 | output.Close() 65 | } 66 | }) 67 | return u 68 | } 69 | 70 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 71 | // time.Sleep(time.Second * 5) 72 | upgrader := newUpgrader() 73 | conn, err := upgrader.Upgrade(w, r, nil) 74 | if err != nil { 75 | panic(err) 76 | } 77 | conn.SetReadDeadline(time.Time{}) 78 | fmt.Println("OnOpen:", conn.RemoteAddr().String()) 79 | } 80 | 81 | func main() { 82 | flag.Parse() 83 | mux := &http.ServeMux{} 84 | mux.HandleFunc("/ws", onWebsocket) 85 | 86 | svr := nbhttp.NewEngine(nbhttp.Config{ 87 | Network: "tcp", 88 | Addrs: []string{"localhost:8888"}, 89 | Handler: mux, 90 | }) 91 | 92 | err := svr.Start() 93 | if err != nil { 94 | fmt.Printf("nbio.Start failed: %v\n", err) 95 | return 96 | } 97 | 98 | interrupt := make(chan os.Signal, 1) 99 | signal.Notify(interrupt, os.Interrupt) 100 | <-interrupt 101 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 102 | defer cancel() 103 | svr.Shutdown(ctx) 104 | } 105 | -------------------------------------------------------------------------------- /websocket/nbioclient/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net/url" 10 | "os" 11 | "os/signal" 12 | "time" 13 | 14 | "github.com/lesismal/nbio/nbhttp" 15 | "github.com/lesismal/nbio/nbhttp/websocket" 16 | ) 17 | 18 | var ( 19 | clients = flag.Int("clients", 1, "number of clients") 20 | ) 21 | 22 | func newUpgrader() *websocket.Upgrader { 23 | u := websocket.NewUpgrader() 24 | u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 25 | // echo 26 | time.AfterFunc(time.Second, func() { 27 | c.WriteMessage(messageType, data) 28 | }) 29 | log.Println("onEcho:", string(data)) 30 | }) 31 | 32 | u.OnClose(func(c *websocket.Conn, err error) { 33 | fmt.Println("OnClose:", c.RemoteAddr().String(), err) 34 | }) 35 | 36 | return u 37 | } 38 | 39 | func main() { 40 | flag.Parse() 41 | engine := nbhttp.NewEngine(nbhttp.Config{}) 42 | err := engine.Start() 43 | if err != nil { 44 | fmt.Printf("nbio.Start failed: %v\n", err) 45 | return 46 | } 47 | 48 | for i := 0; i < *clients; i++ { 49 | go func() { 50 | u := url.URL{Scheme: "ws", Host: "localhost:8888", Path: "/ws"} 51 | dialer := &websocket.Dialer{ 52 | Engine: engine, 53 | Upgrader: newUpgrader(), 54 | DialTimeout: time.Second * 3, 55 | } 56 | c, res, err := dialer.Dial(u.String(), nil) 57 | if err != nil { 58 | if res != nil && res.Body != nil { 59 | bReason, _ := io.ReadAll(res.Body) 60 | fmt.Printf("dial failed: %v, reason: %v\n", err, string(bReason)) 61 | } else { 62 | fmt.Printf("dial failed: %v\n", err) 63 | } 64 | return 65 | } 66 | c.WriteMessage(websocket.TextMessage, []byte("hello")) 67 | }() 68 | } 69 | 70 | interrupt := make(chan os.Signal, 1) 71 | signal.Notify(interrupt, os.Interrupt) 72 | <-interrupt 73 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 74 | defer cancel() 75 | engine.Shutdown(ctx) 76 | } 77 | -------------------------------------------------------------------------------- /websocket/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "os" 8 | "os/signal" 9 | "time" 10 | 11 | "github.com/lesismal/nbio/nbhttp" 12 | "github.com/lesismal/nbio/nbhttp/websocket" 13 | ) 14 | 15 | var ( 16 | upgrader = newUpgrader() 17 | ) 18 | 19 | func newUpgrader() *websocket.Upgrader { 20 | u := websocket.NewUpgrader() 21 | u.OnOpen(func(c *websocket.Conn) { 22 | // echo 23 | fmt.Println("OnOpen:", c.RemoteAddr().String()) 24 | }) 25 | u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 26 | // echo 27 | fmt.Println("OnMessage:", messageType, string(data)) 28 | c.WriteMessage(messageType, data) 29 | }) 30 | u.OnClose(func(c *websocket.Conn, err error) { 31 | fmt.Println("OnClose:", c.RemoteAddr().String(), err) 32 | }) 33 | return u 34 | } 35 | 36 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 37 | conn, err := upgrader.Upgrade(w, r, nil) 38 | if err != nil { 39 | panic(err) 40 | } 41 | fmt.Println("Upgraded:", conn.RemoteAddr().String()) 42 | } 43 | 44 | func main() { 45 | mux := &http.ServeMux{} 46 | mux.HandleFunc("/ws", onWebsocket) 47 | engine := nbhttp.NewEngine(nbhttp.Config{ 48 | Network: "tcp", 49 | Addrs: []string{"localhost:8080"}, 50 | MaxLoad: 1000000, 51 | ReleaseWebsocketPayload: true, 52 | Handler: mux, 53 | }) 54 | 55 | err := engine.Start() 56 | if err != nil { 57 | fmt.Printf("nbio.Start failed: %v\n", err) 58 | return 59 | } 60 | 61 | interrupt := make(chan os.Signal, 1) 62 | signal.Notify(interrupt, os.Interrupt) 63 | <-interrupt 64 | 65 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 66 | defer cancel() 67 | engine.Shutdown(ctx) 68 | } 69 | -------------------------------------------------------------------------------- /websocket/server_manual_frame_assembly/README.md: -------------------------------------------------------------------------------- 1 | if your clients will be sending large websocket messages of fixed size, you probably want to take an approach similar to this example. The problem with nbio and large messages is that as the frames are appended to the message buffer, the message buffers is reallocated (internally by the the golang append message). By using a fixed size buffer pool, you can control the capacity of the message buffer at the time of the first frame and limit how many in flight messages the clients are allowed to send in concurently 2 | -------------------------------------------------------------------------------- /websocket/server_manual_frame_assembly/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "time" 11 | 12 | "github.com/lesismal/nbio-examples/fixedbufferpool" 13 | "github.com/lesismal/nbio/nbhttp" 14 | "github.com/lesismal/nbio/nbhttp/websocket" 15 | ) 16 | 17 | var ( 18 | bufferPool = fixedbufferpool.NewFixedBufferPool(2000, 1*1024*1024, time.Second*10) 19 | ) 20 | 21 | func newUpgrader() *websocket.Upgrader { 22 | u := websocket.NewUpgrader() 23 | 24 | onMessageFunc := func(c *websocket.Conn, data []byte) { 25 | c.WriteMessage(websocket.BinaryMessage, data) 26 | } 27 | u.OnDataFrame(func(c *websocket.Conn, messageType websocket.MessageType, fin bool, frameData []byte) { 28 | curBuf := c.Session() 29 | // frame == message 30 | if fin && curBuf == nil { 31 | onMessageFunc(c, frameData) 32 | return 33 | } 34 | if curBuf == nil { 35 | b, err := bufferPool.Get() 36 | if err != nil { 37 | c.WriteMessage(websocket.CloseMessage, []byte(fmt.Sprintf("%v", err))) 38 | return 39 | } 40 | curBuf = b 41 | } 42 | messageDataSoFar := curBuf.([]byte) 43 | if cap(messageDataSoFar) < len(messageDataSoFar)+len(frameData) { 44 | c.WriteMessage(websocket.CloseMessage, []byte("websocket message too large")) 45 | return 46 | } 47 | messageDataSoFar = append(messageDataSoFar, frameData...) 48 | if fin { 49 | onMessageFunc(c, messageDataSoFar) 50 | bufferPool.Put(messageDataSoFar) 51 | c.SetSession(nil) 52 | } else { 53 | c.SetSession(messageDataSoFar) 54 | } 55 | }) 56 | 57 | u.OnClose(func(c *websocket.Conn, err error) { 58 | curBuf := c.Session() 59 | b := curBuf.([]byte) 60 | bufferPool.Put(b) 61 | }) 62 | return u 63 | } 64 | 65 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 66 | // time.Sleep(time.Second * 5) 67 | upgrader := newUpgrader() 68 | conn, err := upgrader.Upgrade(w, r, nil) 69 | if err != nil { 70 | panic(err) 71 | } 72 | conn.SetReadDeadline(time.Time{}) 73 | fmt.Println("OnOpen:", conn.RemoteAddr().String()) 74 | } 75 | 76 | func main() { 77 | flag.Parse() 78 | mux := &http.ServeMux{} 79 | mux.HandleFunc("/ws", onWebsocket) 80 | 81 | svr := nbhttp.NewEngine(nbhttp.Config{ 82 | Network: "tcp", 83 | Addrs: []string{"localhost:8888"}, 84 | ReleaseWebsocketPayload: true, 85 | Handler: mux, 86 | }) 87 | 88 | err := svr.Start() 89 | if err != nil { 90 | fmt.Printf("nbio.Start failed: %v\n", err) 91 | return 92 | } 93 | 94 | interrupt := make(chan os.Signal, 1) 95 | signal.Notify(interrupt, os.Interrupt) 96 | <-interrupt 97 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 98 | defer cancel() 99 | svr.Shutdown(ctx) 100 | } 101 | -------------------------------------------------------------------------------- /websocket/server_sticky/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "time" 11 | 12 | "github.com/lesismal/nbio/nbhttp" 13 | "github.com/lesismal/nbio/nbhttp/websocket" 14 | ) 15 | 16 | var ( 17 | engine *nbhttp.Engine 18 | ) 19 | 20 | func newUpgrader() *websocket.Upgrader { 21 | u := websocket.NewUpgrader() 22 | u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 23 | // echo 24 | c.WriteMessage(messageType, data) 25 | fmt.Println("OnMessage:", messageType, string(data)) 26 | }) 27 | u.OnClose(func(c *websocket.Conn, err error) { 28 | fmt.Println("OnClose:", c.RemoteAddr().String(), err) 29 | }) 30 | 31 | return u 32 | } 33 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 34 | upgrader := newUpgrader() 35 | conn, err := upgrader.Upgrade(w, r, nil) 36 | if err != nil { 37 | panic(err) 38 | } 39 | fmt.Println("OnOpen:", conn.RemoteAddr().String()) 40 | } 41 | 42 | func main() { 43 | mux := &http.ServeMux{} 44 | mux.HandleFunc("/ws", onWebsocket) 45 | 46 | engine = nbhttp.NewEngine(nbhttp.Config{ 47 | Network: "tcp", 48 | Addrs: []string{"localhost:28001"}, 49 | Handler: mux, 50 | }) 51 | 52 | err := engine.Start() 53 | if err != nil { 54 | fmt.Printf("nbio.Start failed: %v\n", err) 55 | return 56 | } 57 | defer engine.Stop() 58 | 59 | go runProxy("localhost:28000", "localhost:28001") 60 | 61 | interrupt := make(chan os.Signal, 1) 62 | signal.Notify(interrupt, os.Interrupt) 63 | <-interrupt 64 | log.Println("exit") 65 | } 66 | 67 | func fuzzTunnel(clientConn *net.TCPConn, serverAddr string) { 68 | serverConn, dialErr := net.Dial("tcp", serverAddr) 69 | log.Printf("+ fuzzTunnel: [%v -> %v]\n", clientConn.LocalAddr().String(), serverAddr) 70 | if dialErr == nil { 71 | c2sCor := func() { 72 | defer func() { 73 | recover() 74 | }() 75 | 76 | var buf = make([]byte, 4096) 77 | for i := 0; true; i++ { 78 | nread, err := clientConn.Read(buf) 79 | if err != nil { 80 | clientConn.Close() 81 | serverConn.Close() 82 | break 83 | } 84 | tmp := buf[:nread] 85 | // for len(tmp) > 0 { 86 | // nSend := int(rand.Intn(len(tmp)) + 1) 87 | // sendBuf := tmp[:nSend] 88 | // _, err = serverConn.Write(sendBuf) 89 | // tmp = tmp[nSend:] 90 | // if err != nil { 91 | // clientConn.Close() 92 | // serverConn.Close() 93 | // return 94 | // } 95 | // time.Sleep(time.Second / 1000) 96 | // } 97 | for j := 0; j < len(tmp); j++ { 98 | _, err := serverConn.Write([]byte{tmp[j]}) 99 | if err != nil { 100 | clientConn.Close() 101 | serverConn.Close() 102 | return 103 | } 104 | time.Sleep(time.Second / 1000) 105 | } 106 | } 107 | } 108 | 109 | s2cCor := func() { 110 | defer func() { 111 | recover() 112 | }() 113 | 114 | var buf = make([]byte, 4096) 115 | 116 | for i := 0; true; i++ { 117 | nread, err := serverConn.Read(buf) 118 | if err != nil { 119 | clientConn.Close() 120 | serverConn.Close() 121 | break 122 | } 123 | 124 | tmp := buf[:nread] 125 | // for len(tmp) > 0 { 126 | // nSend := int(rand.Intn(len(tmp)) + 1) 127 | // sendBuf := tmp[:nSend] 128 | // _, err = clientConn.Write(sendBuf) 129 | // tmp = tmp[nSend:] 130 | // if err != nil { 131 | // clientConn.Close() 132 | // serverConn.Close() 133 | // return 134 | // } 135 | // time.Sleep(time.Second / 1000) 136 | // } 137 | for j := 0; j < len(tmp); j++ { 138 | _, err := clientConn.Write([]byte{tmp[j]}) 139 | if err != nil { 140 | clientConn.Close() 141 | serverConn.Close() 142 | return 143 | } 144 | time.Sleep(time.Second / 1000) 145 | } 146 | } 147 | } 148 | 149 | go c2sCor() 150 | go s2cCor() 151 | } else { 152 | clientConn.Close() 153 | } 154 | } 155 | 156 | func runProxy(agentAddr string, serverAddr string) { 157 | tcpAddr, err := net.ResolveTCPAddr("tcp4", agentAddr) 158 | if err != nil { 159 | fmt.Println("ResolveTCPAddr Error: ", err) 160 | return 161 | } 162 | 163 | listener, err2 := net.ListenTCP("tcp", tcpAddr) 164 | if err2 != nil { 165 | fmt.Println("ListenTCP Error: ", err2) 166 | return 167 | } 168 | 169 | defer listener.Close() 170 | 171 | fmt.Println(fmt.Sprintf("proxy running on: [%s -> %s]", agentAddr, serverAddr)) 172 | for { 173 | conn, err := listener.AcceptTCP() 174 | 175 | if err != nil { 176 | fmt.Println("AcceptTCP Error: ", err2) 177 | } else { 178 | go fuzzTunnel(conn, serverAddr) 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /websocket_1m/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "flag" 7 | "fmt" 8 | "net/url" 9 | "runtime" 10 | "sync/atomic" 11 | "time" 12 | 13 | "github.com/gorilla/websocket" 14 | ) 15 | 16 | var ( 17 | running = true 18 | counting = false 19 | bodySize int = 1024 20 | connected uint64 = 0 21 | success uint64 = 0 22 | failed uint64 = 0 23 | totalSuccess uint64 = 0 24 | totalFailed uint64 = 0 25 | 26 | chConns chan *websocket.Conn 27 | numClient = flag.Int("c", 200000, "client num") 28 | numGoroutine = flag.Int("g", 1000, "goroutine num") 29 | ) 30 | 31 | func main() { 32 | flag.Parse() 33 | 34 | connNum := *numClient 35 | goroutineNum := *numGoroutine 36 | 37 | chConns = make(chan *websocket.Conn, connNum) 38 | 39 | for i := 0; i < goroutineNum; i++ { 40 | go loop(addrs[i%len(addrs)], connNum/goroutineNum) 41 | } 42 | 43 | for i := 0; i < connNum; i++ { 44 | c := <-chConns 45 | defer c.Close() 46 | } 47 | 48 | time.Sleep(time.Second * 3) 49 | counting = true 50 | 51 | ticker := time.NewTicker(time.Second) 52 | for i := 1; i <= 15; i++ { 53 | <-ticker.C 54 | nSuccess := atomic.SwapUint64(&success, 0) 55 | nFailed := atomic.SwapUint64(&failed, 0) 56 | totalSuccess += nSuccess 57 | totalFailed += nFailed 58 | fmt.Printf("running for %v seconds, online: %v, NumGoroutine: %v, success: %v, totalSuccess: %v, failed: %v, totalFailed: %v\n", i, connected, runtime.NumGoroutine(), nSuccess, totalSuccess, nFailed, totalFailed) 59 | } 60 | 61 | running = false 62 | time.Sleep(time.Second) 63 | } 64 | 65 | func loop(addr string, connNum int) { 66 | u := url.URL{Scheme: "ws", Host: addr, Path: "/ws"} 67 | addr = u.String() 68 | conns := make([]*websocket.Conn, connNum) 69 | for i := 0; i < connNum; i++ { 70 | for { 71 | conn, _, err := websocket.DefaultDialer.Dial(addr, nil) 72 | if err == nil { 73 | conns[i] = conn 74 | chConns <- conn 75 | atomic.AddUint64(&connected, 1) 76 | break 77 | } 78 | time.Sleep(time.Second / 10) 79 | } 80 | } 81 | buf := make([]byte, bodySize) 82 | for running { 83 | for i := 0; i < connNum; i++ { 84 | rand.Read(buf) 85 | echo(conns[i], buf) 86 | // return 87 | } 88 | } 89 | } 90 | 91 | func echo(c *websocket.Conn, buf []byte) { 92 | err := c.WriteMessage(websocket.BinaryMessage, buf) 93 | if err != nil { 94 | atomic.AddUint64(&failed, 1) 95 | return 96 | } 97 | 98 | _, message, err := c.ReadMessage() 99 | if err != nil { 100 | atomic.AddUint64(&failed, 1) 101 | return 102 | } 103 | 104 | if counting { 105 | if !bytes.Equal(message, buf) { 106 | atomic.AddUint64(&failed, 1) 107 | } else { 108 | atomic.AddUint64(&success, 1) 109 | } 110 | } 111 | } 112 | 113 | var addrs = []string{ 114 | "localhost:28001", 115 | "localhost:28002", 116 | "localhost:28003", 117 | "localhost:28004", 118 | "localhost:28005", 119 | "localhost:28006", 120 | "localhost:28007", 121 | "localhost:28008", 122 | "localhost:28009", 123 | "localhost:28010", 124 | 125 | "localhost:28011", 126 | "localhost:28012", 127 | "localhost:28013", 128 | "localhost:28014", 129 | "localhost:28015", 130 | "localhost:28016", 131 | "localhost:28017", 132 | "localhost:28018", 133 | "localhost:28019", 134 | "localhost:28020", 135 | 136 | "localhost:28021", 137 | "localhost:28022", 138 | "localhost:28023", 139 | "localhost:28024", 140 | "localhost:28025", 141 | "localhost:28026", 142 | "localhost:28027", 143 | "localhost:28028", 144 | "localhost:28029", 145 | "localhost:28030", 146 | 147 | "localhost:28031", 148 | "localhost:28032", 149 | "localhost:28033", 150 | "localhost:28034", 151 | "localhost:28035", 152 | "localhost:28036", 153 | "localhost:28037", 154 | "localhost:28038", 155 | "localhost:28039", 156 | "localhost:28040", 157 | 158 | "localhost:28041", 159 | "localhost:28042", 160 | "localhost:28043", 161 | "localhost:28044", 162 | "localhost:28045", 163 | "localhost:28046", 164 | "localhost:28047", 165 | "localhost:28048", 166 | "localhost:28049", 167 | "localhost:28050", 168 | } 169 | -------------------------------------------------------------------------------- /websocket_1m/nbioclient/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "net/url" 7 | "os" 8 | "os/signal" 9 | "runtime" 10 | "sync" 11 | "sync/atomic" 12 | "time" 13 | 14 | "github.com/lesismal/llib/std/crypto/tls" 15 | "github.com/lesismal/nbio/nbhttp" 16 | "github.com/lesismal/nbio/nbhttp/websocket" 17 | "github.com/lesismal/nbio/taskpool" 18 | ) 19 | 20 | var ( 21 | connected uint64 = 0 22 | success uint64 = 0 23 | failed uint64 = 0 24 | totalSuccess uint64 = 0 25 | totalFailed uint64 = 0 26 | 27 | sleepTime = flag.Int("s", 1, "sleep time for each loop in a goroutine") 28 | numClient = flag.Int("c", 50000, "client num") 29 | 30 | text = "hello world" 31 | ) 32 | 33 | func newUpgrader() *websocket.Upgrader { 34 | u := websocket.NewUpgrader() 35 | u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 36 | // echo 37 | if *sleepTime > 0 { 38 | time.AfterFunc(time.Second*time.Duration(*sleepTime), func() { 39 | err := c.WriteMessage(messageType, data) 40 | if err != nil { 41 | fmt.Println("WriteMessage failed 111:", err) 42 | atomic.AddUint64(&failed, 1) 43 | panic(err) 44 | } 45 | }) 46 | } else { 47 | err := c.WriteMessage(messageType, data) 48 | if err != nil { 49 | fmt.Println("WriteMessage failed 111:", err) 50 | atomic.AddUint64(&failed, 1) 51 | panic(err) 52 | } 53 | } 54 | if string(data) != text { 55 | atomic.AddUint64(&failed, 1) 56 | panic(fmt.Errorf("not equal: %v != %v", string(data), text)) 57 | } else { 58 | atomic.AddUint64(&success, 1) 59 | } 60 | }) 61 | 62 | u.OnClose(func(c *websocket.Conn, err error) { 63 | fmt.Println("OnClose:", c.RemoteAddr().String(), err) 64 | }) 65 | 66 | return u 67 | } 68 | 69 | func main() { 70 | flag.Parse() 71 | 72 | engine := nbhttp.NewEngine(nbhttp.Config{}) 73 | err := engine.Start() 74 | if err != nil { 75 | fmt.Printf("nbio.Start failed: %v\n", err) 76 | return 77 | } 78 | 79 | connNum := *numClient 80 | wg := sync.WaitGroup{} 81 | conns := make([]*websocket.Conn, connNum) 82 | pool := taskpool.New(8, 1024) 83 | 84 | go func() { 85 | for i := 0; i < connNum; i++ { 86 | addr := addrs[i%len(addrs)] 87 | u := url.URL{Scheme: "ws", Host: addr, Path: "/ws"} 88 | tlsConfig := &tls.Config{ 89 | InsecureSkipVerify: true, 90 | } 91 | dialer := &websocket.Dialer{ 92 | Engine: engine, 93 | Upgrader: newUpgrader(), 94 | DialTimeout: time.Second * 3, 95 | TLSClientConfig: tlsConfig, 96 | } 97 | idx := i 98 | wg.Add(1) 99 | pool.Go(func() { 100 | defer wg.Done() 101 | for { 102 | conn, _, err := dialer.Dial(u.String(), nil) 103 | if err == nil { 104 | conns[idx] = conn 105 | atomic.AddUint64(&connected, 1) 106 | break 107 | } 108 | time.Sleep(time.Second / 10) 109 | } 110 | }) 111 | } 112 | 113 | wg.Wait() 114 | 115 | for i := 0; i < connNum; i++ { 116 | c := conns[i] 117 | text := "hello world" 118 | err := c.WriteMessage(websocket.TextMessage, []byte(text)) 119 | if err != nil { 120 | fmt.Println("WriteMessage failed 111:", err) 121 | atomic.AddUint64(&failed, 1) 122 | panic(err) 123 | } 124 | } 125 | }() 126 | 127 | go func() { 128 | ticker := time.NewTicker(time.Second) 129 | for i := 1; true; i++ { 130 | <-ticker.C 131 | nSuccess := atomic.SwapUint64(&success, 0) 132 | nFailed := atomic.SwapUint64(&failed, 0) 133 | totalSuccess += nSuccess 134 | totalFailed += nFailed 135 | fmt.Printf("running for %v seconds, online: %v, NumGoroutine: %v, success: %v, totalSuccess: %v, failed: %v, totalFailed: %v\n", i, connected, runtime.NumGoroutine(), nSuccess, totalSuccess, nFailed, totalFailed) 136 | } 137 | }() 138 | 139 | interrupt := make(chan os.Signal, 1) 140 | signal.Notify(interrupt, os.Interrupt) 141 | <-interrupt 142 | } 143 | 144 | var addrs = []string{ 145 | "localhost:28001", 146 | "localhost:28002", 147 | "localhost:28003", 148 | "localhost:28004", 149 | "localhost:28005", 150 | "localhost:28006", 151 | "localhost:28007", 152 | "localhost:28008", 153 | "localhost:28009", 154 | "localhost:28010", 155 | 156 | "localhost:28011", 157 | "localhost:28012", 158 | "localhost:28013", 159 | "localhost:28014", 160 | "localhost:28015", 161 | "localhost:28016", 162 | "localhost:28017", 163 | "localhost:28018", 164 | "localhost:28019", 165 | "localhost:28020", 166 | 167 | "localhost:28021", 168 | "localhost:28022", 169 | "localhost:28023", 170 | "localhost:28024", 171 | "localhost:28025", 172 | "localhost:28026", 173 | "localhost:28027", 174 | "localhost:28028", 175 | "localhost:28029", 176 | "localhost:28030", 177 | 178 | "localhost:28031", 179 | "localhost:28032", 180 | "localhost:28033", 181 | "localhost:28034", 182 | "localhost:28035", 183 | "localhost:28036", 184 | "localhost:28037", 185 | "localhost:28038", 186 | "localhost:28039", 187 | "localhost:28040", 188 | 189 | "localhost:28041", 190 | "localhost:28042", 191 | "localhost:28043", 192 | "localhost:28044", 193 | "localhost:28045", 194 | "localhost:28046", 195 | "localhost:28047", 196 | "localhost:28048", 197 | "localhost:28049", 198 | "localhost:28050", 199 | } 200 | -------------------------------------------------------------------------------- /websocket_1m/nhooyr/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "runtime" 9 | "sync/atomic" 10 | "time" 11 | 12 | "nhooyr.io/websocket" 13 | ) 14 | 15 | var ( 16 | qps uint64 = 0 17 | total uint64 = 0 18 | ) 19 | 20 | func echo(w http.ResponseWriter, r *http.Request) { 21 | c, err := websocket.Accept(w, r, nil) 22 | if err != nil { 23 | return 24 | } 25 | defer c.Close(websocket.StatusInternalError, "the sky is falling") 26 | 27 | for { 28 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*60) 29 | defer cancel() 30 | mt, data, err := c.Read(ctx) 31 | if err != nil { 32 | log.Println("read:", err) 33 | break 34 | } 35 | err = c.Write(ctx, mt, data) 36 | if err != nil { 37 | log.Println("write:", err) 38 | break 39 | } 40 | atomic.AddUint64(&qps, 1) 41 | } 42 | } 43 | 44 | func serve(addrs []string) { 45 | for _, v := range addrs { 46 | go func(addr string) { 47 | mux := &http.ServeMux{} 48 | mux.HandleFunc("/ws", echo) 49 | server := http.Server{ 50 | Addr: addr, 51 | Handler: mux, 52 | } 53 | fmt.Println("server exit:", server.ListenAndServe()) 54 | }(v) 55 | } 56 | } 57 | 58 | func main() { 59 | serve(addrs) 60 | ticker := time.NewTicker(time.Second) 61 | for i := 1; true; i++ { 62 | <-ticker.C 63 | n := atomic.SwapUint64(&qps, 0) 64 | total += n 65 | fmt.Printf("running for %v seconds, NumGoroutine: %v, qps: %v, total: %v\n", i, runtime.NumGoroutine(), n, total) 66 | } 67 | } 68 | 69 | var addrs = []string{ 70 | "localhost:28001", 71 | "localhost:28002", 72 | "localhost:28003", 73 | "localhost:28004", 74 | "localhost:28005", 75 | "localhost:28006", 76 | "localhost:28007", 77 | "localhost:28008", 78 | "localhost:28009", 79 | "localhost:28010", 80 | 81 | "localhost:28011", 82 | "localhost:28012", 83 | "localhost:28013", 84 | "localhost:28014", 85 | "localhost:28015", 86 | "localhost:28016", 87 | "localhost:28017", 88 | "localhost:28018", 89 | "localhost:28019", 90 | "localhost:28020", 91 | 92 | "localhost:28021", 93 | "localhost:28022", 94 | "localhost:28023", 95 | "localhost:28024", 96 | "localhost:28025", 97 | "localhost:28026", 98 | "localhost:28027", 99 | "localhost:28028", 100 | "localhost:28029", 101 | "localhost:28030", 102 | 103 | "localhost:28031", 104 | "localhost:28032", 105 | "localhost:28033", 106 | "localhost:28034", 107 | "localhost:28035", 108 | "localhost:28036", 109 | "localhost:28037", 110 | "localhost:28038", 111 | "localhost:28039", 112 | "localhost:28040", 113 | 114 | "localhost:28041", 115 | "localhost:28042", 116 | "localhost:28043", 117 | "localhost:28044", 118 | "localhost:28045", 119 | "localhost:28046", 120 | "localhost:28047", 121 | "localhost:28048", 122 | "localhost:28049", 123 | "localhost:28050", 124 | } 125 | -------------------------------------------------------------------------------- /websocket_1m/server_nbio/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "net/http" 7 | "runtime" 8 | "sync/atomic" 9 | "time" 10 | 11 | "github.com/lesismal/nbio/nbhttp" 12 | "github.com/lesismal/nbio/nbhttp/websocket" 13 | ) 14 | 15 | var ( 16 | qps uint64 = 0 17 | total uint64 = 0 18 | 19 | engine *nbhttp.Engine 20 | 21 | upgrader = newUpgrader() 22 | ) 23 | 24 | func newUpgrader() *websocket.Upgrader { 25 | u := websocket.NewUpgrader() 26 | u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 27 | c.SetReadDeadline(time.Now().Add(time.Second * 60)) 28 | c.WriteMessage(messageType, data) 29 | atomic.AddUint64(&qps, 1) 30 | }) 31 | 32 | return u 33 | } 34 | 35 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 36 | _, err := upgrader.Upgrade(w, r, nil) 37 | if err != nil { 38 | panic(err) 39 | } 40 | } 41 | 42 | func main() { 43 | flag.Parse() 44 | 45 | mux := &http.ServeMux{} 46 | mux.HandleFunc("/ws", onWebsocket) 47 | 48 | engine = nbhttp.NewEngine(nbhttp.Config{ 49 | Network: "tcp", 50 | Addrs: addrs, 51 | MaxLoad: 1000000, 52 | ReleaseWebsocketPayload: true, 53 | Handler: mux, 54 | ReadBufferSize: 1024 * 4, 55 | IOMod: nbhttp.IOModMixed, 56 | MaxBlockingOnline: 100000, 57 | }) 58 | 59 | err := engine.Start() 60 | if err != nil { 61 | fmt.Printf("nbio.Start failed: %v\n", err) 62 | return 63 | } 64 | defer engine.Stop() 65 | 66 | ticker := time.NewTicker(time.Second) 67 | for i := 1; true; i++ { 68 | <-ticker.C 69 | n := atomic.SwapUint64(&qps, 0) 70 | total += n 71 | fmt.Printf("running for %v seconds, NumGoroutine: %v, qps: %v, total: %v\n", i, runtime.NumGoroutine(), n, total) 72 | } 73 | } 74 | 75 | var addrs = []string{ 76 | "localhost:28001", 77 | "localhost:28002", 78 | "localhost:28003", 79 | "localhost:28004", 80 | "localhost:28005", 81 | "localhost:28006", 82 | "localhost:28007", 83 | "localhost:28008", 84 | "localhost:28009", 85 | "localhost:28010", 86 | 87 | "localhost:28011", 88 | "localhost:28012", 89 | "localhost:28013", 90 | "localhost:28014", 91 | "localhost:28015", 92 | "localhost:28016", 93 | "localhost:28017", 94 | "localhost:28018", 95 | "localhost:28019", 96 | "localhost:28020", 97 | 98 | "localhost:28021", 99 | "localhost:28022", 100 | "localhost:28023", 101 | "localhost:28024", 102 | "localhost:28025", 103 | "localhost:28026", 104 | "localhost:28027", 105 | "localhost:28028", 106 | "localhost:28029", 107 | "localhost:28030", 108 | 109 | "localhost:28031", 110 | "localhost:28032", 111 | "localhost:28033", 112 | "localhost:28034", 113 | "localhost:28035", 114 | "localhost:28036", 115 | "localhost:28037", 116 | "localhost:28038", 117 | "localhost:28039", 118 | "localhost:28040", 119 | 120 | "localhost:28041", 121 | "localhost:28042", 122 | "localhost:28043", 123 | "localhost:28044", 124 | "localhost:28045", 125 | "localhost:28046", 126 | "localhost:28047", 127 | "localhost:28048", 128 | "localhost:28049", 129 | "localhost:28050", 130 | } 131 | -------------------------------------------------------------------------------- /websocket_1m/server_net/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "runtime" 8 | "sync/atomic" 9 | "time" 10 | 11 | "github.com/gorilla/websocket" 12 | ) 13 | 14 | var ( 15 | qps uint64 = 0 16 | total uint64 = 0 17 | 18 | upgrader = websocket.Upgrader{} 19 | ) 20 | 21 | func echo(w http.ResponseWriter, r *http.Request) { 22 | c, err := upgrader.Upgrade(w, r, nil) 23 | if err != nil { 24 | log.Print("upgrade:", err) 25 | return 26 | } 27 | defer c.Close() 28 | for { 29 | mt, message, err := c.ReadMessage() 30 | if err != nil { 31 | log.Println("read:", err) 32 | break 33 | } 34 | err = c.WriteMessage(mt, message) 35 | if err != nil { 36 | log.Println("write:", err) 37 | break 38 | } 39 | atomic.AddUint64(&qps, 1) 40 | } 41 | } 42 | 43 | func serve(addrs []string) { 44 | for _, v := range addrs { 45 | go func(addr string) { 46 | mux := &http.ServeMux{} 47 | mux.HandleFunc("/ws", echo) 48 | server := http.Server{ 49 | Addr: addr, 50 | Handler: mux, 51 | } 52 | fmt.Println("server exit:", server.ListenAndServe()) 53 | }(v) 54 | } 55 | } 56 | func main() { 57 | serve(addrs) 58 | ticker := time.NewTicker(time.Second) 59 | for i := 1; true; i++ { 60 | <-ticker.C 61 | n := atomic.SwapUint64(&qps, 0) 62 | total += n 63 | fmt.Printf("running for %v seconds, NumGoroutine: %v, qps: %v, total: %v\n", i, runtime.NumGoroutine(), n, total) 64 | } 65 | } 66 | 67 | var addrs = []string{ 68 | "localhost:28001", 69 | "localhost:28002", 70 | "localhost:28003", 71 | "localhost:28004", 72 | "localhost:28005", 73 | "localhost:28006", 74 | "localhost:28007", 75 | "localhost:28008", 76 | "localhost:28009", 77 | "localhost:28010", 78 | 79 | "localhost:28011", 80 | "localhost:28012", 81 | "localhost:28013", 82 | "localhost:28014", 83 | "localhost:28015", 84 | "localhost:28016", 85 | "localhost:28017", 86 | "localhost:28018", 87 | "localhost:28019", 88 | "localhost:28020", 89 | 90 | "localhost:28021", 91 | "localhost:28022", 92 | "localhost:28023", 93 | "localhost:28024", 94 | "localhost:28025", 95 | "localhost:28026", 96 | "localhost:28027", 97 | "localhost:28028", 98 | "localhost:28029", 99 | "localhost:28030", 100 | 101 | "localhost:28031", 102 | "localhost:28032", 103 | "localhost:28033", 104 | "localhost:28034", 105 | "localhost:28035", 106 | "localhost:28036", 107 | "localhost:28037", 108 | "localhost:28038", 109 | "localhost:28039", 110 | "localhost:28040", 111 | 112 | "localhost:28041", 113 | "localhost:28042", 114 | "localhost:28043", 115 | "localhost:28044", 116 | "localhost:28045", 117 | "localhost:28046", 118 | "localhost:28047", 119 | "localhost:28048", 120 | "localhost:28049", 121 | "localhost:28050", 122 | } 123 | -------------------------------------------------------------------------------- /websocket_1m_tls/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "flag" 6 | "fmt" 7 | "net/url" 8 | "runtime" 9 | "sync" 10 | "sync/atomic" 11 | "time" 12 | 13 | "github.com/gorilla/websocket" 14 | "github.com/lesismal/nbio/taskpool" 15 | ) 16 | 17 | var ( 18 | connected uint64 = 0 19 | success uint64 = 0 20 | failed uint64 = 0 21 | totalSuccess uint64 = 0 22 | totalFailed uint64 = 0 23 | 24 | sleepTime = flag.Int("s", 0, "sleep time for each loop in a goroutine") 25 | numClient = flag.Int("c", 100000, "client num") 26 | numGoroutine = flag.Int("g", 1000, "goroutine num") 27 | ) 28 | 29 | func main() { 30 | flag.Parse() 31 | 32 | connNum := *numClient 33 | goroutineNum := *numGoroutine 34 | 35 | wg := sync.WaitGroup{} 36 | conns := make([]*websocket.Conn, connNum) 37 | pool := taskpool.New(8, 1024) 38 | 39 | go func() { 40 | for i := 0; i < connNum; i++ { 41 | addr := addrs[i%len(addrs)] 42 | u := url.URL{Scheme: "wss", Host: addr, Path: "/wss"} 43 | dialer := websocket.DefaultDialer 44 | dialer.TLSClientConfig = &tls.Config{ 45 | InsecureSkipVerify: true, 46 | } 47 | idx := i 48 | wg.Add(1) 49 | pool.Go(func() { 50 | defer wg.Done() 51 | for { 52 | conn, _, err := dialer.Dial(u.String(), nil) 53 | if err == nil { 54 | conns[idx] = conn 55 | atomic.AddUint64(&connected, 1) 56 | break 57 | } 58 | time.Sleep(time.Second / 10) 59 | } 60 | }) 61 | } 62 | wg.Wait() 63 | 64 | for i := 0; i < goroutineNum; i++ { 65 | subConns := conns[:connNum/goroutineNum] 66 | conns = conns[connNum/goroutineNum:] 67 | go loop(subConns) 68 | } 69 | }() 70 | 71 | ticker := time.NewTicker(time.Second) 72 | for i := 1; true; i++ { 73 | <-ticker.C 74 | nSuccess := atomic.SwapUint64(&success, 0) 75 | nFailed := atomic.SwapUint64(&failed, 0) 76 | totalSuccess += nSuccess 77 | totalFailed += nFailed 78 | fmt.Printf("running for %v seconds, online: %v, NumGoroutine: %v, success: %v, totalSuccess: %v, failed: %v, totalFailed: %v\n", i, connected, runtime.NumGoroutine(), nSuccess, totalSuccess, nFailed, totalFailed) 79 | } 80 | } 81 | 82 | func loop(conns []*websocket.Conn) { 83 | for { 84 | for _, conn := range conns { 85 | echo(conn) 86 | } 87 | if *sleepTime > 0 { 88 | time.Sleep(time.Second * time.Duration(*sleepTime)) 89 | } 90 | } 91 | } 92 | 93 | func echo(c *websocket.Conn) { 94 | text := "hello world" 95 | err := c.WriteMessage(websocket.TextMessage, []byte(text)) 96 | if err != nil { 97 | fmt.Println("WriteMessage failed 111:", err) 98 | atomic.AddUint64(&failed, 1) 99 | panic(err) 100 | } 101 | 102 | _, message, err := c.ReadMessage() 103 | if err != nil { 104 | fmt.Println("ReadMessage failed 222:", err) 105 | atomic.AddUint64(&failed, 1) 106 | panic(err) 107 | } 108 | 109 | if string(message) != text { 110 | fmt.Println("ReadMessage failed 333:", string(message)) 111 | atomic.AddUint64(&failed, 1) 112 | panic(err) 113 | } else { 114 | atomic.AddUint64(&success, 1) 115 | } 116 | } 117 | 118 | var addrs = []string{ 119 | "localhost:28001", 120 | "localhost:28002", 121 | "localhost:28003", 122 | "localhost:28004", 123 | "localhost:28005", 124 | "localhost:28006", 125 | "localhost:28007", 126 | "localhost:28008", 127 | "localhost:28009", 128 | "localhost:28010", 129 | 130 | "localhost:28011", 131 | "localhost:28012", 132 | "localhost:28013", 133 | "localhost:28014", 134 | "localhost:28015", 135 | "localhost:28016", 136 | "localhost:28017", 137 | "localhost:28018", 138 | "localhost:28019", 139 | "localhost:28020", 140 | 141 | "localhost:28021", 142 | "localhost:28022", 143 | "localhost:28023", 144 | "localhost:28024", 145 | "localhost:28025", 146 | "localhost:28026", 147 | "localhost:28027", 148 | "localhost:28028", 149 | "localhost:28029", 150 | "localhost:28030", 151 | 152 | "localhost:28031", 153 | "localhost:28032", 154 | "localhost:28033", 155 | "localhost:28034", 156 | "localhost:28035", 157 | "localhost:28036", 158 | "localhost:28037", 159 | "localhost:28038", 160 | "localhost:28039", 161 | "localhost:28040", 162 | 163 | "localhost:28041", 164 | "localhost:28042", 165 | "localhost:28043", 166 | "localhost:28044", 167 | "localhost:28045", 168 | "localhost:28046", 169 | "localhost:28047", 170 | "localhost:28048", 171 | "localhost:28049", 172 | "localhost:28050", 173 | } 174 | -------------------------------------------------------------------------------- /websocket_1m_tls/nbioclient/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "net/url" 7 | "os" 8 | "os/signal" 9 | "runtime" 10 | "sync" 11 | "sync/atomic" 12 | "time" 13 | 14 | "github.com/lesismal/llib/std/crypto/tls" 15 | "github.com/lesismal/nbio/nbhttp" 16 | "github.com/lesismal/nbio/nbhttp/websocket" 17 | "github.com/lesismal/nbio/taskpool" 18 | ) 19 | 20 | var ( 21 | connected uint64 = 0 22 | success uint64 = 0 23 | failed uint64 = 0 24 | totalSuccess uint64 = 0 25 | totalFailed uint64 = 0 26 | 27 | sleepTime = flag.Int("s", 1, "sleep time for each loop in a goroutine") 28 | numClient = flag.Int("c", 50000, "client num") 29 | 30 | text = "hello world" 31 | ) 32 | 33 | func newUpgrader() *websocket.Upgrader { 34 | u := websocket.NewUpgrader() 35 | u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 36 | // echo 37 | if *sleepTime > 0 { 38 | time.AfterFunc(time.Second*time.Duration(*sleepTime), func() { 39 | err := c.WriteMessage(messageType, data) 40 | if err != nil { 41 | fmt.Println("WriteMessage failed 111:", err) 42 | atomic.AddUint64(&failed, 1) 43 | panic(err) 44 | } 45 | }) 46 | } else { 47 | err := c.WriteMessage(messageType, data) 48 | if err != nil { 49 | fmt.Println("WriteMessage failed 111:", err) 50 | atomic.AddUint64(&failed, 1) 51 | panic(err) 52 | } 53 | } 54 | if string(data) != text { 55 | atomic.AddUint64(&failed, 1) 56 | panic(fmt.Errorf("not equal: %v != %v", string(data), text)) 57 | } else { 58 | atomic.AddUint64(&success, 1) 59 | } 60 | }) 61 | 62 | u.OnClose(func(c *websocket.Conn, err error) { 63 | fmt.Println("OnClose:", c.RemoteAddr().String(), err) 64 | }) 65 | 66 | return u 67 | } 68 | 69 | func main() { 70 | flag.Parse() 71 | 72 | engine := nbhttp.NewEngine(nbhttp.Config{}) 73 | err := engine.Start() 74 | if err != nil { 75 | fmt.Printf("nbio.Start failed: %v\n", err) 76 | return 77 | } 78 | 79 | connNum := *numClient 80 | wg := sync.WaitGroup{} 81 | conns := make([]*websocket.Conn, connNum) 82 | pool := taskpool.New(8, 1024) 83 | 84 | go func() { 85 | for i := 0; i < connNum; i++ { 86 | addr := addrs[i%len(addrs)] 87 | u := url.URL{Scheme: "wss", Host: addr, Path: "/wss"} 88 | tlsConfig := &tls.Config{ 89 | InsecureSkipVerify: true, 90 | } 91 | dialer := &websocket.Dialer{ 92 | Engine: engine, 93 | Upgrader: newUpgrader(), 94 | DialTimeout: time.Second * 3, 95 | TLSClientConfig: tlsConfig, 96 | } 97 | idx := i 98 | wg.Add(1) 99 | pool.Go(func() { 100 | defer wg.Done() 101 | for { 102 | conn, _, err := dialer.Dial(u.String(), nil) 103 | if err == nil { 104 | conns[idx] = conn 105 | atomic.AddUint64(&connected, 1) 106 | break 107 | } 108 | time.Sleep(time.Second / 10) 109 | } 110 | }) 111 | } 112 | 113 | wg.Wait() 114 | 115 | for i := 0; i < connNum; i++ { 116 | c := conns[i] 117 | text := "hello world" 118 | err := c.WriteMessage(websocket.TextMessage, []byte(text)) 119 | if err != nil { 120 | fmt.Println("WriteMessage failed 111:", err) 121 | atomic.AddUint64(&failed, 1) 122 | panic(err) 123 | } 124 | } 125 | }() 126 | 127 | go func() { 128 | ticker := time.NewTicker(time.Second) 129 | for i := 1; true; i++ { 130 | <-ticker.C 131 | nSuccess := atomic.SwapUint64(&success, 0) 132 | nFailed := atomic.SwapUint64(&failed, 0) 133 | totalSuccess += nSuccess 134 | totalFailed += nFailed 135 | fmt.Printf("running for %v seconds, online: %v, NumGoroutine: %v, success: %v, totalSuccess: %v, failed: %v, totalFailed: %v\n", i, connected, runtime.NumGoroutine(), nSuccess, totalSuccess, nFailed, totalFailed) 136 | } 137 | }() 138 | 139 | interrupt := make(chan os.Signal, 1) 140 | signal.Notify(interrupt, os.Interrupt) 141 | <-interrupt 142 | } 143 | 144 | var addrs = []string{ 145 | "localhost:28001", 146 | "localhost:28002", 147 | "localhost:28003", 148 | "localhost:28004", 149 | "localhost:28005", 150 | "localhost:28006", 151 | "localhost:28007", 152 | "localhost:28008", 153 | "localhost:28009", 154 | "localhost:28010", 155 | 156 | "localhost:28011", 157 | "localhost:28012", 158 | "localhost:28013", 159 | "localhost:28014", 160 | "localhost:28015", 161 | "localhost:28016", 162 | "localhost:28017", 163 | "localhost:28018", 164 | "localhost:28019", 165 | "localhost:28020", 166 | 167 | "localhost:28021", 168 | "localhost:28022", 169 | "localhost:28023", 170 | "localhost:28024", 171 | "localhost:28025", 172 | "localhost:28026", 173 | "localhost:28027", 174 | "localhost:28028", 175 | "localhost:28029", 176 | "localhost:28030", 177 | 178 | "localhost:28031", 179 | "localhost:28032", 180 | "localhost:28033", 181 | "localhost:28034", 182 | "localhost:28035", 183 | "localhost:28036", 184 | "localhost:28037", 185 | "localhost:28038", 186 | "localhost:28039", 187 | "localhost:28040", 188 | 189 | "localhost:28041", 190 | "localhost:28042", 191 | "localhost:28043", 192 | "localhost:28044", 193 | "localhost:28045", 194 | "localhost:28046", 195 | "localhost:28047", 196 | "localhost:28048", 197 | "localhost:28049", 198 | "localhost:28050", 199 | } 200 | -------------------------------------------------------------------------------- /websocket_1m_tls/server_nbio/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "runtime" 9 | "sync/atomic" 10 | "time" 11 | 12 | "github.com/lesismal/llib/std/crypto/tls" 13 | "github.com/lesismal/nbio/nbhttp" 14 | "github.com/lesismal/nbio/nbhttp/websocket" 15 | ) 16 | 17 | var ( 18 | qps uint64 = 0 19 | total uint64 = 0 20 | 21 | engine *nbhttp.Engine 22 | 23 | upgrader = newUpgrader() 24 | ) 25 | 26 | func newUpgrader() *websocket.Upgrader { 27 | u := websocket.NewUpgrader() 28 | u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 29 | c.WriteMessage(messageType, data) 30 | atomic.AddUint64(&qps, 1) 31 | }) 32 | 33 | return u 34 | } 35 | 36 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 37 | conn, err := upgrader.Upgrade(w, r, nil) 38 | if err != nil { 39 | panic(err) 40 | } 41 | conn.SetReadDeadline(time.Time{}) 42 | } 43 | 44 | func main() { 45 | go func() { 46 | if err := http.ListenAndServe("localhost:6060", nil); err != nil { 47 | panic(err) 48 | } 49 | }() 50 | 51 | cert, err := tls.X509KeyPair(rsaCertPEM, rsaKeyPEM) 52 | if err != nil { 53 | log.Fatalf("tls.X509KeyPair failed: %v", err) 54 | } 55 | tlsConfig := &tls.Config{ 56 | Certificates: []tls.Certificate{cert}, 57 | InsecureSkipVerify: true, 58 | } 59 | 60 | mux := &http.ServeMux{} 61 | mux.HandleFunc("/wss", onWebsocket) 62 | 63 | engine = nbhttp.NewEngine(nbhttp.Config{ 64 | Network: "tcp", 65 | AddrsTLS: addrs, 66 | TLSConfig: tlsConfig, 67 | MaxLoad: 1000000, 68 | ReleaseWebsocketPayload: true, 69 | Handler: mux, 70 | }) 71 | 72 | err = engine.Start() 73 | if err != nil { 74 | fmt.Printf("nbio.Start failed: %v\n", err) 75 | return 76 | } 77 | defer engine.Stop() 78 | 79 | ticker := time.NewTicker(time.Second) 80 | for i := 1; true; i++ { 81 | <-ticker.C 82 | n := atomic.SwapUint64(&qps, 0) 83 | total += n 84 | fmt.Printf("running for %v seconds, NumGoroutine: %v, qps: %v, total: %v\n", i, runtime.NumGoroutine(), n, total) 85 | } 86 | } 87 | 88 | var addrs = []string{ 89 | "localhost:28001", 90 | "localhost:28002", 91 | "localhost:28003", 92 | "localhost:28004", 93 | "localhost:28005", 94 | "localhost:28006", 95 | "localhost:28007", 96 | "localhost:28008", 97 | "localhost:28009", 98 | "localhost:28010", 99 | 100 | "localhost:28011", 101 | "localhost:28012", 102 | "localhost:28013", 103 | "localhost:28014", 104 | "localhost:28015", 105 | "localhost:28016", 106 | "localhost:28017", 107 | "localhost:28018", 108 | "localhost:28019", 109 | "localhost:28020", 110 | 111 | "localhost:28021", 112 | "localhost:28022", 113 | "localhost:28023", 114 | "localhost:28024", 115 | "localhost:28025", 116 | "localhost:28026", 117 | "localhost:28027", 118 | "localhost:28028", 119 | "localhost:28029", 120 | "localhost:28030", 121 | 122 | "localhost:28031", 123 | "localhost:28032", 124 | "localhost:28033", 125 | "localhost:28034", 126 | "localhost:28035", 127 | "localhost:28036", 128 | "localhost:28037", 129 | "localhost:28038", 130 | "localhost:28039", 131 | "localhost:28040", 132 | 133 | "localhost:28041", 134 | "localhost:28042", 135 | "localhost:28043", 136 | "localhost:28044", 137 | "localhost:28045", 138 | "localhost:28046", 139 | "localhost:28047", 140 | "localhost:28048", 141 | "localhost:28049", 142 | "localhost:28050", 143 | } 144 | 145 | var rsaCertPEM = []byte(`-----BEGIN CERTIFICATE----- 146 | MIIDazCCAlOgAwIBAgIUJeohtgk8nnt8ofratXJg7kUJsI4wDQYJKoZIhvcNAQEL 147 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 148 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMDcwODIyNThaFw0zMDEy 149 | MDUwODIyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 150 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 151 | AQUAA4IBDwAwggEKAoIBAQCy+ZrIvwwiZv4bPmvKx/637ltZLwfgh3ouiEaTchGu 152 | IQltthkqINHxFBqqJg44TUGHWthlrq6moQuKnWNjIsEc6wSD1df43NWBLgdxbPP0 153 | x4tAH9pIJU7TQqbznjDBhzRbUjVXBIcn7bNknY2+5t784pPF9H1v7h8GqTWpNH9l 154 | cz/v+snoqm9HC+qlsFLa4A3X9l5v05F1uoBfUALlP6bWyjHAfctpiJkoB9Yw1TJa 155 | gpq7E50kfttwfKNkkAZIbib10HugkMoQJAs2EsGkje98druIl8IXmuvBIF6nZHuM 156 | lt3UIZjS9RwPPLXhRHt1P0mR7BoBcOjiHgtSEs7Wk+j7AgMBAAGjUzBRMB0GA1Ud 157 | DgQWBBQdheJv73XSOhgMQtkwdYPnfO02+TAfBgNVHSMEGDAWgBQdheJv73XSOhgM 158 | QtkwdYPnfO02+TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf 159 | SKVNMdmBpD9m53kCrguo9iKQqmhnI0WLkpdWszc/vBgtpOE5ENOfHGAufHZve871 160 | 2fzTXrgR0TF6UZWsQOqCm5Oh3URsCdXWewVMKgJ3DCii6QJ0MnhSFt6+xZE9C6Hi 161 | WhcywgdR8t/JXKDam6miohW8Rum/IZo5HK9Jz/R9icKDGumcqoaPj/ONvY4EUwgB 162 | irKKB7YgFogBmCtgi30beLVkXgk0GEcAf19lHHtX2Pv/lh3m34li1C9eBm1ca3kk 163 | M2tcQtm1G89NROEjcG92cg+GX3GiWIjbI0jD1wnVy2LCOXMgOVbKfGfVKISFt0b1 164 | DNn00G8C6ttLoGU2snyk 165 | -----END CERTIFICATE----- 166 | `) 167 | 168 | var rsaKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- 169 | MIIEogIBAAKCAQEAsvmayL8MImb+Gz5rysf+t+5bWS8H4Id6LohGk3IRriEJbbYZ 170 | KiDR8RQaqiYOOE1Bh1rYZa6upqELip1jYyLBHOsEg9XX+NzVgS4HcWzz9MeLQB/a 171 | SCVO00Km854wwYc0W1I1VwSHJ+2zZJ2Nvube/OKTxfR9b+4fBqk1qTR/ZXM/7/rJ 172 | 6KpvRwvqpbBS2uAN1/Zeb9ORdbqAX1AC5T+m1soxwH3LaYiZKAfWMNUyWoKauxOd 173 | JH7bcHyjZJAGSG4m9dB7oJDKECQLNhLBpI3vfHa7iJfCF5rrwSBep2R7jJbd1CGY 174 | 0vUcDzy14UR7dT9JkewaAXDo4h4LUhLO1pPo+wIDAQABAoIBAF6yWwekrlL1k7Xu 175 | jTI6J7hCUesaS1yt0iQUzuLtFBXCPS7jjuUPgIXCUWl9wUBhAC8SDjWe+6IGzAiH 176 | xjKKDQuz/iuTVjbDAeTb6exF7b6yZieDswdBVjfJqHR2Wu3LEBTRpo9oQesKhkTS 177 | aFF97rZ3XCD9f/FdWOU5Wr8wm8edFK0zGsZ2N6r57yf1N6ocKlGBLBZ0v1Sc5ShV 178 | 1PVAxeephQvwL5DrOgkArnuAzwRXwJQG78L0aldWY2q6xABQZQb5+ml7H/kyytef 179 | i+uGo3jHKepVALHmdpCGr9Yv+yCElup+ekv6cPy8qcmMBqGMISL1i1FEONxLcKWp 180 | GEJi6QECgYEA3ZPGMdUm3f2spdHn3C+/+xskQpz6efiPYpnqFys2TZD7j5OOnpcP 181 | ftNokA5oEgETg9ExJQ8aOCykseDc/abHerYyGw6SQxmDbyBLmkZmp9O3iMv2N8Pb 182 | Nrn9kQKSr6LXZ3gXzlrDvvRoYUlfWuLSxF4b4PYifkA5AfsdiKkj+5sCgYEAzseF 183 | XDTRKHHJnzxZDDdHQcwA0G9agsNj64BGUEjsAGmDiDyqOZnIjDLRt0O2X3oiIE5S 184 | TXySSEiIkxjfErVJMumLaIwqVvlS4pYKdQo1dkM7Jbt8wKRQdleRXOPPN7msoEUk 185 | Ta9ZsftHVUknPqblz9Uthb5h+sRaxIaE1llqDiECgYATS4oHzuL6k9uT+Qpyzymt 186 | qThoIJljQ7TgxjxvVhD9gjGV2CikQM1Vov1JBigj4Toc0XuxGXaUC7cv0kAMSpi2 187 | Y+VLG+K6ux8J70sGHTlVRgeGfxRq2MBfLKUbGplBeDG/zeJs0tSW7VullSkblgL6 188 | nKNa3LQ2QEt2k7KHswryHwKBgENDxk8bY1q7wTHKiNEffk+aFD25q4DUHMH0JWti 189 | fVsY98+upFU+gG2S7oOmREJE0aser0lDl7Zp2fu34IEOdfRY4p+s0O0gB+Vrl5VB 190 | L+j7r9bzaX6lNQN6MvA7ryHahZxRQaD/xLbQHgFRXbHUyvdTyo4yQ1821qwNclLk 191 | HUrhAoGAUtjR3nPFR4TEHlpTSQQovS8QtGTnOi7s7EzzdPWmjHPATrdLhMA0ezPj 192 | Mr+u5TRncZBIzAZtButlh1AHnpN/qO3P0c0Rbdep3XBc/82JWO8qdb5QvAkxga3X 193 | BpA7MNLxiqss+rCbwf3NbWxEMiDQ2zRwVoafVFys7tjmv6t2Xck= 194 | -----END RSA PRIVATE KEY----- 195 | `) 196 | -------------------------------------------------------------------------------- /websocket_1m_tls/server_net/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDazCCAlOgAwIBAgIUAtGajmdRu86Si+4JqDS9R0xjCJ8wDQYJKoZIhvcNAQEL 3 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTAzMjAwMzMyMDZaFw0yMjAz 5 | MjAwMzMyMDZaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 6 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 7 | AQUAA4IBDwAwggEKAoIBAQCodlskKbfkv5CJbxmum3G78YZq3XujcCp8yzaLJ7iB 8 | GDu8myaEAbv6YmWuSGuJuScomnRx6MTi9lVwbtPlPw7LI24n+wk+FbkMwSyO+DpH 9 | 18w3q0+WouoPou6kqooV9PY6UkzW1dE4b9yN2X2SpUfAisTkqCRP++kV7SdMMjTR 10 | jKdtZzEwfLD5TEGTgFf6/ertVJXjIIDhCMV016xWuvumi0kTLgRVAk71q1fulkBa 11 | si2qyfr3dwWgDeOmUwV7escK0Tvlr8wWumMoeA0MKH+6KXkW96tQuAujy250TJ0A 12 | 0Z9Y8MwKV7/plsjWIjkfwgU+p0BCIDO8BC02WfFd9IGjAgMBAAGjUzBRMB0GA1Ud 13 | DgQWBBTOg4YQWasdScACK36FUXwklGXIlTAfBgNVHSMEGDAWgBTOg4YQWasdScAC 14 | K36FUXwklGXIlTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCl 15 | uWCs702luYPw1fFUXvcUDsa80IBsUMUr/Pcz+xAhj8CLPKNHUNrR1klXT6U4O0LW 16 | 7N/XYXxY3lhevjPO8SsP3mEuP9cB8S4P0O452Y+cWySrI0pDhaPv1T8mfUIBxg5K 17 | PZsmxv7NHMCj+QOgljKFapmP6RJG3t9nQefljPwBNaNxvBL7ST6rZ/TT9WlLg4EE 18 | vYoG/Mv3AtxP9tKzjrXJTJOmNAyuaQhC7YhVNg72E9jqryQw2A1iAAvgtBpTJ6If 19 | zHZkMACp5DIR4sVOAyBaM27gRb8vpiJs+1LDCwN681UHe4Rjbrl81ruhVfAUT1eX 20 | pdwdlCM6U9/oz11spqL+ 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /websocket_1m_tls/server_net/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | _ "net/http/pprof" 8 | "runtime" 9 | "sync/atomic" 10 | "time" 11 | 12 | "github.com/gorilla/websocket" 13 | ) 14 | 15 | var ( 16 | qps uint64 = 0 17 | total uint64 = 0 18 | 19 | upgrader = websocket.Upgrader{} 20 | ) 21 | 22 | func echo(w http.ResponseWriter, r *http.Request) { 23 | c, err := upgrader.Upgrade(w, r, nil) 24 | if err != nil { 25 | log.Print("upgrade:", err) 26 | return 27 | } 28 | defer c.Close() 29 | for { 30 | mt, message, err := c.ReadMessage() 31 | if err != nil { 32 | log.Println("read:", err) 33 | break 34 | } 35 | err = c.WriteMessage(mt, message) 36 | if err != nil { 37 | log.Println("write:", err) 38 | break 39 | } 40 | } 41 | } 42 | 43 | func serve(addrs []string) { 44 | for _, v := range addrs { 45 | go func(addr string) { 46 | mux := &http.ServeMux{} 47 | mux.HandleFunc("/wss", echo) 48 | server := http.Server{ 49 | Addr: addr, 50 | Handler: mux, 51 | } 52 | server.ListenAndServeTLS("server.crt", "server.key") 53 | }(v) 54 | } 55 | } 56 | func main() { 57 | go func() { 58 | if err := http.ListenAndServe("localhost:6060", nil); err != nil { 59 | panic(err) 60 | } 61 | }() 62 | 63 | serve(addrs) 64 | ticker := time.NewTicker(time.Second) 65 | for i := 1; true; i++ { 66 | <-ticker.C 67 | n := atomic.SwapUint64(&qps, 0) 68 | total += n 69 | fmt.Printf("running for %v seconds, NumGoroutine: %v, qps: %v, total: %v\n", i, runtime.NumGoroutine(), n, total) 70 | } 71 | } 72 | 73 | var addrs = []string{ 74 | "localhost:28001", 75 | "localhost:28002", 76 | "localhost:28003", 77 | "localhost:28004", 78 | "localhost:28005", 79 | "localhost:28006", 80 | "localhost:28007", 81 | "localhost:28008", 82 | "localhost:28009", 83 | "localhost:28010", 84 | 85 | "localhost:28011", 86 | "localhost:28012", 87 | "localhost:28013", 88 | "localhost:28014", 89 | "localhost:28015", 90 | "localhost:28016", 91 | "localhost:28017", 92 | "localhost:28018", 93 | "localhost:28019", 94 | "localhost:28020", 95 | 96 | "localhost:28021", 97 | "localhost:28022", 98 | "localhost:28023", 99 | "localhost:28024", 100 | "localhost:28025", 101 | "localhost:28026", 102 | "localhost:28027", 103 | "localhost:28028", 104 | "localhost:28029", 105 | "localhost:28030", 106 | 107 | "localhost:28031", 108 | "localhost:28032", 109 | "localhost:28033", 110 | "localhost:28034", 111 | "localhost:28035", 112 | "localhost:28036", 113 | "localhost:28037", 114 | "localhost:28038", 115 | "localhost:28039", 116 | "localhost:28040", 117 | 118 | "localhost:28041", 119 | "localhost:28042", 120 | "localhost:28043", 121 | "localhost:28044", 122 | "localhost:28045", 123 | "localhost:28046", 124 | "localhost:28047", 125 | "localhost:28048", 126 | "localhost:28049", 127 | "localhost:28050", 128 | } 129 | -------------------------------------------------------------------------------- /websocket_1m_tls/server_net/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCodlskKbfkv5CJ 3 | bxmum3G78YZq3XujcCp8yzaLJ7iBGDu8myaEAbv6YmWuSGuJuScomnRx6MTi9lVw 4 | btPlPw7LI24n+wk+FbkMwSyO+DpH18w3q0+WouoPou6kqooV9PY6UkzW1dE4b9yN 5 | 2X2SpUfAisTkqCRP++kV7SdMMjTRjKdtZzEwfLD5TEGTgFf6/ertVJXjIIDhCMV0 6 | 16xWuvumi0kTLgRVAk71q1fulkBasi2qyfr3dwWgDeOmUwV7escK0Tvlr8wWumMo 7 | eA0MKH+6KXkW96tQuAujy250TJ0A0Z9Y8MwKV7/plsjWIjkfwgU+p0BCIDO8BC02 8 | WfFd9IGjAgMBAAECggEBAIjyDg1LzK4r/Dd9FYeX3dFG+lqNSiEG4cMo/2IIHDj1 9 | ljowdhMBxeo5aydOv/zpgyfCx76B1uwaDEKmFaNaq4//cWEW9eB+kDwKHUrsB5S3 10 | nM/0d3KhZVzous8pw68tv6yRkt1iW/5hEQ5PK4G20ql3JbAr77kFs+mbLEMI3V8q 11 | gpySk4djFYSYj85rBsW5xA8lJ1Lh0/pVTgFRGN6owXgcQVw8/RK4p3T0uShwljRV 12 | 7+c0VXfhlj26ZhGQLP00nU5zAmKgC8NJ9VaPvZtMvTcevtLHHZU0Sl9DLEPA217e 13 | wxxsNLjMkfKQix5QLcNjtnoXQajprE9r4nJv5lbz/kECgYEA0FY5/eH7rvSw4CRO 14 | OZBjfQVNDbU/6OOZxCEi4GoAHGXRUKftC0H4IIGSjLa19umSYGm6+D9LojldtoLv 15 | 2Ihp4qK17r5AEs1oWyvugjTcWXui1l1Vs3HEcD0CO2FpRJUzZgC67ga+W1a+6SD4 16 | Z9SE+sKf+sf7Ncvnxk0HnifBZNkCgYEAzwDHXm2q0+PTYz76C+JTfxq/NkK0/M0z 17 | 61NxA5bEcX+qc8ZZNDLSi+Cdp/ZEgorDj4f2Z6pQ4KpcbtC/OvRnWplZNQY2II/t 18 | yty9ucNkjqHgsb+U71XcXwz/sT5NWf9vFd39UIajs39wCSclL1ylMFJA6lT5lffW 19 | Mvw5My0nnNsCgYAXbbrzzBgLosTJUAvj+VLW2mPB6OIZBI9kOP29Eu6UQvb5BQlp 20 | PK/0p92dKWbpL3cglINMK4IL6juZrLvgM+cEb5vaD1cRRjw4FIar6dnlzjuPs8tR 21 | dLfMj2/S0a+O4OB41hgvcF6z0tuBSA3nT0TtFjn7b8XWVOnpqPv0UzAycQKBgF/6 22 | uXY0HTf/87yZr7Cg8RwHF8+d4HMy5jbfyo/icRU7H5psxmGoiu0maZM+YYextXkw 23 | jnSEiNmSxCbxjSlVtzJP1gf66E/yzEv6S9H9IfvsXNWtwe2y/unnigrxm0X2ZNhb 24 | xJ94viB1H0pvJxOtvj4IdWbxnYQGlP+w6QiskChvAoGAQJPb45tawHcmp0M5LGdC 25 | gMyOsCgANK+46H+88Mzz7vwk7vcUTmHwhxyFe3HYohg1mfJXNuRinB0LdDuHefLJ 26 | LkiZ9tn7X8Fn/93g0DCHZczk5cYCRP+Xpln1myA8gOSWwwJvfJ5Gx7c1gmCGqU7g 27 | /KfN+L/ICiRDdCRNfuGMnbU= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /websocket_both_tls_nontls/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "crypto/tls" 7 | "log" 8 | "net/url" 9 | 10 | "github.com/gorilla/websocket" 11 | ) 12 | 13 | var ( 14 | addrNonTLS = "localhost:8080" 15 | addrTLS = "localhost:8443" 16 | ) 17 | 18 | func main() { 19 | clientNonTLS() 20 | clientTLS() 21 | } 22 | 23 | func clientNonTLS() { 24 | u := url.URL{Scheme: "ws", Host: addrNonTLS, Path: "/ws"} 25 | log.Printf("connecting to %s", u.String()) 26 | 27 | dialer := &websocket.Dialer{} 28 | 29 | c, _, err := dialer.Dial(u.String(), nil) 30 | if err != nil { 31 | log.Fatal("dial:", err) 32 | } 33 | defer c.Close() 34 | 35 | for i := 0; i < 3; i++ { 36 | echo(addrNonTLS, c, websocket.TextMessage) 37 | echo(addrNonTLS, c, websocket.BinaryMessage) 38 | } 39 | } 40 | 41 | func clientTLS() { 42 | u := url.URL{Scheme: "wss", Host: addrTLS, Path: "/wss"} 43 | log.Printf("connecting to %s", u.String()) 44 | 45 | tlsConfig := &tls.Config{ 46 | InsecureSkipVerify: true, 47 | } 48 | dialer := &websocket.Dialer{ 49 | TLSClientConfig: tlsConfig, 50 | } 51 | 52 | c, _, err := dialer.Dial(u.String(), nil) 53 | if err != nil { 54 | log.Fatal("dial:", err) 55 | } 56 | defer c.Close() 57 | 58 | for i := 0; i < 3; i++ { 59 | echo(addrTLS, c, websocket.TextMessage) 60 | echo(addrTLS, c, websocket.BinaryMessage) 61 | } 62 | } 63 | 64 | func echo(addr string, c *websocket.Conn, messageType int) { 65 | request := make([]byte, 1024) 66 | if messageType == websocket.TextMessage { 67 | for i := 0; i < len(request); i++ { 68 | request[i] = 'a' + byte(i)%26 69 | } 70 | } else { 71 | rand.Read(request) 72 | } 73 | 74 | err := c.WriteMessage(messageType, request) 75 | if err != nil { 76 | log.Fatalf("write: %v", err) 77 | return 78 | } 79 | 80 | mt, response, err := c.ReadMessage() 81 | if err != nil { 82 | log.Panicf("ReadMessage failed: %v", err) 83 | } 84 | if mt != messageType { 85 | log.Panicf("invalid response messageType: [%v != %v]", mt, messageType) 86 | } 87 | if !bytes.Equal(request, response) { 88 | log.Panicf("invalid response: not equal") 89 | } 90 | log.Printf("echo to %v with message type [%v] success", addr, messageType) 91 | } 92 | -------------------------------------------------------------------------------- /websocket_both_tls_nontls/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "time" 11 | 12 | "github.com/lesismal/llib/std/crypto/tls" 13 | "github.com/lesismal/nbio/nbhttp" 14 | "github.com/lesismal/nbio/nbhttp/websocket" 15 | ) 16 | 17 | func newUpgrader() *websocket.Upgrader { 18 | u := websocket.NewUpgrader() 19 | u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 20 | // echo 21 | c.WriteMessage(messageType, data) 22 | c.SetReadDeadline(time.Now().Add(nbhttp.DefaultKeepaliveTime)) 23 | }) 24 | u.OnClose(func(c *websocket.Conn, err error) { 25 | fmt.Println("OnClose:", c.LocalAddr().String(), err) 26 | }) 27 | 28 | return u 29 | } 30 | 31 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 32 | upgrader := newUpgrader() 33 | conn, err := upgrader.Upgrade(w, r, nil) 34 | if err != nil { 35 | panic(err) 36 | } 37 | fmt.Println("OnOpen:", conn.LocalAddr().String(), r.URL.Path) 38 | } 39 | 40 | func main() { 41 | cert, err := tls.X509KeyPair(rsaCertPEM, rsaKeyPEM) 42 | if err != nil { 43 | log.Fatalf("tls.X509KeyPair failed: %v", err) 44 | } 45 | tlsConfig := &tls.Config{ 46 | Certificates: []tls.Certificate{cert}, 47 | InsecureSkipVerify: true, 48 | } 49 | 50 | mux := &http.ServeMux{} 51 | mux.HandleFunc("/ws", onWebsocket) 52 | mux.HandleFunc("/wss", onWebsocket) 53 | 54 | engine := nbhttp.NewEngine(nbhttp.Config{ 55 | Addrs: []string{"localhost:8080"}, 56 | AddrsTLS: []string{"localhost:8443"}, 57 | TLSConfig: tlsConfig, 58 | Handler: mux, 59 | }) 60 | 61 | err = engine.Start() 62 | if err != nil { 63 | fmt.Printf("nbio.Start failed: %v\n", err) 64 | return 65 | } 66 | 67 | interrupt := make(chan os.Signal, 1) 68 | signal.Notify(interrupt, os.Interrupt) 69 | <-interrupt 70 | 71 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 72 | defer cancel() 73 | engine.Shutdown(ctx) 74 | 75 | log.Println("exit") 76 | } 77 | 78 | var rsaCertPEM = []byte(`-----BEGIN CERTIFICATE----- 79 | MIIDazCCAlOgAwIBAgIUJeohtgk8nnt8ofratXJg7kUJsI4wDQYJKoZIhvcNAQEL 80 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 81 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMDcwODIyNThaFw0zMDEy 82 | MDUwODIyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 83 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 84 | AQUAA4IBDwAwggEKAoIBAQCy+ZrIvwwiZv4bPmvKx/637ltZLwfgh3ouiEaTchGu 85 | IQltthkqINHxFBqqJg44TUGHWthlrq6moQuKnWNjIsEc6wSD1df43NWBLgdxbPP0 86 | x4tAH9pIJU7TQqbznjDBhzRbUjVXBIcn7bNknY2+5t784pPF9H1v7h8GqTWpNH9l 87 | cz/v+snoqm9HC+qlsFLa4A3X9l5v05F1uoBfUALlP6bWyjHAfctpiJkoB9Yw1TJa 88 | gpq7E50kfttwfKNkkAZIbib10HugkMoQJAs2EsGkje98druIl8IXmuvBIF6nZHuM 89 | lt3UIZjS9RwPPLXhRHt1P0mR7BoBcOjiHgtSEs7Wk+j7AgMBAAGjUzBRMB0GA1Ud 90 | DgQWBBQdheJv73XSOhgMQtkwdYPnfO02+TAfBgNVHSMEGDAWgBQdheJv73XSOhgM 91 | QtkwdYPnfO02+TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf 92 | SKVNMdmBpD9m53kCrguo9iKQqmhnI0WLkpdWszc/vBgtpOE5ENOfHGAufHZve871 93 | 2fzTXrgR0TF6UZWsQOqCm5Oh3URsCdXWewVMKgJ3DCii6QJ0MnhSFt6+xZE9C6Hi 94 | WhcywgdR8t/JXKDam6miohW8Rum/IZo5HK9Jz/R9icKDGumcqoaPj/ONvY4EUwgB 95 | irKKB7YgFogBmCtgi30beLVkXgk0GEcAf19lHHtX2Pv/lh3m34li1C9eBm1ca3kk 96 | M2tcQtm1G89NROEjcG92cg+GX3GiWIjbI0jD1wnVy2LCOXMgOVbKfGfVKISFt0b1 97 | DNn00G8C6ttLoGU2snyk 98 | -----END CERTIFICATE----- 99 | `) 100 | 101 | var rsaKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- 102 | MIIEogIBAAKCAQEAsvmayL8MImb+Gz5rysf+t+5bWS8H4Id6LohGk3IRriEJbbYZ 103 | KiDR8RQaqiYOOE1Bh1rYZa6upqELip1jYyLBHOsEg9XX+NzVgS4HcWzz9MeLQB/a 104 | SCVO00Km854wwYc0W1I1VwSHJ+2zZJ2Nvube/OKTxfR9b+4fBqk1qTR/ZXM/7/rJ 105 | 6KpvRwvqpbBS2uAN1/Zeb9ORdbqAX1AC5T+m1soxwH3LaYiZKAfWMNUyWoKauxOd 106 | JH7bcHyjZJAGSG4m9dB7oJDKECQLNhLBpI3vfHa7iJfCF5rrwSBep2R7jJbd1CGY 107 | 0vUcDzy14UR7dT9JkewaAXDo4h4LUhLO1pPo+wIDAQABAoIBAF6yWwekrlL1k7Xu 108 | jTI6J7hCUesaS1yt0iQUzuLtFBXCPS7jjuUPgIXCUWl9wUBhAC8SDjWe+6IGzAiH 109 | xjKKDQuz/iuTVjbDAeTb6exF7b6yZieDswdBVjfJqHR2Wu3LEBTRpo9oQesKhkTS 110 | aFF97rZ3XCD9f/FdWOU5Wr8wm8edFK0zGsZ2N6r57yf1N6ocKlGBLBZ0v1Sc5ShV 111 | 1PVAxeephQvwL5DrOgkArnuAzwRXwJQG78L0aldWY2q6xABQZQb5+ml7H/kyytef 112 | i+uGo3jHKepVALHmdpCGr9Yv+yCElup+ekv6cPy8qcmMBqGMISL1i1FEONxLcKWp 113 | GEJi6QECgYEA3ZPGMdUm3f2spdHn3C+/+xskQpz6efiPYpnqFys2TZD7j5OOnpcP 114 | ftNokA5oEgETg9ExJQ8aOCykseDc/abHerYyGw6SQxmDbyBLmkZmp9O3iMv2N8Pb 115 | Nrn9kQKSr6LXZ3gXzlrDvvRoYUlfWuLSxF4b4PYifkA5AfsdiKkj+5sCgYEAzseF 116 | XDTRKHHJnzxZDDdHQcwA0G9agsNj64BGUEjsAGmDiDyqOZnIjDLRt0O2X3oiIE5S 117 | TXySSEiIkxjfErVJMumLaIwqVvlS4pYKdQo1dkM7Jbt8wKRQdleRXOPPN7msoEUk 118 | Ta9ZsftHVUknPqblz9Uthb5h+sRaxIaE1llqDiECgYATS4oHzuL6k9uT+Qpyzymt 119 | qThoIJljQ7TgxjxvVhD9gjGV2CikQM1Vov1JBigj4Toc0XuxGXaUC7cv0kAMSpi2 120 | Y+VLG+K6ux8J70sGHTlVRgeGfxRq2MBfLKUbGplBeDG/zeJs0tSW7VullSkblgL6 121 | nKNa3LQ2QEt2k7KHswryHwKBgENDxk8bY1q7wTHKiNEffk+aFD25q4DUHMH0JWti 122 | fVsY98+upFU+gG2S7oOmREJE0aser0lDl7Zp2fu34IEOdfRY4p+s0O0gB+Vrl5VB 123 | L+j7r9bzaX6lNQN6MvA7ryHahZxRQaD/xLbQHgFRXbHUyvdTyo4yQ1821qwNclLk 124 | HUrhAoGAUtjR3nPFR4TEHlpTSQQovS8QtGTnOi7s7EzzdPWmjHPATrdLhMA0ezPj 125 | Mr+u5TRncZBIzAZtButlh1AHnpN/qO3P0c0Rbdep3XBc/82JWO8qdb5QvAkxga3X 126 | BpA7MNLxiqss+rCbwf3NbWxEMiDQ2zRwVoafVFys7tjmv6t2Xck= 127 | -----END RSA PRIVATE KEY----- 128 | `) 129 | -------------------------------------------------------------------------------- /websocket_proxy/app_client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "time" 7 | 8 | "github.com/gorilla/websocket" 9 | ) 10 | 11 | var ( 12 | proxyServerAddr = "ws://localhost:8888/ws" 13 | ) 14 | 15 | func main() { 16 | c, _, err := websocket.DefaultDialer.Dial(proxyServerAddr, nil) 17 | if err != nil { 18 | log.Fatalf("Dial failed: %v, %v", proxyServerAddr, err) 19 | } 20 | defer c.Close() 21 | 22 | for i := 0; i < 10; i++ { 23 | { 24 | request := fmt.Sprintf("hello %v", i) 25 | err := c.WriteMessage(websocket.BinaryMessage, []byte(request)) 26 | if err != nil { 27 | log.Fatalf("write: %v", err) 28 | return 29 | } 30 | 31 | receiveType, response, err := c.ReadMessage() 32 | if err != nil { 33 | log.Println("ReadMessage failed:", err) 34 | return 35 | } 36 | if receiveType != websocket.BinaryMessage { 37 | log.Printf("received type(%d) != websocket.BinaryMessage(%d)\n", receiveType, websocket.BinaryMessage) 38 | return 39 | 40 | } 41 | 42 | if string(response) != request { 43 | log.Printf("'%v' != '%v'", len(response), len(request)) 44 | return 45 | } 46 | 47 | log.Printf("success echo: [websocket.BinaryMessage], %v", request) 48 | } 49 | 50 | { 51 | request := fmt.Sprintf("hello %v", i) 52 | err := c.WriteMessage(websocket.TextMessage, []byte(request)) 53 | if err != nil { 54 | log.Fatalf("write: %v", err) 55 | return 56 | } 57 | 58 | receiveType, response, err := c.ReadMessage() 59 | if err != nil { 60 | log.Println("ReadMessage failed:", err) 61 | return 62 | } 63 | if receiveType != websocket.TextMessage { 64 | log.Printf("received type(%d) != websocket.TextMessage(%d)\n", receiveType, websocket.TextMessage) 65 | return 66 | 67 | } 68 | 69 | if string(response) != request { 70 | log.Printf("'%v' != '%v'", len(response), len(request)) 71 | return 72 | } 73 | 74 | log.Printf("success echo: [websocket.TextMessage], %v", request) 75 | } 76 | time.Sleep(time.Second) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /websocket_proxy/app_server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | 7 | "github.com/gorilla/websocket" 8 | ) 9 | 10 | func msgType(messageType int) string { 11 | switch messageType { 12 | case websocket.BinaryMessage: 13 | return "[websocket.BinaryMessage]" 14 | case websocket.TextMessage: 15 | return "[websocket.TextMessage]" 16 | default: 17 | } 18 | return "[]" 19 | } 20 | 21 | func echo(w http.ResponseWriter, r *http.Request) { 22 | upgrader := &websocket.Upgrader{} 23 | c, err := upgrader.Upgrade(w, r, nil) 24 | if err != nil { 25 | log.Print("upgrade:", err) 26 | return 27 | } 28 | defer c.Close() 29 | for { 30 | messageType, message, err := c.ReadMessage() 31 | if err != nil { 32 | log.Println("read failed:", err) 33 | break 34 | } 35 | 36 | log.Printf("app server onMessage: %v, %v", msgType(messageType), string(message)) 37 | 38 | err = c.WriteMessage(messageType, message) 39 | if err != nil { 40 | log.Println("write failed:", err) 41 | break 42 | } 43 | } 44 | } 45 | 46 | var ( 47 | appServerAddr = "localhost:9999" 48 | ) 49 | 50 | func main() { 51 | mux := &http.ServeMux{} 52 | mux.HandleFunc("/ws", echo) 53 | server := http.Server{ 54 | Addr: appServerAddr, 55 | Handler: mux, 56 | } 57 | log.Println("server exit:", server.ListenAndServe()) 58 | } 59 | -------------------------------------------------------------------------------- /websocket_proxy/proxy_server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "time" 11 | 12 | "github.com/lesismal/nbio/nbhttp" 13 | "github.com/lesismal/nbio/nbhttp/websocket" 14 | ) 15 | 16 | var ( 17 | proxyServerAddr = "localhost:8888" 18 | 19 | appServerAddr = "ws://localhost:9999/ws" 20 | 21 | proxyServer *nbhttp.Engine 22 | 23 | upgrader = newUpgrader() 24 | ) 25 | 26 | func msgType(messageType websocket.MessageType) string { 27 | switch messageType { 28 | case websocket.BinaryMessage: 29 | return "[websocket.BinaryMessage]" 30 | case websocket.TextMessage: 31 | return "[websocket.TextMessage]" 32 | default: 33 | } 34 | return "[]" 35 | } 36 | 37 | func newUpgrader() *websocket.Upgrader { 38 | u := websocket.NewUpgrader() 39 | u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 40 | peer, ok := c.Session().(*websocket.Conn) 41 | if ok { 42 | log.Printf("proxy server onMessage [%v -> %v]: %v, %v", c.RemoteAddr(), peer.RemoteAddr(), msgType(messageType), string(data)) 43 | peer.WriteMessage(messageType, data) 44 | } 45 | }) 46 | u.OnClose(func(c *websocket.Conn, err error) { 47 | peer, ok := c.Session().(*websocket.Conn) 48 | if ok { 49 | log.Printf("proxy server onClose [%v -> %v]", c.RemoteAddr(), peer.RemoteAddr()) 50 | peer.Close() 51 | } else { 52 | log.Printf("proxy server onClose [%v -> %v]", c.RemoteAddr(), "") 53 | } 54 | }) 55 | return u 56 | } 57 | 58 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 59 | srcConn, err := upgrader.Upgrade(w, r, nil) 60 | if err != nil { 61 | panic(err) 62 | } 63 | 64 | dialer := &websocket.Dialer{ 65 | Engine: proxyServer, 66 | Upgrader: newUpgrader(), 67 | DialTimeout: time.Second * 3, 68 | } 69 | dstConn, _, err := dialer.Dial(appServerAddr, nil) 70 | if err != nil { 71 | log.Printf("Dial failed: %v, %v", appServerAddr, err) 72 | srcConn.Close() 73 | return 74 | } 75 | 76 | srcConn.SetSession(dstConn) 77 | dstConn.SetSession(srcConn) 78 | } 79 | 80 | func main() { 81 | mux := &http.ServeMux{} 82 | mux.HandleFunc("/ws", onWebsocket) 83 | 84 | proxyServer = nbhttp.NewEngine(nbhttp.Config{ 85 | Network: "tcp", 86 | Addrs: []string{proxyServerAddr}, 87 | Handler: mux, 88 | }) 89 | 90 | err := proxyServer.Start() 91 | if err != nil { 92 | fmt.Printf("nbio.Start failed: %v\n", err) 93 | return 94 | } 95 | 96 | interrupt := make(chan os.Signal, 1) 97 | signal.Notify(interrupt, os.Interrupt) 98 | <-interrupt 99 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 100 | defer cancel() 101 | proxyServer.Shutdown(ctx) 102 | } 103 | -------------------------------------------------------------------------------- /websocket_proxy/proxy_ws2tcp/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net" 8 | "net/http" 9 | "os" 10 | "os/signal" 11 | "time" 12 | 13 | "github.com/lesismal/nbio" 14 | "github.com/lesismal/nbio/nbhttp" 15 | "github.com/lesismal/nbio/nbhttp/websocket" 16 | ) 17 | 18 | var ( 19 | proxyServerAddr = "localhost:8888" 20 | 21 | appServerAddr = "ws://localhost:9999/ws" 22 | 23 | proxyServer *nbhttp.Engine 24 | ) 25 | 26 | func newUpgrader() *websocket.Upgrader { 27 | u := websocket.NewUpgrader() 28 | return u 29 | } 30 | 31 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 32 | // step 1: authenticated with http request infomations 33 | 34 | // step 2: make sure to dial before Upgrade this conn to websocket, 35 | // because if we Upgrade first, the client may have received the ws handshake response then the client will begin to send data to us. 36 | // if we receive the data before we have changed the conn's data handler, the conn will parse the ws frames, then, everything goes wrong. 37 | dialer := &websocket.Dialer{ 38 | Engine: proxyServer, 39 | Upgrader: newUpgrader(), 40 | DialTimeout: time.Second * 3, 41 | } 42 | dstConn, _, err := dialer.Dial(appServerAddr, nil) 43 | if err != nil { 44 | log.Printf("Dial failed: %v, %v", appServerAddr, err) 45 | w.WriteHeader(http.StatusBadGateway) 46 | return 47 | } 48 | 49 | upgrader := newUpgrader() 50 | srcConn, err := upgrader.Upgrade(w, r, nil) 51 | if err != nil { 52 | panic(err) 53 | } 54 | 55 | // step 3: get tcp conns from both src and dst ws conn 56 | srcNBConn, _ := srcConn.Conn.(*nbio.Conn) 57 | dstNBConn, _ := dstConn.Conn.(*nbio.Conn) 58 | 59 | // step 4: set sessions and data handler for tcp conns 60 | srcNBConn.SetSession(dstNBConn) 61 | dstNBConn.SetSession(srcNBConn) 62 | srcNBConn.OnData(func(c *nbio.Conn, data []byte) { 63 | dstNBConn.Write(data) 64 | }) 65 | dstNBConn.OnData(func(c *nbio.Conn, data []byte) { 66 | srcNBConn.Write(data) 67 | }) 68 | } 69 | 70 | func main() { 71 | mux := &http.ServeMux{} 72 | mux.HandleFunc("/ws", onWebsocket) 73 | 74 | proxyServer = nbhttp.NewEngine(nbhttp.Config{ 75 | Network: "tcp", 76 | Addrs: []string{proxyServerAddr}, 77 | Handler: mux, 78 | }) 79 | 80 | // close peer 81 | proxyServer.OnClose(func(c net.Conn, err error) { 82 | session := c.(*nbio.Conn).Session() 83 | if session != nil { 84 | if nbc, ok := session.(*nbio.Conn); ok { 85 | nbc.Close() 86 | } 87 | } 88 | }) 89 | 90 | err := proxyServer.Start() 91 | if err != nil { 92 | fmt.Printf("nbio.Start failed: %v\n", err) 93 | return 94 | } 95 | 96 | interrupt := make(chan os.Signal, 1) 97 | signal.Notify(interrupt, os.Interrupt) 98 | <-interrupt 99 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 100 | defer cancel() 101 | proxyServer.Shutdown(ctx) 102 | } 103 | -------------------------------------------------------------------------------- /websocket_tls/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "errors" 7 | "flag" 8 | "io" 9 | "log" 10 | "net/url" 11 | "sync" 12 | "time" 13 | 14 | "github.com/gorilla/websocket" 15 | ) 16 | 17 | var addr = flag.String("addr", "localhost:8888", "http service address") 18 | var mesgLen = flag.Int("message-len", 4*1048576, "length of message sent") 19 | var clients = flag.Int("clients", 1, "number of clients to simulate") 20 | var print = flag.Bool("print", false, "stdout input and output") 21 | 22 | func main() { 23 | flag.Parse() 24 | 25 | u := url.URL{Scheme: "wss", Host: *addr, Path: "/wss"} 26 | log.Printf("connecting to %s", u.String()) 27 | 28 | tlsConfig := &tls.Config{ 29 | InsecureSkipVerify: true, 30 | } 31 | dialer := &websocket.Dialer{ 32 | TLSClientConfig: tlsConfig, 33 | } 34 | waitGroup := sync.WaitGroup{} 35 | 36 | waitGroup.Add(*clients) 37 | 38 | text := make([]byte, *mesgLen, *mesgLen) 39 | for i := 0; i < *mesgLen; i++ { 40 | text[i] = 'A' 41 | } 42 | for i := 0; i < *clients; i++ { 43 | go func() { 44 | defer waitGroup.Done() 45 | c, _, err := dialer.Dial(u.String(), nil) 46 | if err != nil { 47 | log.Fatal("dial:", err) 48 | } 49 | defer c.Close() 50 | 51 | for { 52 | err := c.WriteMessage(websocket.TextMessage, text) 53 | if err != nil { 54 | log.Fatalf("write: %v", err) 55 | return 56 | } 57 | log.Println("wrote") 58 | if *print { 59 | log.Println("write:", text) 60 | } 61 | 62 | _, reader, err := c.NextReader() 63 | if err != nil { 64 | log.Println("read:", err) 65 | return 66 | } 67 | var message []byte 68 | line := make([]byte, 65535) 69 | i := 0 70 | for { 71 | log.Printf("pre read") 72 | l, err := reader.Read(line) 73 | log.Printf("post read") 74 | if err != nil { 75 | if errors.Is(err, io.EOF) { 76 | break 77 | } 78 | log.Fatalf("error while reading %s", err) 79 | } 80 | message = append(message, line[:l]...) 81 | log.Printf("read %d %d %d", i, l, len(message)) 82 | i++ 83 | 84 | } 85 | log.Println("read:", len(message)) 86 | res := bytes.Compare(message, text) 87 | if res != 0 { 88 | log.Fatalf("message != text: at offset %d, %v\n", res, message[res:]) 89 | } else { 90 | if *print { 91 | log.Println("read :", string(message)) 92 | } 93 | log.Println("good") 94 | } 95 | time.Sleep(time.Millisecond * 500) 96 | } 97 | }() 98 | } 99 | waitGroup.Wait() 100 | } 101 | -------------------------------------------------------------------------------- /websocket_tls/client_macfoundation/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clang -o macws main.mm -framework Foundation -framework Security --std=c++11 -lstdc++ 4 | 5 | -------------------------------------------------------------------------------- /websocket_tls/client_macfoundation/input.txt: -------------------------------------------------------------------------------- 1 | Hello world Hello worldHello worldHello worldHello worldHello world -------------------------------------------------------------------------------- /websocket_tls/client_macfoundation/run.sh: -------------------------------------------------------------------------------- 1 | ./macws -u wss://localhost:8888/wss -m input.txt 2 | -------------------------------------------------------------------------------- /websocket_tls/client_websocketpp/Makefile: -------------------------------------------------------------------------------- 1 | all: client 2 | 3 | websocketpp: 4 | git clone https://github.com/zaphoyd/websocketpp 5 | 6 | client: websocketpp client.cpp 7 | g++ -std=c++11 -L /usr/local/opt/openssl@1.1/lib -l ssl -lcrypto -lboost_system -lboost_chrono -lboost_random -I /usr/local/opt/openssl@1.1/include -I websocketpp -o $@ client.cpp 8 | 9 | test: client 10 | ./client wss://echo.websocket.org 11 | 12 | test_local: client 13 | ./client 14 | 15 | .PHONY: all test test_local -------------------------------------------------------------------------------- /websocket_tls/client_websocketpp/client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | typedef websocketpp::client client; 8 | 9 | using websocketpp::lib::placeholders::_1; 10 | using websocketpp::lib::placeholders::_2; 11 | using websocketpp::lib::bind; 12 | 13 | // pull out the type of messages sent by our config 14 | typedef websocketpp::config::asio_client::message_type::ptr message_ptr; 15 | 16 | void sendFunc(client* c,websocketpp::connection_hdl hdl) { 17 | websocketpp::lib::error_code ec; 18 | char *in = "Be careful when adding or removing oneof fields. If checking the value of a oneof returns None/NOT_SET, it could mean that the oneof has not been set or it has been set to a field in a different version of the oneof. There is no way to tell the difference, since there's no way to know if an unknown field on the wire is a member of the oneof.hello world"; 19 | c->send(hdl, in, strlen(in), websocketpp::frame::opcode::binary, ec); 20 | if (ec) { 21 | std::cout << "Echo failed because: " << ec.message() << std::endl; 22 | } 23 | } 24 | // This message handler will be invoked once for each incoming message. It 25 | // prints the message and then sends a copy of the message back to the server. 26 | void on_message(client* c, websocketpp::connection_hdl hdl, message_ptr msg) { 27 | std::cout << "on_message called with hdl: " << hdl.lock().get() 28 | << " and message: " << msg->get_payload() 29 | << std::endl; 30 | 31 | 32 | websocketpp::lib::error_code ec; 33 | 34 | c->send(hdl, msg->get_payload(), msg->get_opcode(), ec); 35 | if (ec) { 36 | std::cout << "Echo failed because: " << ec.message() << std::endl; 37 | } 38 | } 39 | 40 | int main(int argc, char* argv[]) { 41 | // Create a client endpoint 42 | client c; 43 | c.set_tls_init_handler([&c](websocketpp::connection_hdl){ 44 | return websocketpp::lib::make_shared(boost::asio::ssl::context::tlsv12); 45 | }); 46 | 47 | std::string uri = "wss://localhost:8888/wss"; 48 | 49 | if (argc == 2) { 50 | uri = argv[1]; 51 | } 52 | 53 | try { 54 | // Set logging to be pretty verbose (everything except message payloads) 55 | c.set_access_channels(websocketpp::log::alevel::all); 56 | c.clear_access_channels(websocketpp::log::alevel::frame_payload); 57 | 58 | // Initialize ASIO 59 | c.init_asio(); 60 | 61 | // Register our message handler 62 | c.set_message_handler(bind(&on_message,&c,::_1,::_2)); 63 | c.set_open_handler(bind(&sendFunc,&c,::_1)); 64 | 65 | 66 | websocketpp::lib::error_code ec; 67 | client::connection_ptr con = c.get_connection(uri, ec); 68 | if (ec) { 69 | std::cout << "could not create connection because: " << ec.message() << std::endl; 70 | return 0; 71 | } 72 | 73 | // Note that connect here only requests a connection. No network messages are 74 | // exchanged until the event loop starts running in the next line. 75 | c.connect(con); 76 | 77 | // Start the ASIO io_service run loop 78 | // this will cause a single connection to be made to the server. c.run() 79 | // will exit when this connection is closed. 80 | c.run(); 81 | } catch (websocketpp::exception const & e) { 82 | std::cout << e.what() << std::endl; 83 | } 84 | } -------------------------------------------------------------------------------- /websocket_tls/nbioclient/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "net/url" 9 | "os" 10 | "os/signal" 11 | "time" 12 | 13 | "github.com/lesismal/nbio/nbhttp" 14 | "github.com/lesismal/nbio/nbhttp/websocket" 15 | 16 | "github.com/lesismal/llib/std/crypto/tls" 17 | ) 18 | 19 | var ( 20 | clients = flag.Int("clients", 1, "number of clients") 21 | floodLargeMessage = flag.Bool("flood", false, "flood server with large messages") 22 | noEcho = flag.Bool("no-echo", false, "disables echo server message") 23 | connectedClients chan *websocket.Conn 24 | 25 | upgrader = newUpgrader() 26 | ) 27 | 28 | func newUpgrader() *websocket.Upgrader { 29 | u := websocket.NewUpgrader() 30 | u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 31 | // echo 32 | if !*noEcho { 33 | time.AfterFunc(time.Second, func() { 34 | c.WriteMessage(messageType, data) 35 | }) 36 | log.Println("onEcho:", string(data)) 37 | } 38 | connectedClients <- c 39 | }) 40 | 41 | u.OnClose(func(c *websocket.Conn, err error) { 42 | }) 43 | 44 | return u 45 | } 46 | 47 | func main() { 48 | flag.Parse() 49 | engine := nbhttp.NewEngine(nbhttp.Config{}) 50 | err := engine.Start() 51 | if err != nil { 52 | fmt.Printf("nbio.Start failed: %v\n", err) 53 | return 54 | } 55 | 56 | tlsConfig := &tls.Config{ 57 | InsecureSkipVerify: true, 58 | } 59 | 60 | connectedClients = make(chan *websocket.Conn, *clients) 61 | for i := 0; i < *clients; i++ { 62 | u := url.URL{Scheme: "wss", Host: "localhost:8888", Path: "/wss"} 63 | dialer := &websocket.Dialer{ 64 | Engine: engine, 65 | Upgrader: upgrader, 66 | DialTimeout: time.Second * 3, 67 | TLSClientConfig: tlsConfig, 68 | } 69 | c, _, err := dialer.Dial(u.String(), nil) 70 | if err != nil { 71 | panic(fmt.Errorf("dial: %v", err)) 72 | } 73 | connectedClients <- c 74 | } 75 | 76 | if *floodLargeMessage { 77 | payload := make([]byte, 1024*1024) 78 | for i := 0; i < 100; i++ { 79 | go func() { 80 | for { 81 | select { 82 | case c := <-connectedClients: 83 | c.WriteMessage(websocket.BinaryMessage, payload) 84 | } 85 | } 86 | }() 87 | 88 | } 89 | } 90 | 91 | interrupt := make(chan os.Signal, 1) 92 | signal.Notify(interrupt, os.Interrupt) 93 | <-interrupt 94 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 95 | defer cancel() 96 | engine.Shutdown(ctx) 97 | } 98 | -------------------------------------------------------------------------------- /websocket_tls/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "time" 11 | 12 | "github.com/lesismal/llib/std/crypto/tls" 13 | "github.com/lesismal/nbio/nbhttp" 14 | "github.com/lesismal/nbio/nbhttp/websocket" 15 | ) 16 | 17 | var ( 18 | engine *nbhttp.Engine 19 | print = flag.Bool("print", false, "stdout output of echoed data") 20 | 21 | upgrader = newUpgrader() 22 | ) 23 | 24 | func newUpgrader() *websocket.Upgrader { 25 | u := websocket.NewUpgrader() 26 | u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 27 | // echo 28 | c.WriteMessage(messageType, data) 29 | if *print { 30 | fmt.Println("OnMessage:", messageType, string(data)) 31 | } 32 | c.SetReadDeadline(time.Now().Add(nbhttp.DefaultKeepaliveTime)) 33 | }) 34 | u.OnClose(func(c *websocket.Conn, err error) { 35 | if *print { 36 | fmt.Println("OnClose:", c.RemoteAddr().String(), err) 37 | } 38 | }) 39 | 40 | return u 41 | } 42 | 43 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 44 | // time.Sleep(time.Second * 5) 45 | conn, err := upgrader.Upgrade(w, r, nil) 46 | if err != nil { 47 | panic(err) 48 | } 49 | if *print { 50 | fmt.Println("OnOpen:", conn.RemoteAddr().String()) 51 | } 52 | } 53 | 54 | func main() { 55 | flag.Parse() 56 | 57 | cert, err := tls.X509KeyPair(rsaCertPEM, rsaKeyPEM) 58 | if err != nil { 59 | log.Fatalf("tls.X509KeyPair failed: %v", err) 60 | } 61 | tlsConfig := &tls.Config{ 62 | Certificates: []tls.Certificate{cert}, 63 | InsecureSkipVerify: true, 64 | } 65 | 66 | mux := &http.ServeMux{} 67 | mux.HandleFunc("/wss", onWebsocket) 68 | 69 | engine = nbhttp.NewEngine(nbhttp.Config{ 70 | Network: "tcp", 71 | AddrsTLS: []string{"localhost:8888"}, 72 | TLSConfig: tlsConfig, 73 | Handler: mux, 74 | }) 75 | 76 | err = engine.Start() 77 | if err != nil { 78 | fmt.Printf("nbio.Start failed: %v\n", err) 79 | return 80 | } 81 | defer engine.Stop() 82 | 83 | interrupt := make(chan os.Signal, 1) 84 | signal.Notify(interrupt, os.Interrupt) 85 | <-interrupt 86 | log.Println("exit") 87 | } 88 | 89 | var rsaCertPEM = []byte(`-----BEGIN CERTIFICATE----- 90 | MIIDazCCAlOgAwIBAgIUJeohtgk8nnt8ofratXJg7kUJsI4wDQYJKoZIhvcNAQEL 91 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 92 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMDcwODIyNThaFw0zMDEy 93 | MDUwODIyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 94 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 95 | AQUAA4IBDwAwggEKAoIBAQCy+ZrIvwwiZv4bPmvKx/637ltZLwfgh3ouiEaTchGu 96 | IQltthkqINHxFBqqJg44TUGHWthlrq6moQuKnWNjIsEc6wSD1df43NWBLgdxbPP0 97 | x4tAH9pIJU7TQqbznjDBhzRbUjVXBIcn7bNknY2+5t784pPF9H1v7h8GqTWpNH9l 98 | cz/v+snoqm9HC+qlsFLa4A3X9l5v05F1uoBfUALlP6bWyjHAfctpiJkoB9Yw1TJa 99 | gpq7E50kfttwfKNkkAZIbib10HugkMoQJAs2EsGkje98druIl8IXmuvBIF6nZHuM 100 | lt3UIZjS9RwPPLXhRHt1P0mR7BoBcOjiHgtSEs7Wk+j7AgMBAAGjUzBRMB0GA1Ud 101 | DgQWBBQdheJv73XSOhgMQtkwdYPnfO02+TAfBgNVHSMEGDAWgBQdheJv73XSOhgM 102 | QtkwdYPnfO02+TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf 103 | SKVNMdmBpD9m53kCrguo9iKQqmhnI0WLkpdWszc/vBgtpOE5ENOfHGAufHZve871 104 | 2fzTXrgR0TF6UZWsQOqCm5Oh3URsCdXWewVMKgJ3DCii6QJ0MnhSFt6+xZE9C6Hi 105 | WhcywgdR8t/JXKDam6miohW8Rum/IZo5HK9Jz/R9icKDGumcqoaPj/ONvY4EUwgB 106 | irKKB7YgFogBmCtgi30beLVkXgk0GEcAf19lHHtX2Pv/lh3m34li1C9eBm1ca3kk 107 | M2tcQtm1G89NROEjcG92cg+GX3GiWIjbI0jD1wnVy2LCOXMgOVbKfGfVKISFt0b1 108 | DNn00G8C6ttLoGU2snyk 109 | -----END CERTIFICATE----- 110 | `) 111 | 112 | var rsaKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- 113 | MIIEogIBAAKCAQEAsvmayL8MImb+Gz5rysf+t+5bWS8H4Id6LohGk3IRriEJbbYZ 114 | KiDR8RQaqiYOOE1Bh1rYZa6upqELip1jYyLBHOsEg9XX+NzVgS4HcWzz9MeLQB/a 115 | SCVO00Km854wwYc0W1I1VwSHJ+2zZJ2Nvube/OKTxfR9b+4fBqk1qTR/ZXM/7/rJ 116 | 6KpvRwvqpbBS2uAN1/Zeb9ORdbqAX1AC5T+m1soxwH3LaYiZKAfWMNUyWoKauxOd 117 | JH7bcHyjZJAGSG4m9dB7oJDKECQLNhLBpI3vfHa7iJfCF5rrwSBep2R7jJbd1CGY 118 | 0vUcDzy14UR7dT9JkewaAXDo4h4LUhLO1pPo+wIDAQABAoIBAF6yWwekrlL1k7Xu 119 | jTI6J7hCUesaS1yt0iQUzuLtFBXCPS7jjuUPgIXCUWl9wUBhAC8SDjWe+6IGzAiH 120 | xjKKDQuz/iuTVjbDAeTb6exF7b6yZieDswdBVjfJqHR2Wu3LEBTRpo9oQesKhkTS 121 | aFF97rZ3XCD9f/FdWOU5Wr8wm8edFK0zGsZ2N6r57yf1N6ocKlGBLBZ0v1Sc5ShV 122 | 1PVAxeephQvwL5DrOgkArnuAzwRXwJQG78L0aldWY2q6xABQZQb5+ml7H/kyytef 123 | i+uGo3jHKepVALHmdpCGr9Yv+yCElup+ekv6cPy8qcmMBqGMISL1i1FEONxLcKWp 124 | GEJi6QECgYEA3ZPGMdUm3f2spdHn3C+/+xskQpz6efiPYpnqFys2TZD7j5OOnpcP 125 | ftNokA5oEgETg9ExJQ8aOCykseDc/abHerYyGw6SQxmDbyBLmkZmp9O3iMv2N8Pb 126 | Nrn9kQKSr6LXZ3gXzlrDvvRoYUlfWuLSxF4b4PYifkA5AfsdiKkj+5sCgYEAzseF 127 | XDTRKHHJnzxZDDdHQcwA0G9agsNj64BGUEjsAGmDiDyqOZnIjDLRt0O2X3oiIE5S 128 | TXySSEiIkxjfErVJMumLaIwqVvlS4pYKdQo1dkM7Jbt8wKRQdleRXOPPN7msoEUk 129 | Ta9ZsftHVUknPqblz9Uthb5h+sRaxIaE1llqDiECgYATS4oHzuL6k9uT+Qpyzymt 130 | qThoIJljQ7TgxjxvVhD9gjGV2CikQM1Vov1JBigj4Toc0XuxGXaUC7cv0kAMSpi2 131 | Y+VLG+K6ux8J70sGHTlVRgeGfxRq2MBfLKUbGplBeDG/zeJs0tSW7VullSkblgL6 132 | nKNa3LQ2QEt2k7KHswryHwKBgENDxk8bY1q7wTHKiNEffk+aFD25q4DUHMH0JWti 133 | fVsY98+upFU+gG2S7oOmREJE0aser0lDl7Zp2fu34IEOdfRY4p+s0O0gB+Vrl5VB 134 | L+j7r9bzaX6lNQN6MvA7ryHahZxRQaD/xLbQHgFRXbHUyvdTyo4yQ1821qwNclLk 135 | HUrhAoGAUtjR3nPFR4TEHlpTSQQovS8QtGTnOi7s7EzzdPWmjHPATrdLhMA0ezPj 136 | Mr+u5TRncZBIzAZtButlh1AHnpN/qO3P0c0Rbdep3XBc/82JWO8qdb5QvAkxga3X 137 | BpA7MNLxiqss+rCbwf3NbWxEMiDQ2zRwVoafVFys7tjmv6t2Xck= 138 | -----END RSA PRIVATE KEY----- 139 | `) 140 | -------------------------------------------------------------------------------- /websocket_tls/server_manual_frame_assembly/README.md: -------------------------------------------------------------------------------- 1 | if your clients will be sending large websocket messages of fixed size, you probably want to take an approach similar to this example. The problem with nbio and large messages is that as the frames are appended to the message buffer, the message buffers is reallocated (internally by the the golang append message). By using a fixed size buffer pool, you can control the capacity of the message buffer at the time of the first frame and limit how many in flight messages the clients are allowed to send in concurently 2 | -------------------------------------------------------------------------------- /websocket_tls/server_manual_frame_assembly/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | "os" 10 | "os/signal" 11 | "sync/atomic" 12 | "time" 13 | 14 | "github.com/lesismal/llib/std/crypto/tls" 15 | "github.com/lesismal/nbio-examples/fixedbufferpool" 16 | "github.com/lesismal/nbio/nbhttp" 17 | "github.com/lesismal/nbio/nbhttp/websocket" 18 | ) 19 | 20 | var ( 21 | bufferPool = fixedbufferpool.NewFixedBufferPool(2000, 1*1024*1024, time.Second*10) 22 | messagesReceived uint64 23 | bytesReceived uint64 24 | 25 | upgrader = newUpgrader() 26 | ) 27 | 28 | func newUpgrader() *websocket.Upgrader { 29 | u := websocket.NewUpgrader() 30 | 31 | onMessageFunc := func(c *websocket.Conn, data []byte) { 32 | c.WriteMessage(websocket.BinaryMessage, data) 33 | atomic.AddUint64(&messagesReceived, 1) 34 | atomic.AddUint64(&bytesReceived, uint64(len(data))) 35 | } 36 | u.OnDataFrame(func(c *websocket.Conn, messageType websocket.MessageType, fin bool, frameData []byte) { 37 | curBuf := c.Session() 38 | // frame == message 39 | if fin && curBuf == nil { 40 | onMessageFunc(c, frameData) 41 | return 42 | } 43 | if curBuf == nil { 44 | b, err := bufferPool.Get() 45 | if err != nil { 46 | c.WriteMessage(websocket.CloseMessage, []byte(fmt.Sprintf("%v", err))) 47 | return 48 | } 49 | curBuf = b 50 | } 51 | messageDataSoFar := curBuf.([]byte) 52 | if cap(messageDataSoFar) < len(messageDataSoFar)+len(frameData) { 53 | c.WriteMessage(websocket.CloseMessage, []byte("websocket message too large")) 54 | return 55 | } 56 | messageDataSoFar = append(messageDataSoFar, frameData...) 57 | if fin { 58 | onMessageFunc(c, messageDataSoFar) 59 | bufferPool.Put(messageDataSoFar) 60 | c.SetSession(nil) 61 | } else { 62 | c.SetSession(messageDataSoFar) 63 | } 64 | }) 65 | 66 | u.OnClose(func(c *websocket.Conn, err error) { 67 | curBuf := c.Session() 68 | b, _ := curBuf.([]byte) 69 | if b != nil { 70 | bufferPool.Put(b) 71 | } 72 | }) 73 | return u 74 | } 75 | 76 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 77 | // time.Sleep(time.Second * 5) 78 | conn, err := upgrader.Upgrade(w, r, nil) 79 | if err != nil { 80 | panic(err) 81 | } 82 | conn.SetReadDeadline(time.Time{}) 83 | fmt.Println("OnOpen:", conn.RemoteAddr().String()) 84 | } 85 | 86 | func main() { 87 | flag.Parse() 88 | cert, err := tls.X509KeyPair(rsaCertPEM, rsaKeyPEM) 89 | if err != nil { 90 | log.Fatalf("tls.X509KeyPair failed: %v", err) 91 | } 92 | tlsConfig := &tls.Config{ 93 | Certificates: []tls.Certificate{cert}, 94 | InsecureSkipVerify: true, 95 | } 96 | 97 | mux := &http.ServeMux{} 98 | mux.HandleFunc("/wss", onWebsocket) 99 | 100 | svr := nbhttp.NewEngine(nbhttp.Config{ 101 | Network: "tcp", 102 | AddrsTLS: []string{"localhost:8888"}, 103 | TLSConfig: tlsConfig, 104 | ReleaseWebsocketPayload: true, 105 | Handler: mux, 106 | }) 107 | 108 | err = svr.Start() 109 | if err != nil { 110 | fmt.Printf("nbio.Start failed: %v\n", err) 111 | return 112 | } 113 | go func() { 114 | t := time.NewTicker(time.Second * 5) 115 | for { 116 | select { 117 | case <-t.C: 118 | fmt.Printf("%d %d\n", atomic.LoadUint64(&messagesReceived), atomic.LoadUint64(&bytesReceived)) 119 | } 120 | } 121 | }() 122 | 123 | interrupt := make(chan os.Signal, 1) 124 | signal.Notify(interrupt, os.Interrupt) 125 | <-interrupt 126 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) 127 | defer cancel() 128 | svr.Shutdown(ctx) 129 | } 130 | 131 | var rsaCertPEM = []byte(`-----BEGIN CERTIFICATE----- 132 | MIIDazCCAlOgAwIBAgIUJeohtgk8nnt8ofratXJg7kUJsI4wDQYJKoZIhvcNAQEL 133 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 134 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMDcwODIyNThaFw0zMDEy 135 | MDUwODIyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 136 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 137 | AQUAA4IBDwAwggEKAoIBAQCy+ZrIvwwiZv4bPmvKx/637ltZLwfgh3ouiEaTchGu 138 | IQltthkqINHxFBqqJg44TUGHWthlrq6moQuKnWNjIsEc6wSD1df43NWBLgdxbPP0 139 | x4tAH9pIJU7TQqbznjDBhzRbUjVXBIcn7bNknY2+5t784pPF9H1v7h8GqTWpNH9l 140 | cz/v+snoqm9HC+qlsFLa4A3X9l5v05F1uoBfUALlP6bWyjHAfctpiJkoB9Yw1TJa 141 | gpq7E50kfttwfKNkkAZIbib10HugkMoQJAs2EsGkje98druIl8IXmuvBIF6nZHuM 142 | lt3UIZjS9RwPPLXhRHt1P0mR7BoBcOjiHgtSEs7Wk+j7AgMBAAGjUzBRMB0GA1Ud 143 | DgQWBBQdheJv73XSOhgMQtkwdYPnfO02+TAfBgNVHSMEGDAWgBQdheJv73XSOhgM 144 | QtkwdYPnfO02+TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf 145 | SKVNMdmBpD9m53kCrguo9iKQqmhnI0WLkpdWszc/vBgtpOE5ENOfHGAufHZve871 146 | 2fzTXrgR0TF6UZWsQOqCm5Oh3URsCdXWewVMKgJ3DCii6QJ0MnhSFt6+xZE9C6Hi 147 | WhcywgdR8t/JXKDam6miohW8Rum/IZo5HK9Jz/R9icKDGumcqoaPj/ONvY4EUwgB 148 | irKKB7YgFogBmCtgi30beLVkXgk0GEcAf19lHHtX2Pv/lh3m34li1C9eBm1ca3kk 149 | M2tcQtm1G89NROEjcG92cg+GX3GiWIjbI0jD1wnVy2LCOXMgOVbKfGfVKISFt0b1 150 | DNn00G8C6ttLoGU2snyk 151 | -----END CERTIFICATE----- 152 | `) 153 | 154 | var rsaKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- 155 | MIIEogIBAAKCAQEAsvmayL8MImb+Gz5rysf+t+5bWS8H4Id6LohGk3IRriEJbbYZ 156 | KiDR8RQaqiYOOE1Bh1rYZa6upqELip1jYyLBHOsEg9XX+NzVgS4HcWzz9MeLQB/a 157 | SCVO00Km854wwYc0W1I1VwSHJ+2zZJ2Nvube/OKTxfR9b+4fBqk1qTR/ZXM/7/rJ 158 | 6KpvRwvqpbBS2uAN1/Zeb9ORdbqAX1AC5T+m1soxwH3LaYiZKAfWMNUyWoKauxOd 159 | JH7bcHyjZJAGSG4m9dB7oJDKECQLNhLBpI3vfHa7iJfCF5rrwSBep2R7jJbd1CGY 160 | 0vUcDzy14UR7dT9JkewaAXDo4h4LUhLO1pPo+wIDAQABAoIBAF6yWwekrlL1k7Xu 161 | jTI6J7hCUesaS1yt0iQUzuLtFBXCPS7jjuUPgIXCUWl9wUBhAC8SDjWe+6IGzAiH 162 | xjKKDQuz/iuTVjbDAeTb6exF7b6yZieDswdBVjfJqHR2Wu3LEBTRpo9oQesKhkTS 163 | aFF97rZ3XCD9f/FdWOU5Wr8wm8edFK0zGsZ2N6r57yf1N6ocKlGBLBZ0v1Sc5ShV 164 | 1PVAxeephQvwL5DrOgkArnuAzwRXwJQG78L0aldWY2q6xABQZQb5+ml7H/kyytef 165 | i+uGo3jHKepVALHmdpCGr9Yv+yCElup+ekv6cPy8qcmMBqGMISL1i1FEONxLcKWp 166 | GEJi6QECgYEA3ZPGMdUm3f2spdHn3C+/+xskQpz6efiPYpnqFys2TZD7j5OOnpcP 167 | ftNokA5oEgETg9ExJQ8aOCykseDc/abHerYyGw6SQxmDbyBLmkZmp9O3iMv2N8Pb 168 | Nrn9kQKSr6LXZ3gXzlrDvvRoYUlfWuLSxF4b4PYifkA5AfsdiKkj+5sCgYEAzseF 169 | XDTRKHHJnzxZDDdHQcwA0G9agsNj64BGUEjsAGmDiDyqOZnIjDLRt0O2X3oiIE5S 170 | TXySSEiIkxjfErVJMumLaIwqVvlS4pYKdQo1dkM7Jbt8wKRQdleRXOPPN7msoEUk 171 | Ta9ZsftHVUknPqblz9Uthb5h+sRaxIaE1llqDiECgYATS4oHzuL6k9uT+Qpyzymt 172 | qThoIJljQ7TgxjxvVhD9gjGV2CikQM1Vov1JBigj4Toc0XuxGXaUC7cv0kAMSpi2 173 | Y+VLG+K6ux8J70sGHTlVRgeGfxRq2MBfLKUbGplBeDG/zeJs0tSW7VullSkblgL6 174 | nKNa3LQ2QEt2k7KHswryHwKBgENDxk8bY1q7wTHKiNEffk+aFD25q4DUHMH0JWti 175 | fVsY98+upFU+gG2S7oOmREJE0aser0lDl7Zp2fu34IEOdfRY4p+s0O0gB+Vrl5VB 176 | L+j7r9bzaX6lNQN6MvA7ryHahZxRQaD/xLbQHgFRXbHUyvdTyo4yQ1821qwNclLk 177 | HUrhAoGAUtjR3nPFR4TEHlpTSQQovS8QtGTnOi7s7EzzdPWmjHPATrdLhMA0ezPj 178 | Mr+u5TRncZBIzAZtButlh1AHnpN/qO3P0c0Rbdep3XBc/82JWO8qdb5QvAkxga3X 179 | BpA7MNLxiqss+rCbwf3NbWxEMiDQ2zRwVoafVFys7tjmv6t2Xck= 180 | -----END RSA PRIVATE KEY----- 181 | `) 182 | -------------------------------------------------------------------------------- /websocket_tls/sticky/client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "crypto/tls" 7 | "flag" 8 | "log" 9 | "net/url" 10 | "sort" 11 | "sync" 12 | 13 | "github.com/gorilla/websocket" 14 | "github.com/lesismal/nbio/taskpool" 15 | ) 16 | 17 | var addr = flag.String("addr", "localhost:8888", "http service address") 18 | 19 | func main() { 20 | flag.Parse() 21 | 22 | u := url.URL{Scheme: "wss", Host: *addr, Path: "/wss"} 23 | log.Printf("connecting to %s", u.String()) 24 | 25 | tlsConfig := &tls.Config{ 26 | InsecureSkipVerify: true, 27 | } 28 | dialer := &websocket.Dialer{ 29 | TLSClientConfig: tlsConfig, 30 | } 31 | 32 | nn := []int{0, 1, 2, 128, 65535, 65537} 33 | for i := 1; i <= 130; i++ { 34 | nn = append(nn, 512*i) 35 | } 36 | clientNum := 4 37 | pool := taskpool.New(clientNum, 1024) 38 | wg := sync.WaitGroup{} 39 | success := []int{} 40 | 41 | conns := []*websocket.Conn{} 42 | for i := 0; i < clientNum; i++ { 43 | c, _, err := dialer.Dial(u.String(), nil) 44 | if err != nil { 45 | log.Fatal("dial:", err) 46 | } 47 | conns = append(conns, c) 48 | defer c.Close() 49 | } 50 | 51 | for i, l := range nn { 52 | wg.Add(1) 53 | n := l 54 | idx := i % clientNum 55 | pool.Go(func() { 56 | c := conns[idx] 57 | defer wg.Done() 58 | data := make([]byte, n) 59 | for j := 0; j < n; j++ { 60 | data[j] = 'a' + byte(j%26) 61 | } 62 | 63 | err := c.WriteMessage(websocket.TextMessage, data) 64 | if err != nil { 65 | log.Fatalf("write length: %v, %v", n, err) 66 | return 67 | } 68 | 69 | typ, message, err := c.ReadMessage() 70 | if err != nil { 71 | log.Fatalf("read length: %v, %v", n, err) 72 | return 73 | } 74 | if typ != websocket.TextMessage || !bytes.Equal(message, data) { 75 | log.Fatalf("length: %v, typ: %v != %v, message != text: %v, %v", n, typ, websocket.TextMessage, len(message), string(message)) 76 | } 77 | 78 | nr, err := rand.Read(data) 79 | if err != nil || nr != n { 80 | log.Fatalf("rand read failed length: %v, %v, %v", n, nr, err) 81 | } 82 | err = c.WriteMessage(websocket.BinaryMessage, data) 83 | if err != nil { 84 | log.Fatalf("write length: %v, %v", n, err) 85 | return 86 | } 87 | 88 | typ, message, err = c.ReadMessage() 89 | if err != nil || nr != n { 90 | log.Fatalf("ReadMessage: %v, %v, %v", typ, message, err) 91 | } 92 | if typ != websocket.BinaryMessage || !bytes.Equal(message, data) { 93 | log.Fatalf("length: %v, typ: %v != %v, message != text: %v, %v", n, typ, websocket.TextMessage, len(message), string(message)) 94 | } 95 | success = append(success, n) 96 | log.Println("success with data length:", n) 97 | }) 98 | } 99 | wg.Wait() 100 | sort.Ints(success) 101 | log.Println("success all with data length:", success) 102 | } 103 | -------------------------------------------------------------------------------- /websocket_tls/sticky/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "time" 11 | 12 | "github.com/lesismal/llib/std/crypto/tls" 13 | "github.com/lesismal/nbio-examples/sticky/proxy" 14 | "github.com/lesismal/nbio/nbhttp" 15 | "github.com/lesismal/nbio/nbhttp/websocket" 16 | ) 17 | 18 | var ( 19 | engine *nbhttp.Engine 20 | 21 | upgrader = websocket.NewUpgrader() 22 | ) 23 | 24 | func init() { 25 | upgrader.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { 26 | c.WriteMessage(messageType, data) 27 | }) 28 | upgrader.OnClose(func(c *websocket.Conn, err error) { 29 | fmt.Println("OnClose:", c.RemoteAddr().String(), err) 30 | }) 31 | } 32 | 33 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 34 | conn, err := upgrader.Upgrade(w, r, nil) 35 | if err != nil { 36 | panic(err) 37 | } 38 | fmt.Println("OnOpen:", conn.RemoteAddr().String()) 39 | } 40 | 41 | func main() { 42 | go proxy.Run("localhost:8888", "localhost:9999", time.Nanosecond, func(max int) int { 43 | n := rand.Intn(max) % max 44 | if n == 0 { 45 | n = 1 46 | } 47 | return n 48 | }) 49 | 50 | cert, err := tls.X509KeyPair(rsaCertPEM, rsaKeyPEM) 51 | if err != nil { 52 | log.Fatalf("tls.X509KeyPair failed: %v", err) 53 | } 54 | tlsConfig := &tls.Config{ 55 | Certificates: []tls.Certificate{cert}, 56 | InsecureSkipVerify: true, 57 | } 58 | 59 | mux := &http.ServeMux{} 60 | mux.HandleFunc("/wss", onWebsocket) 61 | 62 | engine = nbhttp.NewEngine(nbhttp.Config{ 63 | Network: "tcp", 64 | AddrsTLS: []string{"localhost:8888"}, 65 | TLSConfig: tlsConfig, 66 | Handler: mux, 67 | }) 68 | 69 | err = engine.Start() 70 | if err != nil { 71 | fmt.Printf("nbio.Start failed: %v\n", err) 72 | return 73 | } 74 | defer engine.Stop() 75 | 76 | interrupt := make(chan os.Signal, 1) 77 | signal.Notify(interrupt, os.Interrupt) 78 | <-interrupt 79 | log.Println("exit") 80 | } 81 | 82 | var rsaCertPEM = []byte(`-----BEGIN CERTIFICATE----- 83 | MIIDazCCAlOgAwIBAgIUJeohtgk8nnt8ofratXJg7kUJsI4wDQYJKoZIhvcNAQEL 84 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 85 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMDcwODIyNThaFw0zMDEy 86 | MDUwODIyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 87 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 88 | AQUAA4IBDwAwggEKAoIBAQCy+ZrIvwwiZv4bPmvKx/637ltZLwfgh3ouiEaTchGu 89 | IQltthkqINHxFBqqJg44TUGHWthlrq6moQuKnWNjIsEc6wSD1df43NWBLgdxbPP0 90 | x4tAH9pIJU7TQqbznjDBhzRbUjVXBIcn7bNknY2+5t784pPF9H1v7h8GqTWpNH9l 91 | cz/v+snoqm9HC+qlsFLa4A3X9l5v05F1uoBfUALlP6bWyjHAfctpiJkoB9Yw1TJa 92 | gpq7E50kfttwfKNkkAZIbib10HugkMoQJAs2EsGkje98druIl8IXmuvBIF6nZHuM 93 | lt3UIZjS9RwPPLXhRHt1P0mR7BoBcOjiHgtSEs7Wk+j7AgMBAAGjUzBRMB0GA1Ud 94 | DgQWBBQdheJv73XSOhgMQtkwdYPnfO02+TAfBgNVHSMEGDAWgBQdheJv73XSOhgM 95 | QtkwdYPnfO02+TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf 96 | SKVNMdmBpD9m53kCrguo9iKQqmhnI0WLkpdWszc/vBgtpOE5ENOfHGAufHZve871 97 | 2fzTXrgR0TF6UZWsQOqCm5Oh3URsCdXWewVMKgJ3DCii6QJ0MnhSFt6+xZE9C6Hi 98 | WhcywgdR8t/JXKDam6miohW8Rum/IZo5HK9Jz/R9icKDGumcqoaPj/ONvY4EUwgB 99 | irKKB7YgFogBmCtgi30beLVkXgk0GEcAf19lHHtX2Pv/lh3m34li1C9eBm1ca3kk 100 | M2tcQtm1G89NROEjcG92cg+GX3GiWIjbI0jD1wnVy2LCOXMgOVbKfGfVKISFt0b1 101 | DNn00G8C6ttLoGU2snyk 102 | -----END CERTIFICATE----- 103 | `) 104 | 105 | var rsaKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- 106 | MIIEogIBAAKCAQEAsvmayL8MImb+Gz5rysf+t+5bWS8H4Id6LohGk3IRriEJbbYZ 107 | KiDR8RQaqiYOOE1Bh1rYZa6upqELip1jYyLBHOsEg9XX+NzVgS4HcWzz9MeLQB/a 108 | SCVO00Km854wwYc0W1I1VwSHJ+2zZJ2Nvube/OKTxfR9b+4fBqk1qTR/ZXM/7/rJ 109 | 6KpvRwvqpbBS2uAN1/Zeb9ORdbqAX1AC5T+m1soxwH3LaYiZKAfWMNUyWoKauxOd 110 | JH7bcHyjZJAGSG4m9dB7oJDKECQLNhLBpI3vfHa7iJfCF5rrwSBep2R7jJbd1CGY 111 | 0vUcDzy14UR7dT9JkewaAXDo4h4LUhLO1pPo+wIDAQABAoIBAF6yWwekrlL1k7Xu 112 | jTI6J7hCUesaS1yt0iQUzuLtFBXCPS7jjuUPgIXCUWl9wUBhAC8SDjWe+6IGzAiH 113 | xjKKDQuz/iuTVjbDAeTb6exF7b6yZieDswdBVjfJqHR2Wu3LEBTRpo9oQesKhkTS 114 | aFF97rZ3XCD9f/FdWOU5Wr8wm8edFK0zGsZ2N6r57yf1N6ocKlGBLBZ0v1Sc5ShV 115 | 1PVAxeephQvwL5DrOgkArnuAzwRXwJQG78L0aldWY2q6xABQZQb5+ml7H/kyytef 116 | i+uGo3jHKepVALHmdpCGr9Yv+yCElup+ekv6cPy8qcmMBqGMISL1i1FEONxLcKWp 117 | GEJi6QECgYEA3ZPGMdUm3f2spdHn3C+/+xskQpz6efiPYpnqFys2TZD7j5OOnpcP 118 | ftNokA5oEgETg9ExJQ8aOCykseDc/abHerYyGw6SQxmDbyBLmkZmp9O3iMv2N8Pb 119 | Nrn9kQKSr6LXZ3gXzlrDvvRoYUlfWuLSxF4b4PYifkA5AfsdiKkj+5sCgYEAzseF 120 | XDTRKHHJnzxZDDdHQcwA0G9agsNj64BGUEjsAGmDiDyqOZnIjDLRt0O2X3oiIE5S 121 | TXySSEiIkxjfErVJMumLaIwqVvlS4pYKdQo1dkM7Jbt8wKRQdleRXOPPN7msoEUk 122 | Ta9ZsftHVUknPqblz9Uthb5h+sRaxIaE1llqDiECgYATS4oHzuL6k9uT+Qpyzymt 123 | qThoIJljQ7TgxjxvVhD9gjGV2CikQM1Vov1JBigj4Toc0XuxGXaUC7cv0kAMSpi2 124 | Y+VLG+K6ux8J70sGHTlVRgeGfxRq2MBfLKUbGplBeDG/zeJs0tSW7VullSkblgL6 125 | nKNa3LQ2QEt2k7KHswryHwKBgENDxk8bY1q7wTHKiNEffk+aFD25q4DUHMH0JWti 126 | fVsY98+upFU+gG2S7oOmREJE0aser0lDl7Zp2fu34IEOdfRY4p+s0O0gB+Vrl5VB 127 | L+j7r9bzaX6lNQN6MvA7ryHahZxRQaD/xLbQHgFRXbHUyvdTyo4yQ1821qwNclLk 128 | HUrhAoGAUtjR3nPFR4TEHlpTSQQovS8QtGTnOi7s7EzzdPWmjHPATrdLhMA0ezPj 129 | Mr+u5TRncZBIzAZtButlh1AHnpN/qO3P0c0Rbdep3XBc/82JWO8qdb5QvAkxga3X 130 | BpA7MNLxiqss+rCbwf3NbWxEMiDQ2zRwVoafVFys7tjmv6t2Xck= 131 | -----END RSA PRIVATE KEY----- 132 | `) 133 | -------------------------------------------------------------------------------- /websocket_transfer/client_nb/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "log" 7 | "net/url" 8 | "sync" 9 | 10 | "github.com/gorilla/websocket" 11 | ) 12 | 13 | var addr = flag.String("addr", "localhost:8082", "http service address") 14 | var mesgLen = flag.Int("message-len", 1024, "length of message sent") 15 | var clients = flag.Int("clients", 1000, "number of clients to simulate") 16 | 17 | func main() { 18 | flag.Parse() 19 | 20 | u := url.URL{Scheme: "ws", Host: *addr, Path: "/ws"} 21 | log.Printf("connecting to %s", u.String()) 22 | 23 | dialer := &websocket.Dialer{} 24 | wg := sync.WaitGroup{} 25 | 26 | wg.Add(*clients) 27 | 28 | for i := 0; i < *clients; i++ { 29 | go func() { 30 | defer wg.Done() 31 | 32 | text := make([]byte, *mesgLen) 33 | for i := 0; i < *mesgLen; i++ { 34 | text[i] = 'A' 35 | } 36 | 37 | c, _, err := dialer.Dial(u.String(), nil) 38 | if err != nil { 39 | log.Fatal("dial:", err) 40 | } 41 | defer c.Close() 42 | 43 | for { 44 | err := c.WriteMessage(websocket.TextMessage, text) 45 | if err != nil { 46 | log.Fatalf("write: %v", err) 47 | return 48 | } 49 | 50 | _, message, err := c.ReadMessage() 51 | if err != nil { 52 | log.Println("read:", err) 53 | return 54 | } 55 | if !bytes.Equal(message, text) { 56 | panic("not equal") 57 | } 58 | } 59 | }() 60 | } 61 | 62 | wg.Wait() 63 | } 64 | -------------------------------------------------------------------------------- /websocket_transfer/client_std/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "log" 7 | "net/url" 8 | "sync" 9 | 10 | "github.com/gorilla/websocket" 11 | ) 12 | 13 | var addr = flag.String("addr", "localhost:8080", "http service address") 14 | var mesgLen = flag.Int("message-len", 1024, "length of message sent") 15 | var clients = flag.Int("clients", 1000, "number of clients to simulate") 16 | 17 | func main() { 18 | flag.Parse() 19 | 20 | u := url.URL{Scheme: "ws", Host: *addr, Path: "/ws"} 21 | log.Printf("connecting to %s", u.String()) 22 | 23 | dialer := &websocket.Dialer{} 24 | wg := sync.WaitGroup{} 25 | 26 | wg.Add(*clients) 27 | 28 | for i := 0; i < *clients; i++ { 29 | go func() { 30 | defer wg.Done() 31 | 32 | text := make([]byte, *mesgLen) 33 | for i := 0; i < *mesgLen; i++ { 34 | text[i] = 'A' 35 | } 36 | 37 | c, _, err := dialer.Dial(u.String(), nil) 38 | if err != nil { 39 | log.Fatal("dial:", err) 40 | } 41 | defer c.Close() 42 | 43 | for { 44 | err := c.WriteMessage(websocket.TextMessage, text) 45 | if err != nil { 46 | log.Fatalf("write: %v", err) 47 | return 48 | } 49 | 50 | _, message, err := c.ReadMessage() 51 | if err != nil { 52 | log.Println("read:", err) 53 | return 54 | } 55 | if !bytes.Equal(message, text) { 56 | panic("not equal") 57 | } 58 | } 59 | }() 60 | } 61 | 62 | wg.Wait() 63 | } 64 | -------------------------------------------------------------------------------- /websocket_transfer/client_tls_nb/client_tls.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "flag" 7 | "log" 8 | "net/url" 9 | "sync" 10 | 11 | "github.com/gorilla/websocket" 12 | ) 13 | 14 | var addr = flag.String("addr", "localhost:8083", "http service address") 15 | var mesgLen = flag.Int("message-len", 1024, "length of message sent") 16 | var clients = flag.Int("clients", 100, "number of clients to simulate") 17 | 18 | func main() { 19 | flag.Parse() 20 | 21 | u := url.URL{Scheme: "wss", Host: *addr, Path: "/wss"} 22 | log.Printf("connecting to %s", u.String()) 23 | 24 | tlsConfig := &tls.Config{ 25 | InsecureSkipVerify: true, 26 | } 27 | 28 | wg := sync.WaitGroup{} 29 | 30 | wg.Add(*clients) 31 | 32 | for i := 0; i < *clients; i++ { 33 | go func() { 34 | defer wg.Done() 35 | 36 | dialer := &websocket.Dialer{ 37 | TLSClientConfig: tlsConfig, 38 | } 39 | 40 | c, _, err := dialer.Dial(u.String(), nil) 41 | if err != nil { 42 | log.Fatal("dial:", err) 43 | } 44 | defer c.Close() 45 | 46 | text := make([]byte, *mesgLen) 47 | for i := 0; i < *mesgLen; i++ { 48 | text[i] = 'A' 49 | } 50 | 51 | for { 52 | err := c.WriteMessage(websocket.TextMessage, text) 53 | if err != nil { 54 | log.Fatalf("write: %v", err) 55 | return 56 | } 57 | 58 | _, message, err := c.ReadMessage() 59 | if err != nil { 60 | log.Println("read:", err) 61 | return 62 | } 63 | if !bytes.Equal(message, text) { 64 | panic("not equal") 65 | } 66 | } 67 | }() 68 | } 69 | 70 | wg.Wait() 71 | } 72 | -------------------------------------------------------------------------------- /websocket_transfer/client_tls_std/client_tls.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "flag" 7 | "log" 8 | "net/url" 9 | "sync" 10 | 11 | "github.com/gorilla/websocket" 12 | ) 13 | 14 | var addr = flag.String("addr", "localhost:8081", "http service address") 15 | var mesgLen = flag.Int("message-len", 1024, "length of message sent") 16 | var clients = flag.Int("clients", 100, "number of clients to simulate") 17 | 18 | func main() { 19 | flag.Parse() 20 | 21 | u := url.URL{Scheme: "wss", Host: *addr, Path: "/wss"} 22 | log.Printf("connecting to %s", u.String()) 23 | 24 | tlsConfig := &tls.Config{ 25 | InsecureSkipVerify: true, 26 | } 27 | 28 | wg := sync.WaitGroup{} 29 | 30 | wg.Add(*clients) 31 | 32 | for i := 0; i < *clients; i++ { 33 | go func() { 34 | defer wg.Done() 35 | 36 | dialer := &websocket.Dialer{ 37 | TLSClientConfig: tlsConfig, 38 | } 39 | 40 | c, _, err := dialer.Dial(u.String(), nil) 41 | if err != nil { 42 | log.Fatal("dial:", err) 43 | } 44 | defer c.Close() 45 | 46 | text := make([]byte, *mesgLen) 47 | for i := 0; i < *mesgLen; i++ { 48 | text[i] = 'A' 49 | } 50 | 51 | for { 52 | err := c.WriteMessage(websocket.TextMessage, text) 53 | if err != nil { 54 | log.Fatalf("write: %v", err) 55 | return 56 | } 57 | 58 | _, message, err := c.ReadMessage() 59 | if err != nil { 60 | log.Println("read:", err) 61 | return 62 | } 63 | if !bytes.Equal(message, text) { 64 | panic("not equal") 65 | } 66 | } 67 | }() 68 | } 69 | 70 | wg.Wait() 71 | } 72 | -------------------------------------------------------------------------------- /websocket_transfer/server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net" 8 | "net/http" 9 | _ "net/http/pprof" 10 | "os" 11 | "os/signal" 12 | "runtime" 13 | "sync/atomic" 14 | "time" 15 | 16 | "github.com/lesismal/llib/std/crypto/tls" 17 | "github.com/lesismal/nbio/nbhttp" 18 | "github.com/lesismal/nbio/nbhttp/websocket" 19 | ) 20 | 21 | var ( 22 | qps uint64 = 0 23 | total uint64 = 0 24 | 25 | addrTLS = "localhost:8081" 26 | addrNonTLS = "localhost:8080" 27 | addrNBHTTP = "localhost:8082" 28 | addrNBHTTPTLS = "localhost:8083" 29 | 30 | muxHTTP = &http.ServeMux{} 31 | muxNBHTTP = &http.ServeMux{} 32 | nbhttpEngine *nbhttp.Engine 33 | httpServerTLS = &http.Server{} 34 | httpServerNonTLS = &http.Server{} 35 | 36 | upgrader = newUpgrader() 37 | ) 38 | 39 | func init() { 40 | muxHTTP.HandleFunc("/now", onNow) 41 | muxHTTP.HandleFunc("/ws", onWebsocket) 42 | muxHTTP.HandleFunc("/wss", onWebsocket) 43 | 44 | muxNBHTTP.HandleFunc("/now", onNow) 45 | muxNBHTTP.HandleFunc("/ws", onNBWebsocket) 46 | muxNBHTTP.HandleFunc("/wss", onNBWebsocket) 47 | 48 | } 49 | 50 | func newUpgrader() *websocket.Upgrader { 51 | u := websocket.NewUpgrader() 52 | u.Engine = nbhttpEngine 53 | u.BlockingModAsyncWrite = true 54 | n := uint64(0) 55 | u.OnOpen(func(c *websocket.Conn) { 56 | log.Printf("OnOpen: %v", c.RemoteAddr().String()) 57 | }) 58 | u.OnMessage(func(c *websocket.Conn, mt websocket.MessageType, data []byte) { 59 | atomic.AddUint64(&n, 1) 60 | // log.Println("---mt:", n, mt, len(data), c.RemoteAddr().String()) 61 | c.WriteMessage(mt, data) 62 | atomic.AddUint64(&qps, 1) 63 | }) 64 | u.OnClose(func(c *websocket.Conn, err error) { 65 | log.Printf("OnClose: %v, %v", c.RemoteAddr().String(), err) 66 | }) 67 | return u 68 | } 69 | func onNow(w http.ResponseWriter, r *http.Request) { 70 | w.Write([]byte(time.Now().Format("20060102 15:04:05"))) 71 | } 72 | 73 | func onWebsocket(w http.ResponseWriter, r *http.Request) { 74 | _, err := upgrader.UpgradeAndTransferConnToPoller(w, r, nil) 75 | if err != nil { 76 | log.Printf("upgrade: %v", err) 77 | return 78 | } 79 | // log.Printf("OnOpen: %v", wsConn.RemoteAddr().String()) 80 | } 81 | 82 | func onNBWebsocket(w http.ResponseWriter, r *http.Request) { 83 | _, err := upgrader.Upgrade(w, r, nil) 84 | if err != nil { 85 | log.Printf("upgrade: %v", err) 86 | return 87 | } 88 | // log.Printf("OnOpen: %v", wsConn.RemoteAddr().String()) 89 | } 90 | 91 | func startNBIO(tlsConfig *tls.Config) { 92 | nbhttpEngine = nbhttp.NewEngine(nbhttp.Config{ 93 | Addrs: []string{addrNBHTTP}, 94 | AddrsTLS: []string{addrNBHTTPTLS}, 95 | TLSConfig: tlsConfig, 96 | Handler: muxNBHTTP, 97 | ReleaseWebsocketPayload: true, 98 | }) 99 | 100 | err := nbhttpEngine.Start() 101 | if err != nil { 102 | fmt.Printf("nbio.Start failed: %v\n", err) 103 | return 104 | } 105 | } 106 | 107 | func startHTTP(server *http.Server, ln net.Listener, chDone chan error) { 108 | server.Handler = muxHTTP 109 | err := server.Serve(ln) 110 | log.Printf("HTTP Server Exit: %v", err) 111 | chDone <- err 112 | } 113 | 114 | func main() { 115 | cert, err := tls.X509KeyPair(rsaCertPEM, rsaKeyPEM) 116 | if err != nil { 117 | log.Fatalf("tls.X509KeyPair failed: %v", err) 118 | } 119 | tlsConfig := &tls.Config{ 120 | Certificates: []tls.Certificate{cert}, 121 | InsecureSkipVerify: true, 122 | } 123 | ln, err := net.Listen("tcp", addrTLS) 124 | if err != nil { 125 | panic(err) 126 | } 127 | lnTLS := tls.NewListener(ln, tlsConfig, &tls.NativeAllocator{}) 128 | if err != nil { 129 | panic(err) 130 | } 131 | 132 | go startNBIO(tlsConfig) 133 | 134 | chTLS := make(chan error, 1) 135 | go startHTTP(httpServerTLS, lnTLS, chTLS) 136 | 137 | lnTCP, err := net.Listen("tcp", addrNonTLS) 138 | if err != nil { 139 | panic(err) 140 | } 141 | chTCP := make(chan error, 1) 142 | go startHTTP(httpServerNonTLS, lnTCP, chTCP) 143 | 144 | go func() { 145 | ticker := time.NewTicker(time.Second) 146 | for i := 1; true; i++ { 147 | <-ticker.C 148 | n := atomic.SwapUint64(&qps, 0) 149 | total += n 150 | fmt.Printf("running for %v seconds, Online: %v, NumGoroutine: %v, websocket qps: %v, total: %v\n", i, nbhttpEngine.Online(), runtime.NumGoroutine(), n, total) 151 | } 152 | }() 153 | 154 | interrupt := make(chan os.Signal, 1) 155 | signal.Notify(interrupt, os.Interrupt) 156 | <-interrupt 157 | 158 | ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) 159 | defer cancel() 160 | err = httpServerTLS.Shutdown(ctx) 161 | log.Printf("httpServerTLS.Shutdown: %v", err) 162 | err = httpServerNonTLS.Shutdown(ctx) 163 | log.Printf("httpServerNonTLS.Shutdown: %v", err) 164 | <-chTLS 165 | <-chTCP 166 | err = nbhttpEngine.Shutdown(ctx) 167 | log.Printf("nbhttpEngine.Shutdown: %v", err) 168 | } 169 | 170 | var rsaCertPEM = []byte(`-----BEGIN CERTIFICATE----- 171 | MIIDazCCAlOgAwIBAgIUJeohtgk8nnt8ofratXJg7kUJsI4wDQYJKoZIhvcNAQEL 172 | BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 173 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDEyMDcwODIyNThaFw0zMDEy 174 | MDUwODIyNThaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw 175 | HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB 176 | AQUAA4IBDwAwggEKAoIBAQCy+ZrIvwwiZv4bPmvKx/637ltZLwfgh3ouiEaTchGu 177 | IQltthkqINHxFBqqJg44TUGHWthlrq6moQuKnWNjIsEc6wSD1df43NWBLgdxbPP0 178 | x4tAH9pIJU7TQqbznjDBhzRbUjVXBIcn7bNknY2+5t784pPF9H1v7h8GqTWpNH9l 179 | cz/v+snoqm9HC+qlsFLa4A3X9l5v05F1uoBfUALlP6bWyjHAfctpiJkoB9Yw1TJa 180 | gpq7E50kfttwfKNkkAZIbib10HugkMoQJAs2EsGkje98druIl8IXmuvBIF6nZHuM 181 | lt3UIZjS9RwPPLXhRHt1P0mR7BoBcOjiHgtSEs7Wk+j7AgMBAAGjUzBRMB0GA1Ud 182 | DgQWBBQdheJv73XSOhgMQtkwdYPnfO02+TAfBgNVHSMEGDAWgBQdheJv73XSOhgM 183 | QtkwdYPnfO02+TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBf 184 | SKVNMdmBpD9m53kCrguo9iKQqmhnI0WLkpdWszc/vBgtpOE5ENOfHGAufHZve871 185 | 2fzTXrgR0TF6UZWsQOqCm5Oh3URsCdXWewVMKgJ3DCii6QJ0MnhSFt6+xZE9C6Hi 186 | WhcywgdR8t/JXKDam6miohW8Rum/IZo5HK9Jz/R9icKDGumcqoaPj/ONvY4EUwgB 187 | irKKB7YgFogBmCtgi30beLVkXgk0GEcAf19lHHtX2Pv/lh3m34li1C9eBm1ca3kk 188 | M2tcQtm1G89NROEjcG92cg+GX3GiWIjbI0jD1wnVy2LCOXMgOVbKfGfVKISFt0b1 189 | DNn00G8C6ttLoGU2snyk 190 | -----END CERTIFICATE----- 191 | `) 192 | 193 | var rsaKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- 194 | MIIEogIBAAKCAQEAsvmayL8MImb+Gz5rysf+t+5bWS8H4Id6LohGk3IRriEJbbYZ 195 | KiDR8RQaqiYOOE1Bh1rYZa6upqELip1jYyLBHOsEg9XX+NzVgS4HcWzz9MeLQB/a 196 | SCVO00Km854wwYc0W1I1VwSHJ+2zZJ2Nvube/OKTxfR9b+4fBqk1qTR/ZXM/7/rJ 197 | 6KpvRwvqpbBS2uAN1/Zeb9ORdbqAX1AC5T+m1soxwH3LaYiZKAfWMNUyWoKauxOd 198 | JH7bcHyjZJAGSG4m9dB7oJDKECQLNhLBpI3vfHa7iJfCF5rrwSBep2R7jJbd1CGY 199 | 0vUcDzy14UR7dT9JkewaAXDo4h4LUhLO1pPo+wIDAQABAoIBAF6yWwekrlL1k7Xu 200 | jTI6J7hCUesaS1yt0iQUzuLtFBXCPS7jjuUPgIXCUWl9wUBhAC8SDjWe+6IGzAiH 201 | xjKKDQuz/iuTVjbDAeTb6exF7b6yZieDswdBVjfJqHR2Wu3LEBTRpo9oQesKhkTS 202 | aFF97rZ3XCD9f/FdWOU5Wr8wm8edFK0zGsZ2N6r57yf1N6ocKlGBLBZ0v1Sc5ShV 203 | 1PVAxeephQvwL5DrOgkArnuAzwRXwJQG78L0aldWY2q6xABQZQb5+ml7H/kyytef 204 | i+uGo3jHKepVALHmdpCGr9Yv+yCElup+ekv6cPy8qcmMBqGMISL1i1FEONxLcKWp 205 | GEJi6QECgYEA3ZPGMdUm3f2spdHn3C+/+xskQpz6efiPYpnqFys2TZD7j5OOnpcP 206 | ftNokA5oEgETg9ExJQ8aOCykseDc/abHerYyGw6SQxmDbyBLmkZmp9O3iMv2N8Pb 207 | Nrn9kQKSr6LXZ3gXzlrDvvRoYUlfWuLSxF4b4PYifkA5AfsdiKkj+5sCgYEAzseF 208 | XDTRKHHJnzxZDDdHQcwA0G9agsNj64BGUEjsAGmDiDyqOZnIjDLRt0O2X3oiIE5S 209 | TXySSEiIkxjfErVJMumLaIwqVvlS4pYKdQo1dkM7Jbt8wKRQdleRXOPPN7msoEUk 210 | Ta9ZsftHVUknPqblz9Uthb5h+sRaxIaE1llqDiECgYATS4oHzuL6k9uT+Qpyzymt 211 | qThoIJljQ7TgxjxvVhD9gjGV2CikQM1Vov1JBigj4Toc0XuxGXaUC7cv0kAMSpi2 212 | Y+VLG+K6ux8J70sGHTlVRgeGfxRq2MBfLKUbGplBeDG/zeJs0tSW7VullSkblgL6 213 | nKNa3LQ2QEt2k7KHswryHwKBgENDxk8bY1q7wTHKiNEffk+aFD25q4DUHMH0JWti 214 | fVsY98+upFU+gG2S7oOmREJE0aser0lDl7Zp2fu34IEOdfRY4p+s0O0gB+Vrl5VB 215 | L+j7r9bzaX6lNQN6MvA7ryHahZxRQaD/xLbQHgFRXbHUyvdTyo4yQ1821qwNclLk 216 | HUrhAoGAUtjR3nPFR4TEHlpTSQQovS8QtGTnOi7s7EzzdPWmjHPATrdLhMA0ezPj 217 | Mr+u5TRncZBIzAZtButlh1AHnpN/qO3P0c0Rbdep3XBc/82JWO8qdb5QvAkxga3X 218 | BpA7MNLxiqss+rCbwf3NbWxEMiDQ2zRwVoafVFys7tjmv6t2Xck= 219 | -----END RSA PRIVATE KEY----- 220 | `) 221 | --------------------------------------------------------------------------------