github.com/ohlinux/git-lfs@v1.5.4/httputil/response.go (about) 1 package httputil 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net/http" 9 "regexp" 10 11 "github.com/git-lfs/git-lfs/auth" 12 "github.com/git-lfs/git-lfs/config" 13 "github.com/git-lfs/git-lfs/errors" 14 ) 15 16 var ( 17 lfsMediaTypeRE = regexp.MustCompile(`\Aapplication/vnd\.git\-lfs\+json(;|\z)`) 18 jsonMediaTypeRE = regexp.MustCompile(`\Aapplication/json(;|\z)`) 19 hiddenHeaders = map[string]bool{ 20 "Authorization": true, 21 } 22 23 defaultErrors = map[int]string{ 24 400: "Client error: %s", 25 401: "Authorization error: %s\nCheck that you have proper access to the repository", 26 403: "Authorization error: %s\nCheck that you have proper access to the repository", 27 404: "Repository or object not found: %s\nCheck that it exists and that you have proper access to it", 28 429: "Rate limit exceeded: %s", 29 500: "Server error: %s", 30 507: "Insufficient server storage: %s", 31 509: "Bandwidth limit exceeded: %s", 32 } 33 ) 34 35 // DecodeResponse attempts to decode the contents of the response as a JSON object 36 func DecodeResponse(res *http.Response, obj interface{}) error { 37 ctype := res.Header.Get("Content-Type") 38 if !(lfsMediaTypeRE.MatchString(ctype) || jsonMediaTypeRE.MatchString(ctype)) { 39 return nil 40 } 41 42 err := json.NewDecoder(res.Body).Decode(obj) 43 io.Copy(ioutil.Discard, res.Body) 44 res.Body.Close() 45 46 if err != nil { 47 return errors.Wrapf(err, "Unable to parse HTTP response for %s", TraceHttpReq(res.Request)) 48 } 49 50 return nil 51 } 52 53 // GetDefaultError returns the default text for standard error codes (blank if none) 54 func GetDefaultError(code int) string { 55 if s, ok := defaultErrors[code]; ok { 56 return s 57 } 58 return "" 59 } 60 61 // Check the response from a HTTP request for problems 62 func handleResponse(cfg *config.Configuration, res *http.Response, creds auth.Creds) error { 63 auth.SaveCredentials(cfg, creds, res) 64 65 if res.StatusCode < 400 { 66 return nil 67 } 68 69 defer func() { 70 io.Copy(ioutil.Discard, res.Body) 71 res.Body.Close() 72 }() 73 74 cliErr := &ClientError{} 75 err := DecodeResponse(res, cliErr) 76 if err == nil { 77 if len(cliErr.Message) == 0 { 78 err = defaultError(res) 79 } else { 80 err = errors.Wrap(cliErr, "http") 81 } 82 } 83 84 if res.StatusCode == 401 { 85 if err == nil { 86 err = errors.New("api: received status 401") 87 } 88 return errors.NewAuthError(err) 89 } 90 91 if res.StatusCode > 499 && res.StatusCode != 501 && res.StatusCode != 507 && res.StatusCode != 509 { 92 if err == nil { 93 err = errors.Errorf("api: received status %d", res.StatusCode) 94 } 95 return errors.NewFatalError(err) 96 } 97 98 return err 99 } 100 101 func defaultError(res *http.Response) error { 102 var msgFmt string 103 104 if f, ok := defaultErrors[res.StatusCode]; ok { 105 msgFmt = f 106 } else if res.StatusCode < 500 { 107 msgFmt = defaultErrors[400] + fmt.Sprintf(" from HTTP %d", res.StatusCode) 108 } else { 109 msgFmt = defaultErrors[500] + fmt.Sprintf(" from HTTP %d", res.StatusCode) 110 } 111 112 return errors.Errorf(msgFmt, res.Request.URL) 113 } 114 115 func SetErrorResponseContext(cfg *config.Configuration, err error, res *http.Response) { 116 errors.SetContext(err, "Status", res.Status) 117 setErrorHeaderContext(err, "Request", res.Header) 118 setErrorRequestContext(cfg, err, res.Request) 119 } 120 121 func setErrorRequestContext(cfg *config.Configuration, err error, req *http.Request) { 122 errors.SetContext(err, "Endpoint", cfg.Endpoint(auth.GetOperationForRequest(req)).Url) 123 errors.SetContext(err, "URL", TraceHttpReq(req)) 124 setErrorHeaderContext(err, "Response", req.Header) 125 } 126 127 func setErrorHeaderContext(err error, prefix string, head http.Header) { 128 for key, _ := range head { 129 contextKey := fmt.Sprintf("%s:%s", prefix, key) 130 if _, skip := hiddenHeaders[key]; skip { 131 errors.SetContext(err, contextKey, "--") 132 } else { 133 errors.SetContext(err, contextKey, head.Get(key)) 134 } 135 } 136 }