| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * Minio Cloud Storage, (C) 2016 Minio, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 05:50:50 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 	"bufio" | 
					
						
							|  |  |  | 	"crypto/tls" | 
					
						
							| 
									
										
										
										
											2016-10-19 02:46:33 +08:00
										 |  |  | 	"crypto/x509" | 
					
						
							| 
									
										
										
										
											2016-08-27 18:16:52 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2016-11-20 09:37:57 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 	"io" | 
					
						
							|  |  |  | 	"net" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | 	"net/rpc" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2016-10-18 05:31:33 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | // defaultDialTimeout is used for non-secure connection.
 | 
					
						
							|  |  |  | const defaultDialTimeout = 3 * time.Second | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | // RPCClient is a reconnectable RPC client on Call().
 | 
					
						
							| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | type RPCClient struct { | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	sync.Mutex                  // Mutex to lock net rpc client.
 | 
					
						
							|  |  |  | 	netRPCClient    *rpc.Client // Base RPC client to make any RPC call.
 | 
					
						
							|  |  |  | 	serverAddr      string      // RPC server address.
 | 
					
						
							|  |  |  | 	serviceEndpoint string      // Endpoint on the server to make any RPC call.
 | 
					
						
							|  |  |  | 	secureConn      bool        // Make TLS connection to RPC server or not.
 | 
					
						
							| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | // newRPCClient returns new RPCClient object with given serverAddr and serviceEndpoint.
 | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | // It does lazy connect to the remote endpoint on Call().
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | func newRPCClient(serverAddr, serviceEndpoint string, secureConn bool) *RPCClient { | 
					
						
							| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | 	return &RPCClient{ | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 		serverAddr:      serverAddr, | 
					
						
							|  |  |  | 		serviceEndpoint: serviceEndpoint, | 
					
						
							|  |  |  | 		secureConn:      secureConn, | 
					
						
							| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | // dial tries to establish a connection to serverAddr in a safe manner.
 | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | // If there is a valid rpc.Cliemt, it returns that else creates a new one.
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | func (rpcClient *RPCClient) dial() (netRPCClient *rpc.Client, err error) { | 
					
						
							|  |  |  | 	rpcClient.Lock() | 
					
						
							|  |  |  | 	defer rpcClient.Unlock() | 
					
						
							| 
									
										
										
										
											2016-08-27 18:16:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 	// Nothing to do as we already have valid connection.
 | 
					
						
							|  |  |  | 	if rpcClient.netRPCClient != nil { | 
					
						
							|  |  |  | 		return rpcClient.netRPCClient, nil | 
					
						
							| 
									
										
										
										
											2016-08-16 05:33:48 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var conn net.Conn | 
					
						
							|  |  |  | 	if rpcClient.secureConn { | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 		var hostname string | 
					
						
							|  |  |  | 		if hostname, _, err = net.SplitHostPort(rpcClient.serverAddr); err != nil { | 
					
						
							|  |  |  | 			err = &net.OpError{ | 
					
						
							| 
									
										
										
										
											2016-11-20 09:37:57 +08:00
										 |  |  | 				Op:   "dial-http", | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 				Net:  rpcClient.serverAddr + rpcClient.serviceEndpoint, | 
					
						
							| 
									
										
										
										
											2016-11-20 09:37:57 +08:00
										 |  |  | 				Addr: nil, | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 				Err:  fmt.Errorf("Unable to parse server address <%s>: %s", rpcClient.serverAddr, err.Error()), | 
					
						
							| 
									
										
										
										
											2016-11-20 09:37:57 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2016-10-20 22:43:31 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// ServerName in tls.Config needs to be specified to support SNI certificates.
 | 
					
						
							|  |  |  | 		conn, err = tls.Dial("tcp", rpcClient.serverAddr, &tls.Config{ServerName: hostname, RootCAs: globalRootCAs}) | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 		// Dial with a timeout.
 | 
					
						
							|  |  |  | 		conn, err = net.DialTimeout("tcp", rpcClient.serverAddr, defaultDialTimeout) | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-27 18:16:52 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 		// Print RPC connection errors that are worthy to display in log.
 | 
					
						
							| 
									
										
										
										
											2016-10-19 02:46:33 +08:00
										 |  |  | 		switch err.(type) { | 
					
						
							|  |  |  | 		case x509.HostnameError: | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 			errorIf(err, "Unable to establish secure connection to %s", rpcClient.serverAddr) | 
					
						
							| 
									
										
										
										
											2016-10-19 02:46:33 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-20 09:37:57 +08:00
										 |  |  | 		return nil, &net.OpError{ | 
					
						
							|  |  |  | 			Op:   "dial-http", | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 			Net:  rpcClient.serverAddr + rpcClient.serviceEndpoint, | 
					
						
							| 
									
										
										
										
											2016-11-20 09:37:57 +08:00
										 |  |  | 			Addr: nil, | 
					
						
							|  |  |  | 			Err:  err, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-08-27 18:16:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	io.WriteString(conn, "CONNECT "+rpcClient.serviceEndpoint+" HTTP/1.0\n\n") | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 17:30:54 +08:00
										 |  |  | 	// Require successful HTTP response before switching to RPC protocol.
 | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 	resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"}) | 
					
						
							|  |  |  | 	if err == nil && resp.Status == "200 Connected to Go RPC" { | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 		netRPCClient := rpc.NewClient(conn) | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 		if netRPCClient == nil { | 
					
						
							| 
									
										
										
										
											2016-11-20 09:37:57 +08:00
										 |  |  | 			return nil, &net.OpError{ | 
					
						
							|  |  |  | 				Op:   "dial-http", | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 				Net:  rpcClient.serverAddr + rpcClient.serviceEndpoint, | 
					
						
							| 
									
										
										
										
											2016-11-20 09:37:57 +08:00
										 |  |  | 				Addr: nil, | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 				Err:  fmt.Errorf("Unable to initialize new rpc.Client, %s", errUnexpected), | 
					
						
							| 
									
										
										
										
											2016-11-20 09:37:57 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		rpcClient.netRPCClient = netRPCClient | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return netRPCClient, nil | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	conn.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		err = errors.New("unexpected HTTP response: " + resp.Status) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 	return nil, &net.OpError{ | 
					
						
							|  |  |  | 		Op:   "dial-http", | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 		Net:  rpcClient.serverAddr + rpcClient.serviceEndpoint, | 
					
						
							| 
									
										
										
										
											2016-09-30 14:42:37 +08:00
										 |  |  | 		Addr: nil, | 
					
						
							|  |  |  | 		Err:  err, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Call makes a RPC call to the remote endpoint using the default codec, namely encoding/gob.
 | 
					
						
							|  |  |  | func (rpcClient *RPCClient) Call(serviceMethod string, args interface{}, reply interface{}) error { | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 	// Get a new or existing rpc.Client.
 | 
					
						
							|  |  |  | 	netRPCClient, err := rpcClient.dial() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 	return netRPCClient.Call(serviceMethod, args, reply) | 
					
						
							| 
									
										
										
										
											2016-08-27 18:16:52 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | // Close closes underlying rpc.Client.
 | 
					
						
							| 
									
										
										
										
											2016-08-27 18:16:52 +08:00
										 |  |  | func (rpcClient *RPCClient) Close() error { | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	rpcClient.Lock() | 
					
						
							| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 	if rpcClient.netRPCClient != nil { | 
					
						
							|  |  |  | 		// We make a copy of rpc.Client and unlock it immediately so that another
 | 
					
						
							|  |  |  | 		// goroutine could try to dial or close in parallel.
 | 
					
						
							|  |  |  | 		netRPCClient := rpcClient.netRPCClient | 
					
						
							|  |  |  | 		rpcClient.netRPCClient = nil | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 		rpcClient.Unlock() | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return netRPCClient.Close() | 
					
						
							| 
									
										
										
										
											2016-08-27 18:16:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-23 23:12:19 +08:00
										 |  |  | 	rpcClient.Unlock() | 
					
						
							| 
									
										
										
										
											2016-12-18 10:17:40 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-08-15 15:00:18 +08:00
										 |  |  | } |