| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2017 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"reflect" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	xnet "github.com/minio/minio/pkg/net" | 
					
						
							|  |  |  | 	trace "github.com/minio/minio/pkg/trace" | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // recordRequest - records the first recLen bytes
 | 
					
						
							|  |  |  | // of a given io.Reader
 | 
					
						
							|  |  |  | type recordRequest struct { | 
					
						
							|  |  |  | 	// Data source to record
 | 
					
						
							|  |  |  | 	io.Reader | 
					
						
							|  |  |  | 	// Response body should be logged
 | 
					
						
							|  |  |  | 	logBody bool | 
					
						
							|  |  |  | 	// Internal recording buffer
 | 
					
						
							|  |  |  | 	buf bytes.Buffer | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *recordRequest) Read(p []byte) (n int, err error) { | 
					
						
							|  |  |  | 	n, err = r.Reader.Read(p) | 
					
						
							|  |  |  | 	if r.logBody { | 
					
						
							|  |  |  | 		r.buf.Write(p[:n]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return n, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return n, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Return the bytes that were recorded.
 | 
					
						
							|  |  |  | func (r *recordRequest) Data() []byte { | 
					
						
							|  |  |  | 	return r.buf.Bytes() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // recordResponseWriter - records the first recLen bytes
 | 
					
						
							|  |  |  | // of a given http.ResponseWriter
 | 
					
						
							|  |  |  | type recordResponseWriter struct { | 
					
						
							|  |  |  | 	// Data source to record
 | 
					
						
							|  |  |  | 	http.ResponseWriter | 
					
						
							|  |  |  | 	// Response body should be logged
 | 
					
						
							|  |  |  | 	logBody bool | 
					
						
							|  |  |  | 	// Internal recording buffer
 | 
					
						
							|  |  |  | 	headers bytes.Buffer | 
					
						
							|  |  |  | 	body    bytes.Buffer | 
					
						
							|  |  |  | 	// The status code of the current HTTP request
 | 
					
						
							|  |  |  | 	statusCode int | 
					
						
							|  |  |  | 	// Indicate if headers are written in the log
 | 
					
						
							|  |  |  | 	headersLogged bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Write the headers into the given buffer
 | 
					
						
							|  |  |  | func writeHeaders(w io.Writer, statusCode int, headers http.Header) { | 
					
						
							|  |  |  | 	fmt.Fprintf(w, "%d %s\n", statusCode, http.StatusText(statusCode)) | 
					
						
							|  |  |  | 	for k, v := range headers { | 
					
						
							|  |  |  | 		fmt.Fprintf(w, "%s: %s\n", k, v[0]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Record the headers.
 | 
					
						
							|  |  |  | func (r *recordResponseWriter) WriteHeader(i int) { | 
					
						
							|  |  |  | 	r.statusCode = i | 
					
						
							|  |  |  | 	if !r.headersLogged { | 
					
						
							|  |  |  | 		writeHeaders(&r.headers, i, r.ResponseWriter.Header()) | 
					
						
							|  |  |  | 		r.headersLogged = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	r.ResponseWriter.WriteHeader(i) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (r *recordResponseWriter) Write(p []byte) (n int, err error) { | 
					
						
							|  |  |  | 	n, err = r.ResponseWriter.Write(p) | 
					
						
							|  |  |  | 	if !r.headersLogged { | 
					
						
							| 
									
										
										
										
											2018-08-09 03:34:42 +08:00
										 |  |  | 		// We assume the response code to be '200 OK' when WriteHeader() is not called,
 | 
					
						
							|  |  |  | 		// that way following Golang HTTP response behavior.
 | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 		writeHeaders(&r.headers, http.StatusOK, r.ResponseWriter.Header()) | 
					
						
							|  |  |  | 		r.headersLogged = true | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-09 03:34:42 +08:00
										 |  |  | 	if (r.statusCode != http.StatusOK && r.statusCode != http.StatusPartialContent && r.statusCode != 0) || r.logBody { | 
					
						
							|  |  |  | 		// Always logging error responses.
 | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 		r.body.Write(p) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return n, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Calls the underlying Flush.
 | 
					
						
							|  |  |  | func (r *recordResponseWriter) Flush() { | 
					
						
							|  |  |  | 	r.ResponseWriter.(http.Flusher).Flush() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Return response body.
 | 
					
						
							|  |  |  | func (r *recordResponseWriter) Body() []byte { | 
					
						
							|  |  |  | 	return r.body.Bytes() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 04:55:13 +08:00
										 |  |  | // getOpName sanitizes the operation name for mc
 | 
					
						
							|  |  |  | func getOpName(name string) (op string) { | 
					
						
							|  |  |  | 	op = strings.TrimPrefix(name, "github.com/minio/minio/cmd.") | 
					
						
							|  |  |  | 	op = strings.TrimSuffix(op, "Handler-fm") | 
					
						
							|  |  |  | 	op = strings.Replace(op, "objectAPIHandlers", "s3", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "webAPIHandlers", "s3", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "adminAPIHandlers", "admin", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "(*storageRESTServer)", "internal", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "(*peerRESTServer)", "internal", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "(*lockRESTServer)", "internal", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "stsAPIHandlers", "sts", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "LivenessCheckHandler", "healthcheck", 1) | 
					
						
							|  |  |  | 	op = strings.Replace(op, "ReadinessCheckHandler", "healthcheck", 1) | 
					
						
							|  |  |  | 	return op | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | // Trace gets trace of http request
 | 
					
						
							|  |  |  | func Trace(f http.HandlerFunc, logBody bool, w http.ResponseWriter, r *http.Request) trace.Info { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-19 04:55:13 +08:00
										 |  |  | 	name := getOpName(runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()) | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	bodyPlaceHolder := []byte("<BODY>") | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	var reqBodyRecorder *recordRequest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t := trace.Info{FuncName: name} | 
					
						
							|  |  |  | 	reqBodyRecorder = &recordRequest{Reader: r.Body, logBody: logBody} | 
					
						
							|  |  |  | 	r.Body = ioutil.NopCloser(reqBodyRecorder) | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	host, err := xnet.ParseHost(GetLocalPeer(globalEndpoints)) | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		t.NodeName = host.Name | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-19 04:55:13 +08:00
										 |  |  | 	rq := trace.RequestInfo{Time: time.Now().UTC(), Method: r.Method, Path: r.URL.Path, RawQuery: r.URL.RawQuery, Client: r.RemoteAddr} | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	rq.Headers = cloneHeader(r.Header) | 
					
						
							|  |  |  | 	rq.Headers.Set("Content-Length", strconv.Itoa(int(r.ContentLength))) | 
					
						
							|  |  |  | 	rq.Headers.Set("Host", r.Host) | 
					
						
							|  |  |  | 	for _, enc := range r.TransferEncoding { | 
					
						
							|  |  |  | 		rq.Headers.Add("Transfer-Encoding", enc) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if logBody { | 
					
						
							|  |  |  | 		// If body logging is disabled then we print <BODY> as a placeholder
 | 
					
						
							|  |  |  | 		// for the actual body.
 | 
					
						
							|  |  |  | 		rq.Body = reqBodyRecorder.Data() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		rq.Body = bodyPlaceHolder | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Setup a http response body recorder
 | 
					
						
							|  |  |  | 	respBodyRecorder := &recordResponseWriter{ResponseWriter: w, logBody: logBody} | 
					
						
							|  |  |  | 	f(respBodyRecorder, r) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rs := trace.ResponseInfo{Time: time.Now().UTC()} | 
					
						
							|  |  |  | 	rs.Headers = cloneHeader(respBodyRecorder.Header()) | 
					
						
							|  |  |  | 	rs.StatusCode = respBodyRecorder.statusCode | 
					
						
							|  |  |  | 	if rs.StatusCode == 0 { | 
					
						
							|  |  |  | 		rs.StatusCode = http.StatusOK | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	bodyContents := respBodyRecorder.Body() | 
					
						
							|  |  |  | 	if bodyContents != nil { | 
					
						
							|  |  |  | 		rs.Body = bodyContents | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !logBody { | 
					
						
							|  |  |  | 		// If there was no error response and body logging is disabled
 | 
					
						
							|  |  |  | 		// then we print <BODY> as a placeholder for the actual body.
 | 
					
						
							|  |  |  | 		rs.Body = bodyPlaceHolder | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	t.ReqInfo = rq | 
					
						
							|  |  |  | 	t.RespInfo = rs | 
					
						
							|  |  |  | 	return t | 
					
						
							| 
									
										
										
										
											2017-10-25 10:04:51 +08:00
										 |  |  | } |