mirror of https://github.com/knative/func.git
				
				
				
			
		
			
				
	
	
		
			142 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net"
 | |
| 	"os"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/spf13/cobra"
 | |
| 	"golang.org/x/sync/errgroup"
 | |
| )
 | |
| 
 | |
| func newSocatCmd() *cobra.Command {
 | |
| 	var (
 | |
| 		uniDir bool
 | |
| 		dbg    string
 | |
| 	)
 | |
| 	cmd := cobra.Command{
 | |
| 		Use:   "socat [-u] <address> <address>",
 | |
| 		Short: "Minimalistic socat.",
 | |
| 		Long: `Minimalistic socat.
 | |
| Implements only TCP, OPEN and stdio ("-") addresses with no options.
 | |
| Only supported flag is -u.`,
 | |
| 		Args: cobra.ExactArgs(2),
 | |
| 		RunE: func(cmd *cobra.Command, args []string) error {
 | |
| 			stdio := rwc{
 | |
| 				ReadCloser:  cmd.InOrStdin().(io.ReadCloser),
 | |
| 				WriteCloser: cmd.OutOrStdout().(io.WriteCloser),
 | |
| 			}
 | |
| 			left, err := createConnection(args[0], stdio)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			defer left.Close()
 | |
| 			right, err := createConnection(args[1], stdio)
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 			defer right.Close()
 | |
| 			return connect(left, right, uniDir)
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	cmd.Flags().BoolVarP(&uniDir, "unidirect", "u", false, "unidirectional mode (left to right)")
 | |
| 	cmd.Flags().StringVarP(&dbg, "debug", "d", "", "log level (this flag is present only for compatibility and has no effect)")
 | |
| 
 | |
| 	return &cmd
 | |
| }
 | |
| 
 | |
| func createConnection(address string, stdio connection) (connection, error) {
 | |
| 	if address == "-" {
 | |
| 		return stdio, nil
 | |
| 	}
 | |
| 	parts := strings.SplitN(address, ":", 2)
 | |
| 	if len(parts) != 2 {
 | |
| 		return nil, fmt.Errorf("cannot parse address: %q", address)
 | |
| 	}
 | |
| 	typ := strings.ToLower(parts[0])
 | |
| 	parts = strings.Split(parts[1], ",")
 | |
| 	if len(parts) > 1 {
 | |
| 		_, _ = fmt.Fprintf(os.Stderr, "ignored options: %q\n", parts[1])
 | |
| 	}
 | |
| 	addr := parts[0]
 | |
| 	switch typ {
 | |
| 	case "tcp", "tcp4", "tcp6":
 | |
| 		_, _ = fmt.Fprintln(os.Stderr, "opening connection")
 | |
| 		var laddr net.TCPAddr
 | |
| 		raddr, err := net.ResolveTCPAddr(typ, addr)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("name does not resolve: %w", err)
 | |
| 		}
 | |
| 
 | |
| 		conn, err := net.DialTCP(typ, &laddr, raddr)
 | |
| 		if err == nil {
 | |
| 			_, _ = fmt.Fprintf(os.Stderr, "successfully connected to %v\n", raddr)
 | |
| 		}
 | |
| 		return conn, err
 | |
| 	case "open":
 | |
| 		return os.OpenFile(addr, os.O_RDWR, 0644)
 | |
| 	default:
 | |
| 		return nil, fmt.Errorf("unsupported address: %q", address)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func connect(left, right connection, uniDir bool) error {
 | |
| 	g := errgroup.Group{}
 | |
| 	g.SetLimit(2)
 | |
| 
 | |
| 	if !uniDir {
 | |
| 		g.Go(func() error {
 | |
| 			_, err := io.Copy(left, right)
 | |
| 			tryCloseWriteSide(left)
 | |
| 			return err
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	g.Go(func() error {
 | |
| 		_, err := io.Copy(right, left)
 | |
| 		tryCloseWriteSide(right)
 | |
| 		return err
 | |
| 	})
 | |
| 
 | |
| 	return g.Wait()
 | |
| }
 | |
| 
 | |
| type connection interface {
 | |
| 	io.Reader
 | |
| 	io.Writer
 | |
| 	io.Closer
 | |
| }
 | |
| 
 | |
| type writeCloser interface {
 | |
| 	CloseWrite() error
 | |
| }
 | |
| 
 | |
| type rwc struct {
 | |
| 	io.ReadCloser
 | |
| 	io.WriteCloser
 | |
| }
 | |
| 
 | |
| func (r rwc) Close() error {
 | |
| 	err := r.WriteCloser.Close()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return r.ReadCloser.Close()
 | |
| }
 | |
| 
 | |
| func (r rwc) CloseWrite() error {
 | |
| 	return r.WriteCloser.Close()
 | |
| }
 | |
| 
 | |
| func tryCloseWriteSide(c connection) {
 | |
| 	if wc, ok := c.(writeCloser); ok {
 | |
| 		err := wc.CloseWrite()
 | |
| 		if err != nil {
 | |
| 			fmt.Fprintf(os.Stderr, "waring: cannot close write side: %+v\n", err)
 | |
| 		}
 | |
| 	}
 | |
| }
 |