@ -236,7 +236,7 @@ type Response struct {
func newResponse ( r * http . Response ) * Response {
func newResponse ( r * http . Response ) * Response {
response := & Response { Response : r }
response := & Response { Response : r }
response . populatePageValues ( )
response . populatePageValues ( )
response . populateRate ( )
response . Rate = parseRate ( r )
return response
return response
}
}
@ -284,19 +284,21 @@ func (r *Response) populatePageValues() {
}
}
}
}
// populateRate parses the rate related headers and populates the response Rate.
func ( r * Response ) populateRate ( ) {
// parseRate parses the rate related headers.
func parseRate ( r * http . Response ) Rate {
var rate Rate
if limit := r . Header . Get ( headerRateLimit ) ; limit != "" {
if limit := r . Header . Get ( headerRateLimit ) ; limit != "" {
r . R ate. Limit , _ = strconv . Atoi ( limit )
rate . Limit , _ = strconv . Atoi ( limit )
}
}
if remaining := r . Header . Get ( headerRateRemaining ) ; remaining != "" {
if remaining := r . Header . Get ( headerRateRemaining ) ; remaining != "" {
r . R ate. Remaining , _ = strconv . Atoi ( remaining )
rate . Remaining , _ = strconv . Atoi ( remaining )
}
}
if reset := r . Header . Get ( headerRateReset ) ; reset != "" {
if reset := r . Header . Get ( headerRateReset ) ; reset != "" {
if v , _ := strconv . ParseInt ( reset , 10 , 64 ) ; v != 0 {
if v , _ := strconv . ParseInt ( reset , 10 , 64 ) ; v != 0 {
r . R ate. Reset = Timestamp { time . Unix ( v , 0 ) }
rate . Reset = Timestamp { time . Unix ( v , 0 ) }
}
}
}
}
return rate
}
}
// Rate specifies the current rate limit for the client as determined by the
// Rate specifies the current rate limit for the client as determined by the
@ -373,6 +375,20 @@ type TwoFactorAuthError ErrorResponse
func ( r * TwoFactorAuthError ) Error ( ) string { return ( * ErrorResponse ) ( r ) . Error ( ) }
func ( r * TwoFactorAuthError ) Error ( ) string { return ( * ErrorResponse ) ( r ) . Error ( ) }
// RateLimitError occurs when GitHub returns 403 Forbidden response with a rate limit
// remaining value of 0, and error message starts with "API rate limit exceeded for ".
type RateLimitError struct {
Rate Rate // Rate specifies last known rate limit for the client
Response * http . Response // HTTP response that caused this error
Message string ` json:"message" ` // error message
}
func ( r * RateLimitError ) Error ( ) string {
return fmt . Sprintf ( "%v %v: %d %v; rate reset in %v" ,
r . Response . Request . Method , sanitizeURL ( r . Response . Request . URL ) ,
r . Response . StatusCode , r . Message , r . Rate . Reset . Time . Sub ( time . Now ( ) ) )
}
// sanitizeURL redacts the client_secret parameter from the URL which may be
// sanitizeURL redacts the client_secret parameter from the URL which may be
// exposed to the user, specifically in the ErrorResponse error message.
// exposed to the user, specifically in the ErrorResponse error message.
func sanitizeURL ( uri * url . URL ) * url . URL {
func sanitizeURL ( uri * url . URL ) * url . URL {
@ -427,10 +443,18 @@ func CheckResponse(r *http.Response) error {
if err == nil && data != nil {
if err == nil && data != nil {
json . Unmarshal ( data , errorResponse )
json . Unmarshal ( data , errorResponse )
}
}
if r . StatusCode == http . StatusUnauthorized && strings . HasPrefix ( r . Header . Get ( headerOTP ) , "required" ) {
switch {
case r . StatusCode == http . StatusUnauthorized && strings . HasPrefix ( r . Header . Get ( headerOTP ) , "required" ) :
return ( * TwoFactorAuthError ) ( errorResponse )
return ( * TwoFactorAuthError ) ( errorResponse )
case r . StatusCode == http . StatusForbidden && r . Header . Get ( headerRateRemaining ) == "0" && strings . HasPrefix ( errorResponse . Message , "API rate limit exceeded for " ) :
return & RateLimitError {
Rate : parseRate ( r ) ,
Response : errorResponse . Response ,
Message : errorResponse . Message ,
}
default :
return errorResponse
}
}
return errorResponse
}
}
// parseBoolResponse determines the boolean result from a GitHub API response.
// parseBoolResponse determines the boolean result from a GitHub API response.