| 
									
										
										
										
											2022-03-28 22:23:47 +02:00
										 |  |  | //go:build debug | 
					
						
							| 
									
										
										
										
											2017-05-01 15:31:57 +02:00
										 |  |  | // +build debug | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package debug | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-05-01 19:30:52 +02:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2017-05-01 15:31:57 +02:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"net/http/httputil" | 
					
						
							| 
									
										
										
										
											2017-05-01 19:30:52 +02:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2017-07-23 14:21:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/errors" | 
					
						
							| 
									
										
										
										
											2017-05-01 15:31:57 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 19:30:52 +02:00
										 |  |  | type eofDetectRoundTripper struct { | 
					
						
							|  |  |  | 	http.RoundTripper | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type eofDetectReader struct { | 
					
						
							|  |  |  | 	eofSeen bool | 
					
						
							|  |  |  | 	rd      io.ReadCloser | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (rd *eofDetectReader) Read(p []byte) (n int, err error) { | 
					
						
							|  |  |  | 	n, err = rd.rd.Read(p) | 
					
						
							|  |  |  | 	if err == io.EOF { | 
					
						
							|  |  |  | 		rd.eofSeen = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return n, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (rd *eofDetectReader) Close() error { | 
					
						
							|  |  |  | 	if !rd.eofSeen { | 
					
						
							|  |  |  | 		buf, err := ioutil.ReadAll(rd) | 
					
						
							|  |  |  | 		msg := fmt.Sprintf("body not drained, %d bytes not read", len(buf)) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			msg += fmt.Sprintf(", error: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if len(buf) > 0 { | 
					
						
							|  |  |  | 			if len(buf) > 20 { | 
					
						
							|  |  |  | 				buf = append(buf[:20], []byte("...")...) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			msg += fmt.Sprintf(", body: %q", buf) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		fmt.Fprintln(os.Stderr, msg) | 
					
						
							|  |  |  | 		Log("%s: %+v", msg, errors.New("Close()")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return rd.rd.Close() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (tr eofDetectRoundTripper) RoundTrip(req *http.Request) (res *http.Response, err error) { | 
					
						
							|  |  |  | 	res, err = tr.RoundTripper.RoundTrip(req) | 
					
						
							| 
									
										
										
										
											2017-06-09 22:32:42 +02:00
										 |  |  | 	if res != nil && res.Body != nil { | 
					
						
							|  |  |  | 		res.Body = &eofDetectReader{rd: res.Body} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-01 19:30:52 +02:00
										 |  |  | 	return res, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 15:31:57 +02:00
										 |  |  | type loggingRoundTripper struct { | 
					
						
							|  |  |  | 	http.RoundTripper | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RoundTripper returns a new http.RoundTripper which logs all requests (if | 
					
						
							|  |  |  | // debug is enabled). When debug is not enabled, upstream is returned. | 
					
						
							|  |  |  | func RoundTripper(upstream http.RoundTripper) http.RoundTripper { | 
					
						
							| 
									
										
										
										
											2020-09-17 21:40:17 +02:00
										 |  |  | 	eofRoundTripper := eofDetectRoundTripper{upstream} | 
					
						
							|  |  |  | 	if opts.isEnabled { | 
					
						
							|  |  |  | 		// only use loggingRoundTripper if the debug log is configured | 
					
						
							|  |  |  | 		return loggingRoundTripper{eofRoundTripper} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return eofRoundTripper | 
					
						
							| 
									
										
										
										
											2017-05-01 15:31:57 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (tr loggingRoundTripper) RoundTrip(req *http.Request) (res *http.Response, err error) { | 
					
						
							| 
									
										
										
										
											2021-08-04 22:19:44 +02:00
										 |  |  | 	// save original auth and redact it | 
					
						
							|  |  |  | 	origAuth, hasAuth := req.Header["Authorization"] | 
					
						
							|  |  |  | 	if hasAuth { | 
					
						
							|  |  |  | 		req.Header["Authorization"] = []string{"**redacted**"} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 15:31:57 +02:00
										 |  |  | 	trace, err := httputil.DumpRequestOut(req, false) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		Log("DumpRequestOut() error: %v\n", err) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		Log("------------  HTTP REQUEST -----------\n%s", trace) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-04 22:19:44 +02:00
										 |  |  | 	// restore auth | 
					
						
							|  |  |  | 	if hasAuth { | 
					
						
							|  |  |  | 		req.Header["Authorization"] = origAuth | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-01 15:31:57 +02:00
										 |  |  | 	res, err = tr.RoundTripper.RoundTrip(req) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		Log("RoundTrip() returned error: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if res != nil { | 
					
						
							|  |  |  | 		trace, err := httputil.DumpResponse(res, false) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			Log("DumpResponse() error: %v\n", err) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			Log("------------  HTTP RESPONSE ----------\n%s", trace) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return res, err | 
					
						
							|  |  |  | } |