github.com/mholt/caddy-l4@v0.0.0-20241104153248-ec8fae209322/modules/l4tee/tee.go (about)

     1  // Copyright 2020 Matthew Holt
     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 l4tee
    16  
    17  import (
    18  	"encoding/json"
    19  	"io"
    20  	"net"
    21  
    22  	"github.com/caddyserver/caddy/v2"
    23  	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
    24  	"go.uber.org/zap"
    25  
    26  	"github.com/mholt/caddy-l4/layer4"
    27  )
    28  
    29  func init() {
    30  	caddy.RegisterModule(&Handler{})
    31  }
    32  
    33  // Handler is a layer4 handler that replicates a connection so
    34  // that a branch of handlers can concurrently handle it. Reads
    35  // happen in lock-step with all concurrent branches to
    36  // avoid buffering: if one of the branches (including the main
    37  // handler chain) stops reading from the connection, it will
    38  // block all branches.
    39  type Handler struct {
    40  	// Handlers is the list of handlers that constitute this
    41  	// concurrent branch. Any handlers that do connection
    42  	// matching (which involves recording and rewinding the
    43  	// stream) are unsafe to tee, so do all connection
    44  	// matching before teeing.
    45  	HandlersRaw []json.RawMessage `json:"branch,omitempty" caddy:"namespace=layer4.handlers inline_key=handler"`
    46  
    47  	compiledChain layer4.Handler
    48  	logger        *zap.Logger
    49  }
    50  
    51  // CaddyModule returns the Caddy module information.
    52  func (*Handler) CaddyModule() caddy.ModuleInfo {
    53  	return caddy.ModuleInfo{
    54  		ID:  "layer4.handlers.tee",
    55  		New: func() caddy.Module { return new(Handler) },
    56  	}
    57  }
    58  
    59  // Provision sets up the handler.
    60  func (t *Handler) Provision(ctx caddy.Context) error {
    61  	t.logger = ctx.Logger(t)
    62  
    63  	// set up the handler chain
    64  	mods, err := ctx.LoadModule(t, "HandlersRaw")
    65  	if err != nil {
    66  		return err
    67  	}
    68  	handlers := make(layer4.Handlers, 0)
    69  	for _, mod := range mods.([]interface{}) {
    70  		handlers = append(handlers, mod.(layer4.NextHandler))
    71  	}
    72  	t.compiledChain = handlers.Compile()
    73  
    74  	return nil
    75  }
    76  
    77  // Handle handles the connection.
    78  func (t *Handler) Handle(cx *layer4.Connection, next layer4.Handler) error {
    79  	// what is read by the next handler will also be
    80  	// read by the branch handlers; this is done by
    81  	// writing conn's reads into a pipe, and having
    82  	// the branch read from the pipe
    83  	pr, pw := io.Pipe()
    84  
    85  	// this is the conn we pass to the next handler;
    86  	// anything read by it will be teed into the pipe
    87  	// (it also needs a pointer to the pipe, so it can
    88  	// close the pipe when the connection closes,
    89  	// otherwise we'll leak the goroutine, yikes!)
    90  	nextc := *cx
    91  	nextc.Conn = nextConn{
    92  		Conn:   cx,
    93  		Reader: io.TeeReader(cx, pw),
    94  		pipe:   pw,
    95  	}
    96  
    97  	// this is the conn we pass to the branch
    98  	branchc := *cx
    99  	branchc.Conn = teeConn{
   100  		Conn:   cx,
   101  		Reader: pr,
   102  	}
   103  
   104  	// run the branch concurrently
   105  	go func() {
   106  		err := t.compiledChain.Handle(&branchc)
   107  		if err != nil {
   108  			t.logger.Error("handling connection in branch", zap.String("remote", cx.RemoteAddr().String()), zap.Error(err))
   109  		}
   110  	}()
   111  
   112  	return next.Handle(&nextc)
   113  }
   114  
   115  // UnmarshalCaddyfile sets up the Handler from Caddyfile tokens. Syntax:
   116  //
   117  //	tee {
   118  //		<handler>
   119  //		<handler> [<args>]
   120  //	}
   121  func (t *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
   122  	d.Next() // consume wrapper name
   123  
   124  	// No same-line options are supported
   125  	if d.CountRemainingArgs() > 0 {
   126  		return d.ArgErr()
   127  	}
   128  
   129  	if err := layer4.ParseCaddyfileNestedHandlers(d, &t.HandlersRaw); err != nil {
   130  		return err
   131  	}
   132  
   133  	return nil
   134  }
   135  
   136  // teeConn is a connection wrapper that reads
   137  // from a different reader.
   138  type teeConn struct {
   139  	net.Conn
   140  	io.Reader
   141  }
   142  
   143  func (tc teeConn) Read(p []byte) (int, error) {
   144  	return tc.Reader.Read(p)
   145  }
   146  
   147  // nextConn is a connection wrapper that reads from
   148  // a different reader, and when the reader returns
   149  // EOF, the associated pipe is closed.
   150  type nextConn struct {
   151  	net.Conn
   152  	io.Reader
   153  	pipe *io.PipeWriter
   154  }
   155  
   156  func (nc nextConn) Read(p []byte) (n int, err error) {
   157  	n, err = nc.Reader.Read(p)
   158  	if err == io.EOF {
   159  		_ = nc.pipe.Close()
   160  	}
   161  	return
   162  }
   163  
   164  // Interface guards
   165  var (
   166  	_ caddyfile.Unmarshaler = (*Handler)(nil)
   167  	_ layer4.NextHandler    = (*Handler)(nil)
   168  )