github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/example/echo/echo.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"crypto/rand"
     6  	"crypto/rsa"
     7  	"crypto/tls"
     8  	"crypto/x509"
     9  	"encoding/pem"
    10  	"fmt"
    11  	"io"
    12  	"log"
    13  	"math/big"
    14  
    15  	"github.com/daeuniverse/quic-go"
    16  )
    17  
    18  const addr = "localhost:4242"
    19  
    20  const message = "foobar"
    21  
    22  // We start a server echoing data on the first stream the client opens,
    23  // then connect with a client, send the message, and wait for its receipt.
    24  func main() {
    25  	go func() { log.Fatal(echoServer()) }()
    26  
    27  	err := clientMain()
    28  	if err != nil {
    29  		panic(err)
    30  	}
    31  }
    32  
    33  // Start a server that echos all data on the first stream opened by the client
    34  func echoServer() error {
    35  	listener, err := quic.ListenAddr(addr, generateTLSConfig(), nil)
    36  	if err != nil {
    37  		return err
    38  	}
    39  	defer listener.Close()
    40  
    41  	conn, err := listener.Accept(context.Background())
    42  	if err != nil {
    43  		return err
    44  	}
    45  
    46  	stream, err := conn.AcceptStream(context.Background())
    47  	if err != nil {
    48  		panic(err)
    49  	}
    50  	defer stream.Close()
    51  
    52  	// Echo through the loggingWriter
    53  	_, err = io.Copy(loggingWriter{stream}, stream)
    54  	return err
    55  }
    56  
    57  func clientMain() error {
    58  	tlsConf := &tls.Config{
    59  		InsecureSkipVerify: true,
    60  		NextProtos:         []string{"quic-echo-example"},
    61  	}
    62  	conn, err := quic.DialAddr(context.Background(), addr, tlsConf, nil)
    63  	if err != nil {
    64  		return err
    65  	}
    66  	defer conn.CloseWithError(0, "")
    67  
    68  	stream, err := conn.OpenStreamSync(context.Background())
    69  	if err != nil {
    70  		return err
    71  	}
    72  	defer stream.Close()
    73  
    74  	fmt.Printf("Client: Sending '%s'\n", message)
    75  	_, err = stream.Write([]byte(message))
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	buf := make([]byte, len(message))
    81  	_, err = io.ReadFull(stream, buf)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	fmt.Printf("Client: Got '%s'\n", buf)
    86  
    87  	return nil
    88  }
    89  
    90  // A wrapper for io.Writer that also logs the message.
    91  type loggingWriter struct{ io.Writer }
    92  
    93  func (w loggingWriter) Write(b []byte) (int, error) {
    94  	fmt.Printf("Server: Got '%s'\n", string(b))
    95  	return w.Writer.Write(b)
    96  }
    97  
    98  // Setup a bare-bones TLS config for the server
    99  func generateTLSConfig() *tls.Config {
   100  	key, err := rsa.GenerateKey(rand.Reader, 1024)
   101  	if err != nil {
   102  		panic(err)
   103  	}
   104  	template := x509.Certificate{SerialNumber: big.NewInt(1)}
   105  	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
   106  	if err != nil {
   107  		panic(err)
   108  	}
   109  	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
   110  	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
   111  
   112  	tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
   113  	if err != nil {
   114  		panic(err)
   115  	}
   116  	return &tls.Config{
   117  		Certificates: []tls.Certificate{tlsCert},
   118  		NextProtos:   []string{"quic-echo-example"},
   119  	}
   120  }