github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/dsync/dsync-client_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package dsync 19 20 import ( 21 "bytes" 22 "context" 23 "errors" 24 "net/http" 25 "net/url" 26 "time" 27 28 xhttp "github.com/minio/minio/internal/http" 29 "github.com/minio/minio/internal/rest" 30 ) 31 32 // ReconnectRESTClient is a wrapper type for rest.Client which provides reconnect on first failure. 33 type ReconnectRESTClient struct { 34 u *url.URL 35 rest *rest.Client 36 } 37 38 // newClient constructs a ReconnectRESTClient object with addr and endpoint initialized. 39 // It _doesn't_ connect to the remote endpoint. See Call method to see when the 40 // connect happens. 41 func newClient(endpoint string) NetLocker { 42 u, err := url.Parse(endpoint) 43 if err != nil { 44 panic(err) 45 } 46 47 tr := &http.Transport{ 48 Proxy: http.ProxyFromEnvironment, 49 MaxIdleConnsPerHost: 1024, 50 WriteBufferSize: 32 << 10, // 32KiB moving up from 4KiB default 51 ReadBufferSize: 32 << 10, // 32KiB moving up from 4KiB default 52 IdleConnTimeout: 15 * time.Second, 53 ResponseHeaderTimeout: 15 * time.Minute, // Set conservative timeouts for MinIO internode. 54 TLSHandshakeTimeout: 15 * time.Second, 55 ExpectContinueTimeout: 15 * time.Second, 56 // Go net/http automatically unzip if content-type is 57 // gzip disable this feature, as we are always interested 58 // in raw stream. 59 DisableCompression: true, 60 } 61 62 return &ReconnectRESTClient{ 63 u: u, 64 rest: rest.NewClient(u, tr, nil), 65 } 66 } 67 68 // Close closes the underlying socket file descriptor. 69 func (restClient *ReconnectRESTClient) IsOnline() bool { 70 // If rest client has not connected yet there is nothing to close. 71 return restClient.rest != nil 72 } 73 74 func (restClient *ReconnectRESTClient) IsLocal() bool { 75 return false 76 } 77 78 // Close closes the underlying socket file descriptor. 79 func (restClient *ReconnectRESTClient) Close() error { 80 return nil 81 } 82 83 var ( 84 errLockConflict = errors.New("lock conflict") 85 errLockNotFound = errors.New("lock not found") 86 ) 87 88 func toLockError(err error) error { 89 if err == nil { 90 return nil 91 } 92 93 switch err.Error() { 94 case errLockConflict.Error(): 95 return errLockConflict 96 case errLockNotFound.Error(): 97 return errLockNotFound 98 } 99 return err 100 } 101 102 // Call makes a REST call to the remote endpoint using the msgp codec 103 func (restClient *ReconnectRESTClient) Call(method string, args LockArgs) (status bool, err error) { 104 buf, err := args.MarshalMsg(nil) 105 if err != nil { 106 return false, err 107 } 108 body := bytes.NewReader(buf) 109 respBody, err := restClient.rest.Call(context.Background(), method, 110 url.Values{}, body, body.Size()) 111 defer xhttp.DrainBody(respBody) 112 113 switch toLockError(err) { 114 case nil: 115 return true, nil 116 case errLockConflict, errLockNotFound: 117 return false, nil 118 default: 119 return false, err 120 } 121 } 122 123 func (restClient *ReconnectRESTClient) RLock(ctx context.Context, args LockArgs) (status bool, err error) { 124 return restClient.Call("/v1/rlock", args) 125 } 126 127 func (restClient *ReconnectRESTClient) Lock(ctx context.Context, args LockArgs) (status bool, err error) { 128 return restClient.Call("/v1/lock", args) 129 } 130 131 func (restClient *ReconnectRESTClient) RUnlock(ctx context.Context, args LockArgs) (status bool, err error) { 132 return restClient.Call("/v1/runlock", args) 133 } 134 135 func (restClient *ReconnectRESTClient) Unlock(ctx context.Context, args LockArgs) (status bool, err error) { 136 return restClient.Call("/v1/unlock", args) 137 } 138 139 func (restClient *ReconnectRESTClient) Refresh(ctx context.Context, args LockArgs) (refreshed bool, err error) { 140 return restClient.Call("/v1/refresh", args) 141 } 142 143 func (restClient *ReconnectRESTClient) ForceUnlock(ctx context.Context, args LockArgs) (reply bool, err error) { 144 return restClient.Call("/v1/force-unlock", args) 145 } 146 147 func (restClient *ReconnectRESTClient) String() string { 148 return restClient.u.String() 149 }