go.ligato.io/vpp-agent/v3@v3.5.0/tests/e2e/e2etest/connection.go (about)

     1  //  Copyright (c) 2020 Cisco and/or its affiliates.
     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 e2etest
    16  
    17  import (
    18  	"bufio"
    19  	"context"
    20  	"fmt"
    21  	"log"
    22  	"net"
    23  	"strings"
    24  	"time"
    25  )
    26  
    27  // TCP or UDP connection request
    28  type connectionRequest struct {
    29  	conn net.Conn
    30  	err  error
    31  }
    32  
    33  func simpleTCPServer(ctx context.Context, ms *Microservice, addr string, expReqMsg, respMsg string, done chan<- error, logger *log.Logger) {
    34  	defer func() {
    35  		done <- nil
    36  	}()
    37  	// move to the network namespace where server should listen
    38  	exitNetNs := ms.EnterNetNs()
    39  	defer exitNetNs()
    40  
    41  	listener, err := net.Listen("tcp", addr)
    42  	if err != nil {
    43  		done <- err
    44  		return
    45  	}
    46  	defer listener.Close()
    47  
    48  	// accept single connection
    49  	newConn := make(chan connectionRequest, 1)
    50  	go func() {
    51  		conn, err := listener.Accept()
    52  		if err != nil {
    53  			err = fmt.Errorf("accept failed with: %v", err)
    54  			logger.Println(err)
    55  		}
    56  		newConn <- connectionRequest{conn: conn, err: err}
    57  		close(newConn)
    58  	}()
    59  
    60  	// wait for connection
    61  	var cr connectionRequest
    62  	select {
    63  	case <-ctx.Done():
    64  		done <- fmt.Errorf("tcp server listening on %s was canceled", addr)
    65  		return
    66  	case cr = <-newConn:
    67  		if cr.err != nil {
    68  			done <- cr.err
    69  			return
    70  		}
    71  		defer cr.conn.Close()
    72  	}
    73  
    74  	// communicate with the client
    75  	commRv := make(chan error, 1)
    76  	go func() {
    77  		defer close(commRv)
    78  		// receive message from the client
    79  		message, err := bufio.NewReader(cr.conn).ReadString('\n')
    80  		if err != nil {
    81  			err = fmt.Errorf("failed to read data from client: %v", err)
    82  			logger.Println(err)
    83  			commRv <- err
    84  			return
    85  		}
    86  		// send response to the client
    87  		_, err = cr.conn.Write([]byte(respMsg + "\n"))
    88  		if err != nil {
    89  			err = fmt.Errorf("failed to send data to client: %v", err)
    90  			logger.Println(err)
    91  			commRv <- err
    92  			return
    93  		}
    94  		// check if the exchanged data are as expected
    95  		message = strings.TrimRight(message, "\n")
    96  		if message != expReqMsg {
    97  			err = fmt.Errorf("unexpected message received from client ('%s' vs. '%s')",
    98  				message, expReqMsg)
    99  			logger.Println(err)
   100  			commRv <- err
   101  			return
   102  		}
   103  		commRv <- nil
   104  	}()
   105  
   106  	// wait for the message exchange to execute
   107  	select {
   108  	case <-ctx.Done():
   109  		done <- fmt.Errorf("tcp server listening on %s was canceled", addr)
   110  		return
   111  	case err = <-commRv:
   112  		done <- err
   113  	}
   114  
   115  	// do not close until client confirms reception of the message
   116  	<-ctx.Done()
   117  }
   118  
   119  func simpleUDPServer(ctx context.Context, ms *Microservice, addr string, expReqMsg, respMsg string, done chan<- error, ready chan<- error, logger *log.Logger) {
   120  	defer func() {
   121  		done <- nil
   122  	}()
   123  
   124  	const maxBufferSize = 1024
   125  	// move to the network namespace where server should listen
   126  	exitNetNs := ms.EnterNetNs()
   127  	defer exitNetNs()
   128  
   129  	conn, err := net.ListenPacket("udp", addr)
   130  	ready <- err
   131  	if err != nil {
   132  		done <- err
   133  		return
   134  	}
   135  	defer conn.Close()
   136  
   137  	// communicate with the client
   138  	commRv := make(chan error, 1)
   139  	go func() {
   140  		defer close(commRv)
   141  		// receive message from the client
   142  		buffer := make([]byte, maxBufferSize)
   143  		n, addr, err := conn.ReadFrom(buffer)
   144  		if err != nil {
   145  			err = fmt.Errorf("failed to read data from client: %v", err)
   146  			logger.Println(err)
   147  			commRv <- err
   148  			return
   149  		}
   150  		message := string(buffer[:n])
   151  		// send response to the client
   152  		_, err = conn.WriteTo([]byte(respMsg+"\n"), addr)
   153  		if err != nil {
   154  			err = fmt.Errorf("failed to send data to client: %v", err)
   155  			logger.Println(err)
   156  			commRv <- err
   157  			return
   158  		}
   159  		// check if the exchanged data are as expected
   160  		message = strings.TrimRight(message, "\n")
   161  		if message != expReqMsg {
   162  			err = fmt.Errorf("unexpected message received from client ('%s' vs. '%s')",
   163  				message, expReqMsg)
   164  			logger.Println(err)
   165  			commRv <- err
   166  			return
   167  		}
   168  		commRv <- nil
   169  	}()
   170  
   171  	// wait for the message exchange to execute
   172  	select {
   173  	case <-ctx.Done():
   174  		done <- fmt.Errorf("udp server listening on %s was canceled", addr)
   175  		return
   176  	case err = <-commRv:
   177  		done <- err
   178  	}
   179  
   180  	// do not close until client confirms reception of the message
   181  	<-ctx.Done()
   182  }
   183  
   184  func simpleTCPClient(ms *Microservice, addr string, reqMsg, expRespMsg string, timeout time.Duration, done chan<- error, logger *log.Logger) {
   185  	// try to connect with the server
   186  	newConn := make(chan connectionRequest, 1)
   187  
   188  	go func() {
   189  		// move to the network namespace from which the connection should be initiated
   190  		exitNetNs := ms.EnterNetNs()
   191  		defer exitNetNs()
   192  		start := time.Now()
   193  		for {
   194  			conn, err := net.Dial("tcp", addr)
   195  			if err != nil && time.Since(start) < timeout {
   196  				time.Sleep(checkPollingInterval)
   197  				continue
   198  			}
   199  			if err != nil {
   200  				err = fmt.Errorf("dial failed with: %v", err)
   201  				logger.Println(err)
   202  			}
   203  			newConn <- connectionRequest{conn: conn, err: err}
   204  			break
   205  		}
   206  		close(newConn)
   207  	}()
   208  
   209  	simpleTCPOrUDPClient(newConn, addr, reqMsg, expRespMsg, timeout, done, logger)
   210  }
   211  
   212  func simpleUDPClient(ms *Microservice, addr string, reqMsg, expRespMsg string, timeout time.Duration, done chan<- error, srvReady chan error, logger *log.Logger) {
   213  	// try to connect with the server
   214  	newConn := make(chan connectionRequest, 1)
   215  
   216  	go func() {
   217  		// move to the network namespace from which the connection should be initiated
   218  		exitNetNs := ms.EnterNetNs()
   219  		defer exitNetNs()
   220  		udpAddr, err := net.ResolveUDPAddr("udp", addr)
   221  		if err != nil {
   222  			err = fmt.Errorf("dial failed with: %v", err)
   223  			logger.Println(err)
   224  			newConn <- connectionRequest{conn: nil, err: err}
   225  		} else {
   226  			start := time.Now()
   227  			err = <-srvReady
   228  			if err != nil {
   229  				err = fmt.Errorf("dial failed with: %v", "server not ready")
   230  				logger.Println(err)
   231  				newConn <- connectionRequest{conn: nil, err: err}
   232  			} else {
   233  				for {
   234  					conn, err := net.DialUDP("udp", nil, udpAddr)
   235  					if err != nil && time.Since(start) < timeout {
   236  						time.Sleep(checkPollingInterval)
   237  						continue
   238  					}
   239  					if err != nil {
   240  						err = fmt.Errorf("dial failed with: %v", err)
   241  						logger.Println(err)
   242  					}
   243  					newConn <- connectionRequest{conn: conn, err: err}
   244  					break
   245  				}
   246  			}
   247  		}
   248  		close(newConn)
   249  	}()
   250  
   251  	simpleTCPOrUDPClient(newConn, addr, reqMsg, expRespMsg, timeout, done, logger)
   252  }
   253  
   254  func simpleTCPOrUDPClient(newConn chan connectionRequest, addr, reqMsg, expRespMsg string,
   255  	timeout time.Duration, done chan<- error, logger *log.Logger) {
   256  
   257  	// wait for connection
   258  	var cr connectionRequest
   259  	select {
   260  	case <-time.After(timeout):
   261  		done <- fmt.Errorf("connection to %s timed out", addr)
   262  		return
   263  	case cr = <-newConn:
   264  		if cr.err != nil {
   265  			done <- cr.err
   266  			return
   267  		}
   268  		defer cr.conn.Close()
   269  	}
   270  
   271  	// communicate with the server
   272  	commRv := make(chan error, 1)
   273  	go func() {
   274  		defer close(commRv)
   275  
   276  		// send message to the server
   277  		_, err := cr.conn.Write([]byte(reqMsg + "\n"))
   278  		if err != nil {
   279  			err = fmt.Errorf("failed to send data to the server: %v", err)
   280  			logger.Println(err)
   281  			commRv <- err
   282  			return
   283  		}
   284  		// listen for reply
   285  		start := time.Now()
   286  		var message string
   287  		for {
   288  			message, err = bufio.NewReader(cr.conn).ReadString('\n')
   289  			if err != nil && time.Since(start) < timeout {
   290  				time.Sleep(checkPollingInterval)
   291  				continue
   292  			}
   293  			if err != nil {
   294  				err = fmt.Errorf("failed to read data from server: %v", err)
   295  				logger.Println(err)
   296  				commRv <- err
   297  				return
   298  			}
   299  			break
   300  		}
   301  		// check if the exchanged data are as expected
   302  		message = strings.TrimRight(message, "\n")
   303  		if message != expRespMsg {
   304  			err = fmt.Errorf("unexpected message received from server ('%s' vs. '%s')",
   305  				message, expRespMsg)
   306  			logger.Println(err)
   307  			commRv <- err
   308  			return
   309  		}
   310  
   311  		commRv <- nil
   312  	}()
   313  
   314  	// wait for the message exchange to execute
   315  	select {
   316  	case <-time.After(timeout):
   317  		done <- fmt.Errorf("communication with %s timed out", addr)
   318  	case err := <-commRv:
   319  		done <- err
   320  	}
   321  }