blob: dfd0913abcdbe5efedea2ea2e8e5c8dd867631a7 [file] [log] [blame]
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -07001/*
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
20package thrift
21
22import (
Yuxuan 'fishy' Wang6f33b042022-02-03 10:44:53 -080023 "errors"
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -070024 "net"
Yuxuan 'fishy' Wang39d72782022-01-08 01:03:57 -080025 "sync/atomic"
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -070026)
27
28// socketConn is a wrapped net.Conn that tries to do connectivity check.
29type socketConn struct {
30 net.Conn
31
Yuxuan 'fishy' Wangcfbb9052020-06-09 13:07:38 -070032 buffer [1]byte
Yuxuan 'fishy' Wang14489342023-02-02 10:43:36 -080033 closed atomic.Int32
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -070034}
35
36var _ net.Conn = (*socketConn)(nil)
37
38// createSocketConnFromReturn is a language sugar to help create socketConn from
39// return values of functions like net.Dial, tls.Dial, net.Listener.Accept, etc.
40func createSocketConnFromReturn(conn net.Conn, err error) (*socketConn, error) {
41 if err != nil {
42 return nil, err
43 }
44 return &socketConn{
45 Conn: conn,
46 }, nil
47}
48
49// wrapSocketConn wraps an existing net.Conn into *socketConn.
50func wrapSocketConn(conn net.Conn) *socketConn {
51 // In case conn is already wrapped,
52 // return it as-is and avoid double wrapping.
53 if sc, ok := conn.(*socketConn); ok {
54 return sc
55 }
56
57 return &socketConn{
58 Conn: conn,
59 }
60}
61
62// isValid checks whether there's a valid connection.
63//
64// It's nil safe, and returns false if sc itself is nil, or if the underlying
65// connection is nil.
66//
67// It's the same as the previous implementation of TSocket.IsOpen and
68// TSSLSocket.IsOpen before we added connectivity check.
69func (sc *socketConn) isValid() bool {
Yuxuan 'fishy' Wang14489342023-02-02 10:43:36 -080070 return sc != nil && sc.Conn != nil && sc.closed.Load() == 0
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -070071}
72
73// IsOpen checks whether the connection is open.
74//
75// It's nil safe, and returns false if sc itself is nil, or if the underlying
76// connection is nil.
77//
78// Otherwise, it tries to do a connectivity check and returns the result.
Yuxuan 'fishy' Wangcfbb9052020-06-09 13:07:38 -070079//
80// It also has the side effect of resetting the previously set read deadline on
81// the socket. As a result, it shouldn't be called between setting read deadline
82// and doing actual read.
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -070083func (sc *socketConn) IsOpen() bool {
84 if !sc.isValid() {
85 return false
86 }
Yuxuan 'fishy' Wang6f33b042022-02-03 10:44:53 -080087 if err := sc.checkConn(); err != nil {
88 if !errors.Is(err, net.ErrClosed) {
89 // The connectivity check failed and the error is not
90 // that the connection is already closed, we need to
91 // close the connection explicitly here to avoid
92 // connection leaks.
93 sc.Close()
94 }
95 return false
96 }
97 return true
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -070098}
99
100// Read implements io.Reader.
101//
102// On Windows, it behaves the same as the underlying net.Conn.Read.
103//
104// On non-Windows, it treats len(p) == 0 as a connectivity check instead of
105// readability check, which means instead of blocking until there's something to
106// read (readability check), or always return (0, nil) (the default behavior of
107// go's stdlib implementation on non-Windows), it never blocks, and will return
108// an error if the connection is lost.
109func (sc *socketConn) Read(p []byte) (n int, err error) {
110 if len(p) == 0 {
111 return 0, sc.read0()
112 }
113
Yuxuan 'fishy' Wangb93fafd2020-08-02 13:36:54 -0700114 return sc.Conn.Read(p)
Yuxuan 'fishy' Wang05023e82020-05-26 15:31:20 -0700115}
Yuxuan 'fishy' Wang39d72782022-01-08 01:03:57 -0800116
117func (sc *socketConn) Close() error {
118 if !sc.isValid() {
119 // Already closed
120 return net.ErrClosed
121 }
Yuxuan 'fishy' Wang14489342023-02-02 10:43:36 -0800122 sc.closed.Store(1)
Yuxuan 'fishy' Wang39d72782022-01-08 01:03:57 -0800123 return sc.Conn.Close()
124}