github.com/cloudwego/hertz@v0.9.3/pkg/app/server/hertz_unix_test.go (about) 1 // Copyright 2023 CloudWeGo Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 16 //go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 17 // +build aix darwin dragonfly freebsd linux netbsd openbsd solaris 18 19 package server 20 21 import ( 22 "context" 23 "net" 24 "net/http" 25 "os" 26 "os/exec" 27 "strconv" 28 "sync/atomic" 29 "syscall" 30 "testing" 31 "time" 32 33 "github.com/cloudwego/hertz/pkg/app" 34 c "github.com/cloudwego/hertz/pkg/app/client" 35 "github.com/cloudwego/hertz/pkg/common/test/assert" 36 "github.com/cloudwego/hertz/pkg/common/utils" 37 "github.com/cloudwego/hertz/pkg/network" 38 "github.com/cloudwego/hertz/pkg/network/standard" 39 "github.com/cloudwego/hertz/pkg/protocol/consts" 40 "golang.org/x/sys/unix" 41 ) 42 43 func TestReusePorts(t *testing.T) { 44 cfg := &net.ListenConfig{Control: func(network, address string, c syscall.RawConn) error { 45 return c.Control(func(fd uintptr) { 46 syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEADDR, 1) 47 syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1) 48 }) 49 }} 50 ha := New(WithHostPorts("localhost:10093"), WithListenConfig(cfg), WithTransport(standard.NewTransporter)) 51 hb := New(WithHostPorts("localhost:10093"), WithListenConfig(cfg), WithTransport(standard.NewTransporter)) 52 hc := New(WithHostPorts("localhost:10093"), WithListenConfig(cfg)) 53 hd := New(WithHostPorts("localhost:10093"), WithListenConfig(cfg)) 54 ha.GET("/ping", func(c context.Context, ctx *app.RequestContext) { 55 ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"}) 56 }) 57 hc.GET("/ping", func(c context.Context, ctx *app.RequestContext) { 58 ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"}) 59 }) 60 hd.GET("/ping", func(c context.Context, ctx *app.RequestContext) { 61 ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"}) 62 }) 63 hb.GET("/ping", func(c context.Context, ctx *app.RequestContext) { 64 ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"}) 65 }) 66 go ha.Run() 67 go hb.Run() 68 go hc.Run() 69 go hd.Run() 70 time.Sleep(time.Second) 71 72 client, _ := c.NewClient() 73 for i := 0; i < 1000; i++ { 74 statusCode, body, err := client.Get(context.Background(), nil, "http://localhost:10093/ping") 75 assert.Nil(t, err) 76 assert.DeepEqual(t, consts.StatusOK, statusCode) 77 assert.DeepEqual(t, "{\"ping\":\"pong\"}", string(body)) 78 } 79 } 80 81 func TestHertz_Spin(t *testing.T) { 82 engine := New(WithHostPorts("127.0.0.1:6668")) 83 engine.GET("/test", func(c context.Context, ctx *app.RequestContext) { 84 time.Sleep(time.Second * 2) 85 path := ctx.Request.URI().PathOriginal() 86 ctx.SetBodyString(string(path)) 87 }) 88 engine.GET("/test2", func(c context.Context, ctx *app.RequestContext) {}) 89 90 testint := uint32(0) 91 engine.Engine.OnShutdown = append(engine.OnShutdown, func(ctx context.Context) { 92 atomic.StoreUint32(&testint, 1) 93 }) 94 95 go engine.Spin() 96 time.Sleep(time.Millisecond) 97 98 hc := http.Client{Timeout: time.Second} 99 var err error 100 var resp *http.Response 101 ch := make(chan struct{}) 102 ch2 := make(chan struct{}) 103 go func() { 104 ticker := time.NewTicker(time.Millisecond * 100) 105 defer ticker.Stop() 106 for range ticker.C { 107 _, err := hc.Get("http://127.0.0.1:6668/test2") 108 t.Logf("[%v]begin listening\n", time.Now()) 109 if err != nil { 110 t.Logf("[%v]listening closed: %v", time.Now(), err) 111 ch2 <- struct{}{} 112 break 113 } 114 } 115 }() 116 go func() { 117 t.Logf("[%v]begin request\n", time.Now()) 118 resp, err = http.Get("http://127.0.0.1:6668/test") 119 t.Logf("[%v]end request\n", time.Now()) 120 ch <- struct{}{} 121 }() 122 123 time.Sleep(time.Second * 1) 124 pid := strconv.Itoa(os.Getpid()) 125 cmd := exec.Command("kill", "-SIGHUP", pid) 126 t.Logf("[%v]begin SIGHUP\n", time.Now()) 127 if err := cmd.Run(); err != nil { 128 t.Fatal(err) 129 } 130 t.Logf("[%v]end SIGHUP\n", time.Now()) 131 <-ch 132 assert.Nil(t, err) 133 assert.NotNil(t, resp) 134 assert.DeepEqual(t, uint32(1), atomic.LoadUint32(&testint)) 135 136 <-ch2 137 } 138 139 func TestWithSenseClientDisconnection(t *testing.T) { 140 var closeFlag int32 141 h := New(WithHostPorts("127.0.0.1:6631"), WithSenseClientDisconnection(true)) 142 h.GET("/ping", func(c context.Context, ctx *app.RequestContext) { 143 assert.DeepEqual(t, "aa", string(ctx.Host())) 144 ch := make(chan struct{}) 145 select { 146 case <-c.Done(): 147 atomic.StoreInt32(&closeFlag, 1) 148 assert.DeepEqual(t, context.Canceled, c.Err()) 149 case <-ch: 150 } 151 }) 152 go h.Spin() 153 time.Sleep(time.Second) 154 con, err := net.Dial("tcp", "127.0.0.1:6631") 155 assert.Nil(t, err) 156 _, err = con.Write([]byte("GET /ping HTTP/1.1\r\nHost: aa\r\n\r\n")) 157 assert.Nil(t, err) 158 time.Sleep(time.Second) 159 assert.DeepEqual(t, atomic.LoadInt32(&closeFlag), int32(0)) 160 assert.Nil(t, con.Close()) 161 time.Sleep(time.Second) 162 assert.DeepEqual(t, atomic.LoadInt32(&closeFlag), int32(1)) 163 } 164 165 func TestWithSenseClientDisconnectionAndWithOnConnect(t *testing.T) { 166 var closeFlag int32 167 h := New(WithHostPorts("127.0.0.1:6632"), WithSenseClientDisconnection(true), WithOnConnect(func(ctx context.Context, conn network.Conn) context.Context { 168 return ctx 169 })) 170 h.GET("/ping", func(c context.Context, ctx *app.RequestContext) { 171 assert.DeepEqual(t, "aa", string(ctx.Host())) 172 ch := make(chan struct{}) 173 select { 174 case <-c.Done(): 175 atomic.StoreInt32(&closeFlag, 1) 176 assert.DeepEqual(t, context.Canceled, c.Err()) 177 case <-ch: 178 } 179 }) 180 go h.Spin() 181 time.Sleep(time.Second) 182 con, err := net.Dial("tcp", "127.0.0.1:6632") 183 assert.Nil(t, err) 184 _, err = con.Write([]byte("GET /ping HTTP/1.1\r\nHost: aa\r\n\r\n")) 185 assert.Nil(t, err) 186 time.Sleep(time.Second) 187 assert.DeepEqual(t, atomic.LoadInt32(&closeFlag), int32(0)) 188 assert.Nil(t, con.Close()) 189 time.Sleep(time.Second) 190 assert.DeepEqual(t, atomic.LoadInt32(&closeFlag), int32(1)) 191 }