istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/echo/server/forwarder/udp.go (about) 1 // Copyright Istio 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 package forwarder 16 17 import ( 18 "bytes" 19 "context" 20 "fmt" 21 "io" 22 "net" 23 "net/http" 24 "strings" 25 26 "istio.io/istio/pkg/test/echo" 27 "istio.io/istio/pkg/test/echo/common" 28 "istio.io/istio/pkg/test/echo/proto" 29 ) 30 31 var _ protocol = &udpProtocol{} 32 33 type udpProtocol struct { 34 e *executor 35 } 36 37 func newUDPProtocol(e *executor) protocol { 38 return &udpProtocol{e: e} 39 } 40 41 func (c *udpProtocol) ForwardEcho(ctx context.Context, cfg *Config) (*proto.ForwardEchoResponse, error) { 42 return doForward(ctx, cfg, c.e, c.makeRequest) 43 } 44 45 func (c *udpProtocol) makeRequest(ctx context.Context, cfg *Config, requestID int) (string, error) { 46 conn, err := newUDPConnection(cfg) 47 if err != nil { 48 return "", err 49 } 50 defer func() { _ = conn.Close() }() 51 52 msgBuilder := strings.Builder{} 53 echo.ForwarderURLField.WriteForRequest(&msgBuilder, requestID, cfg.Request.Url) 54 55 if cfg.Request.Message != "" { 56 echo.ForwarderMessageField.WriteForRequest(&msgBuilder, requestID, cfg.Request.Message) 57 } 58 59 // Apply per-request timeout to calculate deadline for reads/writes. 60 ctx, cancel := context.WithTimeout(ctx, cfg.timeout) 61 defer cancel() 62 63 // Apply the deadline to the connection. 64 deadline, _ := ctx.Deadline() 65 if err := conn.SetWriteDeadline(deadline); err != nil { 66 return msgBuilder.String(), err 67 } 68 if err := conn.SetReadDeadline(deadline); err != nil { 69 return msgBuilder.String(), err 70 } 71 72 // Make sure the client writes something to the buffer 73 message := "HelloWorld" 74 if cfg.Request.Message != "" { 75 message = cfg.Request.Message 76 } 77 78 if _, err := conn.Write([]byte(message + "\n")); err != nil { 79 fwLog.Warnf("UDP write failed: %v", err) 80 return msgBuilder.String(), err 81 } 82 var resBuffer bytes.Buffer 83 buf := make([]byte, 1024+len(message)) 84 n, err := conn.Read(buf) 85 if err != nil && err != io.EOF { 86 fwLog.Warnf("UDP read failed (already read %d bytes): %v", len(resBuffer.String()), err) 87 return msgBuilder.String(), err 88 } 89 resBuffer.Write(buf[:n]) 90 91 // format the output for forwarder response 92 for _, line := range strings.Split(string(buf[:n]), "\n") { 93 if line != "" { 94 echo.WriteBodyLine(&msgBuilder, requestID, line) 95 } 96 } 97 98 msg := msgBuilder.String() 99 expected := fmt.Sprintf("%s=%d", string(echo.StatusCodeField), http.StatusOK) 100 if cfg.Request.ExpectedResponse != nil { 101 expected = cfg.Request.ExpectedResponse.GetValue() 102 } 103 if !strings.Contains(msg, expected) { 104 return msg, fmt.Errorf("expect to recv message with %s, got %s. Return EOF", expected, msg) 105 } 106 return msg, nil 107 } 108 109 func (c *udpProtocol) Close() error { 110 return nil 111 } 112 113 func newUDPConnection(cfg *Config) (net.Conn, error) { 114 address := cfg.Request.Url[len(cfg.scheme+"://"):] 115 116 if cfg.secure { 117 return nil, fmt.Errorf("TLS not available") 118 } 119 120 ctx, cancel := context.WithTimeout(context.Background(), common.ConnectionTimeout) 121 defer cancel() 122 return newDialer(cfg).DialContext(ctx, "udp", address) 123 }