| 
									
										
										
										
											2024-04-11 10:45:35 +01:00
										 |  |  | // GoToSocial | 
					
						
							|  |  |  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | // GNU Affero General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package httpclient | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:27 +00:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2024-04-11 10:45:35 +01:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-26 15:34:10 +02:00
										 |  |  | 	"code.superseriousbusiness.org/gotosocial/internal/log" | 
					
						
							| 
									
										
										
										
											2024-04-11 10:45:35 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	// starting backoff duration. | 
					
						
							|  |  |  | 	baseBackoff = 2 * time.Second | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Request wraps an HTTP request | 
					
						
							|  |  |  | // to add our own retry / backoff. | 
					
						
							|  |  |  | type Request struct { | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-11 10:45:35 +01:00
										 |  |  | 	// Current backoff dur. | 
					
						
							|  |  |  | 	backoff time.Duration | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Delivery attempts. | 
					
						
							|  |  |  | 	attempts uint | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// log fields. | 
					
						
							|  |  |  | 	log.Entry | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// underlying request. | 
					
						
							|  |  |  | 	*http.Request | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // WrapRequest wraps an existing http.Request within | 
					
						
							|  |  |  | // our own httpclient.Request with retry / backoff tracking. | 
					
						
							| 
									
										
										
										
											2024-07-30 11:58:31 +00:00
										 |  |  | func WrapRequest(r *http.Request) *Request { | 
					
						
							|  |  |  | 	rr := new(Request) | 
					
						
							| 
									
										
										
										
											2024-04-11 10:45:35 +01:00
										 |  |  | 	rr.Request = r | 
					
						
							| 
									
										
										
										
											2024-04-30 15:15:50 +01:00
										 |  |  | 	entry := log.WithContext(r.Context()) | 
					
						
							|  |  |  | 	entry = entry.WithField("method", r.Method) | 
					
						
							|  |  |  | 	entry = entry.WithField("url", r.URL.String()) | 
					
						
							|  |  |  | 	if r.Body != nil { | 
					
						
							|  |  |  | 		// Only add content-type header if a request body exists. | 
					
						
							|  |  |  | 		entry = entry.WithField("contentType", r.Header.Get("Content-Type")) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:27 +00:00
										 |  |  | 	entry = entry.WithField("attempt", uintPtr{&rr.attempts}) | 
					
						
							| 
									
										
										
										
											2024-04-30 15:15:50 +01:00
										 |  |  | 	rr.Entry = entry | 
					
						
							| 
									
										
										
										
											2024-04-11 10:45:35 +01:00
										 |  |  | 	return rr | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetBackOff returns the currently set backoff duration, | 
					
						
							|  |  |  | // (using a default according to no. attempts if needed). | 
					
						
							|  |  |  | func (r *Request) BackOff() time.Duration { | 
					
						
							|  |  |  | 	if r.backoff <= 0 { | 
					
						
							|  |  |  | 		// No backoff dur found, set our predefined | 
					
						
							|  |  |  | 		// backoff according to a multiplier of 2^n. | 
					
						
							|  |  |  | 		r.backoff = baseBackoff * 1 << (r.attempts + 1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return r.backoff | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2024-09-28 20:47:27 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | type uintPtr struct{ u *uint } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (f uintPtr) String() string { | 
					
						
							|  |  |  | 	if f.u == nil { | 
					
						
							|  |  |  | 		return "<nil>" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return strconv.FormatUint(uint64(*f.u), 10) | 
					
						
							|  |  |  | } |