github.com/Jeffail/benthos/v3@v3.65.0/lib/input/socket.go (about) 1 package input 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "io" 8 "net" 9 "strings" 10 "sync" 11 "time" 12 13 "github.com/Jeffail/benthos/v3/internal/codec" 14 "github.com/Jeffail/benthos/v3/internal/docs" 15 "github.com/Jeffail/benthos/v3/lib/input/reader" 16 "github.com/Jeffail/benthos/v3/lib/log" 17 "github.com/Jeffail/benthos/v3/lib/message" 18 "github.com/Jeffail/benthos/v3/lib/metrics" 19 "github.com/Jeffail/benthos/v3/lib/types" 20 ) 21 22 //------------------------------------------------------------------------------ 23 24 func init() { 25 Constructors[TypeSocket] = TypeSpec{ 26 constructor: fromSimpleConstructor(NewSocket), 27 Summary: ` 28 Connects to a tcp or unix socket and consumes a continuous stream of messages.`, 29 FieldSpecs: docs.FieldSpecs{ 30 docs.FieldCommon("network", "A network type to assume (unix|tcp).").HasOptions( 31 "unix", "tcp", 32 ), 33 docs.FieldCommon("address", "The address to connect to.", "/tmp/benthos.sock", "127.0.0.1:6000"), 34 codec.ReaderDocs.AtVersion("3.42.0"), 35 docs.FieldDeprecated("delimiter"), 36 docs.FieldDeprecated("multipart"), 37 docs.FieldAdvanced("max_buffer", "The maximum message buffer size. Must exceed the largest message to be consumed."), 38 }, 39 Categories: []Category{ 40 CategoryNetwork, 41 }, 42 } 43 } 44 45 //------------------------------------------------------------------------------ 46 47 // SocketConfig contains configuration values for the Socket input type. 48 type SocketConfig struct { 49 Network string `json:"network" yaml:"network"` 50 Address string `json:"address" yaml:"address"` 51 Codec string `json:"codec" yaml:"codec"` 52 MaxBuffer int `json:"max_buffer" yaml:"max_buffer"` 53 // TODO: V4 remove these fields. 54 Multipart bool `json:"multipart" yaml:"multipart"` 55 Delim string `json:"delimiter" yaml:"delimiter"` 56 } 57 58 // NewSocketConfig creates a new SocketConfig with default values. 59 func NewSocketConfig() SocketConfig { 60 return SocketConfig{ 61 Network: "unix", 62 Address: "/tmp/benthos.sock", 63 Codec: "lines", 64 Multipart: false, 65 MaxBuffer: 1000000, 66 Delim: "", 67 } 68 } 69 70 //------------------------------------------------------------------------------ 71 72 // NewSocket creates a new Socket input type. 73 func NewSocket(conf Config, mgr types.Manager, log log.Modular, stats metrics.Type) (Type, error) { 74 rdr, err := newSocketClient(conf.Socket, log) 75 if err != nil { 76 return nil, err 77 } 78 // TODO: Consider removing the async cut off here. It adds an overhead and 79 // we can get the same results by making sure that the async readers forward 80 // CloseAsync all the way through. We would need it to be configurable as it 81 // wouldn't be appropriate for inputs that have real acks. 82 return NewAsyncReader(TypeSocket, true, reader.NewAsyncCutOff(reader.NewAsyncPreserver(rdr)), log, stats) 83 } 84 85 //------------------------------------------------------------------------------ 86 87 type socketClient struct { 88 log log.Modular 89 90 conf SocketConfig 91 codecCtor codec.ReaderConstructor 92 93 codecMut sync.Mutex 94 codec codec.Reader 95 } 96 97 func newSocketClient(conf SocketConfig, logger log.Modular) (*socketClient, error) { 98 switch conf.Network { 99 case "tcp", "unix": 100 default: 101 return nil, fmt.Errorf("socket network '%v' is not supported by this input", conf.Network) 102 } 103 104 if len(conf.Delim) > 0 { 105 conf.Codec = "delim:" + conf.Delim 106 } 107 if conf.Multipart && !strings.HasSuffix(conf.Codec, "/multipart") { 108 conf.Codec += "/multipart" 109 } 110 111 codecConf := codec.NewReaderConfig() 112 codecConf.MaxScanTokenSize = conf.MaxBuffer 113 ctor, err := codec.GetReader(conf.Codec, codecConf) 114 if err != nil { 115 return nil, err 116 } 117 118 return &socketClient{ 119 log: logger, 120 conf: conf, 121 codecCtor: ctor, 122 }, nil 123 } 124 125 // ConnectWithContext attempts to establish a connection to the target S3 bucket 126 // and any relevant queues used to traverse the objects (SQS, etc). 127 func (s *socketClient) ConnectWithContext(ctx context.Context) error { 128 s.codecMut.Lock() 129 defer s.codecMut.Unlock() 130 131 if s.codec != nil { 132 return nil 133 } 134 135 conn, err := net.Dial(s.conf.Network, s.conf.Address) 136 if err != nil { 137 return err 138 } 139 140 if s.codec, err = s.codecCtor("", conn, func(ctx context.Context, err error) error { 141 return nil 142 }); err != nil { 143 conn.Close() 144 return err 145 } 146 147 s.log.Infof("Consuming from socket at '%v://%v'\n", s.conf.Network, s.conf.Address) 148 return nil 149 } 150 151 // ReadWithContext attempts to read a new message from the target S3 bucket. 152 func (s *socketClient) ReadWithContext(ctx context.Context) (types.Message, reader.AsyncAckFn, error) { 153 s.codecMut.Lock() 154 codec := s.codec 155 s.codecMut.Unlock() 156 157 if codec == nil { 158 return nil, nil, types.ErrNotConnected 159 } 160 161 parts, codecAckFn, err := codec.Next(ctx) 162 if err != nil { 163 if errors.Is(err, context.Canceled) || 164 errors.Is(err, context.DeadlineExceeded) { 165 err = types.ErrTimeout 166 } 167 if err != types.ErrTimeout { 168 s.codecMut.Lock() 169 if s.codec != nil && s.codec == codec { 170 s.codec.Close(ctx) 171 s.codec = nil 172 } 173 s.codecMut.Unlock() 174 } 175 if errors.Is(err, io.EOF) { 176 return nil, nil, types.ErrTimeout 177 } 178 return nil, nil, err 179 } 180 181 // We simply bounce rejected messages in a loop downstream so there's no 182 // benefit to aggregating acks. 183 _ = codecAckFn(context.Background(), nil) 184 185 msg := message.New(nil) 186 msg.Append(parts...) 187 188 if msg.Len() == 0 { 189 return nil, nil, types.ErrTimeout 190 } 191 192 return msg, func(rctx context.Context, res types.Response) error { 193 return nil 194 }, nil 195 } 196 197 // CloseAsync begins cleaning up resources used by this reader asynchronously. 198 func (s *socketClient) CloseAsync() { 199 s.codecMut.Lock() 200 if s.codec != nil { 201 s.codec.Close(context.Background()) 202 s.codec = nil 203 } 204 s.codecMut.Unlock() 205 } 206 207 // WaitForClose will block until either the reader is closed or a specified 208 // timeout occurs. 209 func (s *socketClient) WaitForClose(time.Duration) error { 210 return nil 211 }