Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Licensed to the Apache Software Foundation (ASF) under one |
| 3 | * or more contributor license agreements. See the NOTICE file |
| 4 | * distributed with this work for additional information |
| 5 | * regarding copyright ownership. The ASF licenses this file |
| 6 | * to you under the Apache License, Version 2.0 (the |
| 7 | * "License"); you may not use this file except in compliance |
| 8 | * with the License. You may obtain a copy of the License at |
| 9 | * |
| 10 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | * |
| 12 | * Unless required by applicable law or agreed to in writing, |
| 13 | * software distributed under the License is distributed on an |
| 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | * KIND, either express or implied. See the License for the |
| 16 | * specific language governing permissions and limitations |
| 17 | * under the License. |
| 18 | */ |
| 19 | |
| 20 | package thrift |
| 21 | |
| 22 | import ( |
John Boiles | 5785279 | 2018-01-05 14:37:05 -0800 | [diff] [blame] | 23 | "context" |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 24 | "net" |
| 25 | "time" |
| 26 | ) |
| 27 | |
| 28 | type TSocket struct { |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 29 | conn *socketConn |
| 30 | addr net.Addr |
| 31 | cfg *TConfiguration |
| 32 | |
lvqian | 81334cd | 2020-03-26 19:08:55 +0800 | [diff] [blame] | 33 | connectTimeout time.Duration |
| 34 | socketTimeout time.Duration |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 35 | } |
| 36 | |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 37 | // Deprecated: Use NewTSocketConf instead. |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 38 | func NewTSocket(hostPort string) (*TSocket, error) { |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 39 | return NewTSocketConf(hostPort, &TConfiguration{ |
| 40 | noPropagation: true, |
| 41 | }) |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 42 | } |
| 43 | |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 44 | // NewTSocketConf creates a net.Conn-backed TTransport, given a host and port. |
| 45 | // |
| 46 | // Example: |
| 47 | // |
| 48 | // trans, err := thrift.NewTSocketConf("localhost:9090", &TConfiguration{ |
| 49 | // ConnectTimeout: time.Second, // Use 0 for no timeout |
| 50 | // SocketTimeout: time.Second, // Use 0 for no timeout |
| 51 | // }) |
| 52 | func NewTSocketConf(hostPort string, conf *TConfiguration) (*TSocket, error) { |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 53 | addr, err := net.ResolveTCPAddr("tcp", hostPort) |
| 54 | if err != nil { |
| 55 | return nil, err |
| 56 | } |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 57 | return NewTSocketFromAddrConf(addr, conf), nil |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 58 | } |
| 59 | |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 60 | // Deprecated: Use NewTSocketConf instead. |
| 61 | func NewTSocketTimeout(hostPort string, connTimeout time.Duration, soTimeout time.Duration) (*TSocket, error) { |
| 62 | return NewTSocketConf(hostPort, &TConfiguration{ |
| 63 | ConnectTimeout: connTimeout, |
| 64 | SocketTimeout: soTimeout, |
| 65 | |
| 66 | noPropagation: true, |
| 67 | }) |
| 68 | } |
| 69 | |
| 70 | // NewTSocketFromAddrConf creates a TSocket from a net.Addr |
| 71 | func NewTSocketFromAddrConf(addr net.Addr, conf *TConfiguration) *TSocket { |
| 72 | return &TSocket{ |
| 73 | addr: addr, |
| 74 | cfg: conf, |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | // Deprecated: Use NewTSocketFromAddrConf instead. |
lvqian | 81334cd | 2020-03-26 19:08:55 +0800 | [diff] [blame] | 79 | func NewTSocketFromAddrTimeout(addr net.Addr, connTimeout time.Duration, soTimeout time.Duration) *TSocket { |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 80 | return NewTSocketFromAddrConf(addr, &TConfiguration{ |
| 81 | ConnectTimeout: connTimeout, |
| 82 | SocketTimeout: soTimeout, |
| 83 | |
| 84 | noPropagation: true, |
| 85 | }) |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 86 | } |
| 87 | |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 88 | // NewTSocketFromConnConf creates a TSocket from an existing net.Conn. |
| 89 | func NewTSocketFromConnConf(conn net.Conn, conf *TConfiguration) *TSocket { |
| 90 | return &TSocket{ |
| 91 | conn: wrapSocketConn(conn), |
| 92 | addr: conn.RemoteAddr(), |
| 93 | cfg: conf, |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | // Deprecated: Use NewTSocketFromConnConf instead. |
Yuxuan 'fishy' Wang | 5dc1d26 | 2020-06-27 08:34:31 -0700 | [diff] [blame] | 98 | func NewTSocketFromConnTimeout(conn net.Conn, socketTimeout time.Duration) *TSocket { |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 99 | return NewTSocketFromConnConf(conn, &TConfiguration{ |
| 100 | SocketTimeout: socketTimeout, |
| 101 | |
| 102 | noPropagation: true, |
| 103 | }) |
| 104 | } |
| 105 | |
| 106 | // SetTConfiguration implements TConfigurationSetter. |
| 107 | // |
| 108 | // It can be used to set connect and socket timeouts. |
| 109 | func (p *TSocket) SetTConfiguration(conf *TConfiguration) { |
| 110 | p.cfg = conf |
lvqian | 81334cd | 2020-03-26 19:08:55 +0800 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | // Sets the connect timeout |
| 114 | func (p *TSocket) SetConnTimeout(timeout time.Duration) error { |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 115 | if p.cfg == nil { |
| 116 | p.cfg = &TConfiguration{ |
| 117 | noPropagation: true, |
| 118 | } |
| 119 | } |
| 120 | p.cfg.ConnectTimeout = timeout |
lvqian | 81334cd | 2020-03-26 19:08:55 +0800 | [diff] [blame] | 121 | return nil |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | // Sets the socket timeout |
lvqian | 81334cd | 2020-03-26 19:08:55 +0800 | [diff] [blame] | 125 | func (p *TSocket) SetSocketTimeout(timeout time.Duration) error { |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 126 | if p.cfg == nil { |
| 127 | p.cfg = &TConfiguration{ |
| 128 | noPropagation: true, |
| 129 | } |
| 130 | } |
| 131 | p.cfg.SocketTimeout = timeout |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 132 | return nil |
| 133 | } |
| 134 | |
| 135 | func (p *TSocket) pushDeadline(read, write bool) { |
| 136 | var t time.Time |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 137 | if timeout := p.cfg.GetSocketTimeout(); timeout > 0 { |
| 138 | t = time.Now().Add(time.Duration(timeout)) |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 139 | } |
| 140 | if read && write { |
| 141 | p.conn.SetDeadline(t) |
| 142 | } else if read { |
| 143 | p.conn.SetReadDeadline(t) |
| 144 | } else if write { |
| 145 | p.conn.SetWriteDeadline(t) |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | // Connects the socket, creating a new socket object if necessary. |
| 150 | func (p *TSocket) Open() error { |
Yuxuan 'fishy' Wang | 05023e8 | 2020-05-26 15:31:20 -0700 | [diff] [blame] | 151 | if p.conn.isValid() { |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 152 | return NewTTransportException(ALREADY_OPEN, "Socket already connected.") |
| 153 | } |
| 154 | if p.addr == nil { |
| 155 | return NewTTransportException(NOT_OPEN, "Cannot open nil address.") |
| 156 | } |
| 157 | if len(p.addr.Network()) == 0 { |
| 158 | return NewTTransportException(NOT_OPEN, "Cannot open bad network name.") |
| 159 | } |
| 160 | if len(p.addr.String()) == 0 { |
| 161 | return NewTTransportException(NOT_OPEN, "Cannot open bad address.") |
| 162 | } |
| 163 | var err error |
Yuxuan 'fishy' Wang | 05023e8 | 2020-05-26 15:31:20 -0700 | [diff] [blame] | 164 | if p.conn, err = createSocketConnFromReturn(net.DialTimeout( |
| 165 | p.addr.Network(), |
| 166 | p.addr.String(), |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 167 | p.cfg.GetConnectTimeout(), |
Yuxuan 'fishy' Wang | 05023e8 | 2020-05-26 15:31:20 -0700 | [diff] [blame] | 168 | )); err != nil { |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 169 | return NewTTransportException(NOT_OPEN, err.Error()) |
| 170 | } |
| 171 | return nil |
| 172 | } |
| 173 | |
Konrad Grochowski | 3b5dacb | 2014-11-24 10:55:31 +0100 | [diff] [blame] | 174 | // Retrieve the underlying net.Conn |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 175 | func (p *TSocket) Conn() net.Conn { |
| 176 | return p.conn |
| 177 | } |
| 178 | |
| 179 | // Returns true if the connection is open |
| 180 | func (p *TSocket) IsOpen() bool { |
Yuxuan 'fishy' Wang | 05023e8 | 2020-05-26 15:31:20 -0700 | [diff] [blame] | 181 | return p.conn.IsOpen() |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 182 | } |
| 183 | |
| 184 | // Closes the socket. |
| 185 | func (p *TSocket) Close() error { |
| 186 | // Close the socket |
| 187 | if p.conn != nil { |
| 188 | err := p.conn.Close() |
| 189 | if err != nil { |
| 190 | return err |
| 191 | } |
| 192 | p.conn = nil |
| 193 | } |
| 194 | return nil |
| 195 | } |
| 196 | |
Jens Geyer | 4c33094 | 2014-11-10 21:22:34 +0100 | [diff] [blame] | 197 | //Returns the remote address of the socket. |
| 198 | func (p *TSocket) Addr() net.Addr { |
| 199 | return p.addr |
| 200 | } |
| 201 | |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 202 | func (p *TSocket) Read(buf []byte) (int, error) { |
Yuxuan 'fishy' Wang | 05023e8 | 2020-05-26 15:31:20 -0700 | [diff] [blame] | 203 | if !p.conn.isValid() { |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 204 | return 0, NewTTransportException(NOT_OPEN, "Connection not open") |
| 205 | } |
| 206 | p.pushDeadline(true, false) |
Yuxuan 'fishy' Wang | cfbb905 | 2020-06-09 13:07:38 -0700 | [diff] [blame] | 207 | // NOTE: Calling any of p.IsOpen, p.conn.read0, or p.conn.IsOpen between |
| 208 | // p.pushDeadline and p.conn.Read could cause the deadline set inside |
| 209 | // p.pushDeadline being reset, thus need to be avoided. |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 210 | n, err := p.conn.Read(buf) |
| 211 | return n, NewTTransportExceptionFromError(err) |
| 212 | } |
| 213 | |
| 214 | func (p *TSocket) Write(buf []byte) (int, error) { |
Yuxuan 'fishy' Wang | 05023e8 | 2020-05-26 15:31:20 -0700 | [diff] [blame] | 215 | if !p.conn.isValid() { |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 216 | return 0, NewTTransportException(NOT_OPEN, "Connection not open") |
| 217 | } |
| 218 | p.pushDeadline(false, true) |
| 219 | return p.conn.Write(buf) |
| 220 | } |
| 221 | |
John Boiles | 5785279 | 2018-01-05 14:37:05 -0800 | [diff] [blame] | 222 | func (p *TSocket) Flush(ctx context.Context) error { |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 223 | return nil |
| 224 | } |
| 225 | |
| 226 | func (p *TSocket) Interrupt() error { |
Yuxuan 'fishy' Wang | 05023e8 | 2020-05-26 15:31:20 -0700 | [diff] [blame] | 227 | if !p.conn.isValid() { |
Jens Geyer | 0e87c46 | 2013-06-18 22:25:07 +0200 | [diff] [blame] | 228 | return nil |
| 229 | } |
| 230 | return p.conn.Close() |
| 231 | } |
Jens Geyer | ca8469e | 2015-07-26 01:25:23 +0200 | [diff] [blame] | 232 | |
| 233 | func (p *TSocket) RemainingBytes() (num_bytes uint64) { |
| 234 | const maxSize = ^uint64(0) |
timestee | 370d751 | 2019-05-20 19:20:42 +0800 | [diff] [blame] | 235 | return maxSize // the truth is, we just don't know unless framed is used |
Jens Geyer | ca8469e | 2015-07-26 01:25:23 +0200 | [diff] [blame] | 236 | } |
Yuxuan 'fishy' Wang | c4d1c0d | 2020-12-16 17:10:48 -0800 | [diff] [blame] | 237 | |
| 238 | var _ TConfigurationSetter = (*TSocket)(nil) |