mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 11:02:25 -05:00 
			
		
		
		
	[chore] transport improvements (#1524)
* improve error readability, mark "bad hosts" as fastFail
Signed-off-by: kim <grufwub@gmail.com>
* pull in latest go-byteutil version with byteutil.Reader{}
Signed-off-by: kim <grufwub@gmail.com>
* use rewindable body reader for post requests
Signed-off-by: kim <grufwub@gmail.com>
---------
Signed-off-by: kim <grufwub@gmail.com>
	
	
This commit is contained in:
		
					parent
					
						
							
								3649b231c4
							
						
					
				
			
			
				commit
				
					
						a684fc4628
					
				
			
		
					 11 changed files with 84 additions and 63 deletions
				
			
		
							
								
								
									
										2
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
										
									
									
									
								
							|  | @ -4,7 +4,7 @@ go 1.19 | ||||||
| 
 | 
 | ||||||
| require ( | require ( | ||||||
| 	codeberg.org/gruf/go-bytesize v1.0.2 | 	codeberg.org/gruf/go-bytesize v1.0.2 | ||||||
| 	codeberg.org/gruf/go-byteutil v1.0.2 | 	codeberg.org/gruf/go-byteutil v1.1.2 | ||||||
| 	codeberg.org/gruf/go-cache/v3 v3.2.2 | 	codeberg.org/gruf/go-cache/v3 v3.2.2 | ||||||
| 	codeberg.org/gruf/go-debug v1.3.0 | 	codeberg.org/gruf/go-debug v1.3.0 | ||||||
| 	codeberg.org/gruf/go-errors/v2 v2.1.1 | 	codeberg.org/gruf/go-errors/v2 v2.1.1 | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								go.sum
									
										
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
										
									
									
									
								
							|  | @ -47,8 +47,8 @@ codeberg.org/gruf/go-bytes v1.0.2/go.mod h1:1v/ibfaosfXSZtRdW2rWaVrDXMc9E3bsi/M9 | ||||||
| codeberg.org/gruf/go-bytesize v1.0.2 h1:Mo+ITi+0uZ4YNSZf2ed6Qw8acOI39W4mmgE1a8lslXw= | codeberg.org/gruf/go-bytesize v1.0.2 h1:Mo+ITi+0uZ4YNSZf2ed6Qw8acOI39W4mmgE1a8lslXw= | ||||||
| codeberg.org/gruf/go-bytesize v1.0.2/go.mod h1:n/GU8HzL9f3UNp/mUKyr1qVmTlj7+xacpp0OHfkvLPs= | codeberg.org/gruf/go-bytesize v1.0.2/go.mod h1:n/GU8HzL9f3UNp/mUKyr1qVmTlj7+xacpp0OHfkvLPs= | ||||||
| codeberg.org/gruf/go-byteutil v1.0.0/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= | codeberg.org/gruf/go-byteutil v1.0.0/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= | ||||||
| codeberg.org/gruf/go-byteutil v1.0.2 h1:OesVyK5VKWeWdeDR00zRJ+Oy8hjXx1pBhn7WVvcZWVE= | codeberg.org/gruf/go-byteutil v1.1.2 h1:TQLZtTxTNca9xEfDIndmo7nBYxeS94nrv/9DS3Nk5Tw= | ||||||
| codeberg.org/gruf/go-byteutil v1.0.2/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= | codeberg.org/gruf/go-byteutil v1.1.2/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= | ||||||
| codeberg.org/gruf/go-cache/v3 v3.2.2 h1:hq6/RITgpcArjzbYSyo3uFxfIw7wW3KqAQjEaN7dj58= | codeberg.org/gruf/go-cache/v3 v3.2.2 h1:hq6/RITgpcArjzbYSyo3uFxfIw7wW3KqAQjEaN7dj58= | ||||||
| codeberg.org/gruf/go-cache/v3 v3.2.2/go.mod h1:+Eje6nCvN8QF71VyYjMWMnkdv6t1kHnCO/SvyC4K12Q= | codeberg.org/gruf/go-cache/v3 v3.2.2/go.mod h1:+Eje6nCvN8QF71VyYjMWMnkdv6t1kHnCO/SvyC4K12Q= | ||||||
| codeberg.org/gruf/go-debug v1.3.0 h1:PIRxQiWUFKtGOGZFdZ3Y0pqyfI0Xr87j224IYe2snZs= | codeberg.org/gruf/go-debug v1.3.0 h1:PIRxQiWUFKtGOGZFdZ3Y0pqyfI0Xr87j224IYe2snZs= | ||||||
|  |  | ||||||
|  | @ -19,7 +19,6 @@ | ||||||
| package transport | package transport | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" |  | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | @ -27,6 +26,7 @@ import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 
 | 
 | ||||||
|  | 	"codeberg.org/gruf/go-byteutil" | ||||||
| 	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" | 	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
| ) | ) | ||||||
|  | @ -49,7 +49,7 @@ func (t *transport) BatchDeliver(ctx context.Context, b []byte, recipients []*ur | ||||||
| 	wg.Wait() | 	wg.Wait() | ||||||
| 
 | 
 | ||||||
| 	// receive any buffered errors | 	// receive any buffered errors | ||||||
| 	errs := make([]string, 0, len(recipients)) | 	errs := make([]string, 0, len(errCh)) | ||||||
| outer: | outer: | ||||||
| 	for { | 	for { | ||||||
| 		select { | 		select { | ||||||
|  | @ -75,7 +75,11 @@ func (t *transport) Deliver(ctx context.Context, b []byte, to *url.URL) error { | ||||||
| 
 | 
 | ||||||
| 	urlStr := to.String() | 	urlStr := to.String() | ||||||
| 
 | 
 | ||||||
| 	req, err := http.NewRequestWithContext(ctx, "POST", urlStr, bytes.NewReader(b)) | 	// Use rewindable bytes reader for body. | ||||||
|  | 	var body byteutil.ReadNopCloser | ||||||
|  | 	body.Reset(b) | ||||||
|  | 
 | ||||||
|  | 	req, err := http.NewRequestWithContext(ctx, "POST", urlStr, &body) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | @ -92,7 +96,7 @@ func (t *transport) Deliver(ctx context.Context, b []byte, to *url.URL) error { | ||||||
| 
 | 
 | ||||||
| 	if code := resp.StatusCode; code != http.StatusOK && | 	if code := resp.StatusCode; code != http.StatusOK && | ||||||
| 		code != http.StatusCreated && code != http.StatusAccepted { | 		code != http.StatusCreated && code != http.StatusAccepted { | ||||||
| 		return fmt.Errorf("POST request to %s failed (%d): %s", urlStr, resp.StatusCode, resp.Status) | 		return fmt.Errorf("POST request to %s failed: %s", urlStr, resp.Status) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
|  |  | ||||||
|  | @ -78,6 +78,6 @@ func (t *transport) Dereference(ctx context.Context, iri *url.URL) ([]byte, erro | ||||||
| 	case http.StatusGone: | 	case http.StatusGone: | ||||||
| 		return nil, ErrGone | 		return nil, ErrGone | ||||||
| 	default: | 	default: | ||||||
| 		return nil, fmt.Errorf("GET request to %s failed (%d): %s", iriStr, rsp.StatusCode, rsp.Status) | 		return nil, fmt.Errorf("GET request to %s failed: %s", iriStr, rsp.Status) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -102,7 +102,7 @@ func dereferenceByAPIV1Instance(ctx context.Context, t *transport, iri *url.URL) | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
| 
 | 
 | ||||||
| 	if resp.StatusCode != http.StatusOK { | 	if resp.StatusCode != http.StatusOK { | ||||||
| 		return nil, fmt.Errorf("GET request to %s failed (%d): %s", iriStr, resp.StatusCode, resp.Status) | 		return nil, fmt.Errorf("GET request to %s failed: %s", iriStr, resp.Status) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	b, err := io.ReadAll(resp.Body) | 	b, err := io.ReadAll(resp.Body) | ||||||
|  | @ -252,7 +252,7 @@ func callNodeInfoWellKnown(ctx context.Context, t *transport, iri *url.URL) (*ur | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
| 
 | 
 | ||||||
| 	if resp.StatusCode != http.StatusOK { | 	if resp.StatusCode != http.StatusOK { | ||||||
| 		return nil, fmt.Errorf("callNodeInfoWellKnown: GET request to %s failed (%d): %s", iriStr, resp.StatusCode, resp.Status) | 		return nil, fmt.Errorf("callNodeInfoWellKnown: GET request to %s failed: %s", iriStr, resp.Status) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	b, err := io.ReadAll(resp.Body) | 	b, err := io.ReadAll(resp.Body) | ||||||
|  | @ -303,7 +303,7 @@ func callNodeInfo(ctx context.Context, t *transport, iri *url.URL) (*apimodel.No | ||||||
| 	defer resp.Body.Close() | 	defer resp.Body.Close() | ||||||
| 
 | 
 | ||||||
| 	if resp.StatusCode != http.StatusOK { | 	if resp.StatusCode != http.StatusOK { | ||||||
| 		return nil, fmt.Errorf("callNodeInfo: GET request to %s failed (%d): %s", iriStr, resp.StatusCode, resp.Status) | 		return nil, fmt.Errorf("callNodeInfo: GET request to %s failed: %s", iriStr, resp.Status) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	b, err := io.ReadAll(resp.Body) | 	b, err := io.ReadAll(resp.Body) | ||||||
|  |  | ||||||
|  | @ -46,7 +46,7 @@ func (t *transport) DereferenceMedia(ctx context.Context, iri *url.URL) (io.Read | ||||||
| 
 | 
 | ||||||
| 	// Check for an expected status code | 	// Check for an expected status code | ||||||
| 	if rsp.StatusCode != http.StatusOK { | 	if rsp.StatusCode != http.StatusOK { | ||||||
| 		return nil, 0, fmt.Errorf("GET request to %s failed (%d): %s", iriStr, rsp.StatusCode, rsp.Status) | 		return nil, 0, fmt.Errorf("GET request to %s failed: %s", iriStr, rsp.Status) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return rsp.Body, rsp.ContentLength, nil | 	return rsp.Body, rsp.ContentLength, nil | ||||||
|  |  | ||||||
|  | @ -52,7 +52,7 @@ func (t *transport) Finger(ctx context.Context, targetUsername string, targetDom | ||||||
| 
 | 
 | ||||||
| 	// Check for an expected status code | 	// Check for an expected status code | ||||||
| 	if rsp.StatusCode != http.StatusOK { | 	if rsp.StatusCode != http.StatusOK { | ||||||
| 		return nil, fmt.Errorf("GET request to %s failed (%d): %s", urlStr, rsp.StatusCode, rsp.Status) | 		return nil, fmt.Errorf("GET request to %s failed: %s", urlStr, rsp.Status) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return io.ReadAll(rsp.Body) | 	return io.ReadAll(rsp.Body) | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ import ( | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | 	"codeberg.org/gruf/go-byteutil" | ||||||
| 	errorsv2 "codeberg.org/gruf/go-errors/v2" | 	errorsv2 "codeberg.org/gruf/go-errors/v2" | ||||||
| 	"codeberg.org/gruf/go-kv" | 	"codeberg.org/gruf/go-kv" | ||||||
| 	"github.com/go-fed/httpsig" | 	"github.com/go-fed/httpsig" | ||||||
|  | @ -84,7 +85,7 @@ type transport struct { | ||||||
| 	signerMu   sync.Mutex | 	signerMu   sync.Mutex | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GET will perform given http request using transport client, retrying on certain preset errors, or if status code is among retryOn. | // GET will perform given http request using transport client, retrying on certain preset errors. | ||||||
| func (t *transport) GET(r *http.Request) (*http.Response, error) { | func (t *transport) GET(r *http.Request) (*http.Response, error) { | ||||||
| 	if r.Method != http.MethodGet { | 	if r.Method != http.MethodGet { | ||||||
| 		return nil, errors.New("must be GET request") | 		return nil, errors.New("must be GET request") | ||||||
|  | @ -94,7 +95,7 @@ func (t *transport) GET(r *http.Request) (*http.Response, error) { | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // POST will perform given http request using transport client, retrying on certain preset errors, or if status code is among retryOn. | // POST will perform given http request using transport client, retrying on certain preset errors. | ||||||
| func (t *transport) POST(r *http.Request, body []byte) (*http.Response, error) { | func (t *transport) POST(r *http.Request, body []byte) (*http.Response, error) { | ||||||
| 	if r.Method != http.MethodPost { | 	if r.Method != http.MethodPost { | ||||||
| 		return nil, errors.New("must be POST request") | 		return nil, errors.New("must be POST request") | ||||||
|  | @ -116,18 +117,17 @@ func (t *transport) do(r *http.Request, signer func(*http.Request) error) (*http | ||||||
| 	// Get request hostname | 	// Get request hostname | ||||||
| 	host := r.URL.Hostname() | 	host := r.URL.Hostname() | ||||||
| 
 | 
 | ||||||
| 	// Check if recently reached max retries for this host |  | ||||||
| 	// so we don't need to bother reattempting it. The only |  | ||||||
| 	// errors that are retried upon are server failure and |  | ||||||
| 	// domain resolution type errors, so this cached result |  | ||||||
| 	// indicates this server is likely having issues. |  | ||||||
| 	if t.controller.badHosts.Has(host) { |  | ||||||
| 		return nil, errors.New("too many failed attempts") |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Check whether request should fast fail, we check this | 	// Check whether request should fast fail, we check this | ||||||
| 	// before loop as each context.Value() requires mutex lock. | 	// before loop as each context.Value() requires mutex lock. | ||||||
| 	fastFail := IsFastfail(r.Context()) | 	fastFail := IsFastfail(r.Context()) | ||||||
|  | 	if !fastFail { | ||||||
|  | 		// Check if recently reached max retries for this host | ||||||
|  | 		// so we don't bother with a retry-backoff loop. The only | ||||||
|  | 		// errors that are retried upon are server failure and | ||||||
|  | 		// domain resolution type errors, so this cached result | ||||||
|  | 		// indicates this server is likely having issues. | ||||||
|  | 		fastFail = t.controller.badHosts.Has(host) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// Start a log entry for this request | 	// Start a log entry for this request | ||||||
| 	l := log.WithContext(r.Context()). | 	l := log.WithContext(r.Context()). | ||||||
|  | @ -148,6 +148,12 @@ func (t *transport) do(r *http.Request, signer func(*http.Request) error) (*http | ||||||
| 		r.Header.Del("Signature") | 		r.Header.Del("Signature") | ||||||
| 		r.Header.Del("Digest") | 		r.Header.Del("Digest") | ||||||
| 
 | 
 | ||||||
|  | 		// Rewind body reader and content-length if set. | ||||||
|  | 		if rc, ok := r.Body.(*byteutil.ReadNopCloser); ok { | ||||||
|  | 			r.ContentLength = int64(rc.Len()) | ||||||
|  | 			rc.Rewind() | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		// Perform request signing | 		// Perform request signing | ||||||
| 		if err := signer(r); err != nil { | 		if err := signer(r); err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
|  | @ -226,7 +232,7 @@ func (t *transport) do(r *http.Request, signer func(*http.Request) error) (*http | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Add "bad" entry for this host | 	// Add "bad" entry for this host. | ||||||
| 	t.controller.badHosts.Set(host, struct{}{}) | 	t.controller.badHosts.Set(host, struct{}{}) | ||||||
| 
 | 
 | ||||||
| 	return nil, errors.New("transport reached max retries") | 	return nil, errors.New("transport reached max retries") | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								vendor/codeberg.org/gruf/go-byteutil/bytes.go
									
										
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								vendor/codeberg.org/gruf/go-byteutil/bytes.go
									
										
									
										generated
									
									
										vendored
									
									
								
							|  | @ -18,17 +18,20 @@ func Copy(b []byte) []byte { | ||||||
| // B2S returns a string representation of []byte without allocation. | // B2S returns a string representation of []byte without allocation. | ||||||
| // | // | ||||||
| // According to the Go spec strings are immutable and byte slices are not. The way this gets implemented is strings under the hood are: | // According to the Go spec strings are immutable and byte slices are not. The way this gets implemented is strings under the hood are: | ||||||
|  | // | ||||||
| //	type StringHeader struct { | //	type StringHeader struct { | ||||||
| //		Data uintptr | //		Data uintptr | ||||||
| //		Len  int | //		Len  int | ||||||
| //	} | //	} | ||||||
| // | // | ||||||
| // while slices are: | // while slices are: | ||||||
|  | // | ||||||
| //	type SliceHeader struct { | //	type SliceHeader struct { | ||||||
| //		Data uintptr | //		Data uintptr | ||||||
| //		Len  int | //		Len  int | ||||||
| //		Cap  int | //		Cap  int | ||||||
| //	} | //	} | ||||||
|  | // | ||||||
| // because being mutable, you can change the data, length etc, but the string has to promise to be read-only to all who get copies of it. | // because being mutable, you can change the data, length etc, but the string has to promise to be read-only to all who get copies of it. | ||||||
| // | // | ||||||
| // So in practice when you do a conversion of `string(byteSlice)` it actually performs an allocation because it has to copy the contents of the byte slice into a safe read-only state. | // So in practice when you do a conversion of `string(byteSlice)` it actually performs an allocation because it has to copy the contents of the byte slice into a safe read-only state. | ||||||
|  | @ -54,31 +57,3 @@ func S2B(s string) []byte { | ||||||
| 
 | 
 | ||||||
| 	return b | 	return b | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // ToUpper offers a faster ToUpper implementation using a lookup table. |  | ||||||
| func ToUpper(b []byte) { |  | ||||||
| 	const toUpperTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + |  | ||||||
| 		" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~" + |  | ||||||
| 		"\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96" + |  | ||||||
| 		"\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + |  | ||||||
| 		"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8" + |  | ||||||
| 		"\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1" + |  | ||||||
| 		"\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" |  | ||||||
| 	for i := 0; i < len(b); i++ { |  | ||||||
| 		b[i] = toUpperTable[b[i]] |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // ToLower offers a faster ToLower implementation using a lookup table. |  | ||||||
| func ToLower(b []byte) { |  | ||||||
| 	const toLowerTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + |  | ||||||
| 		" !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + |  | ||||||
| 		"\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96" + |  | ||||||
| 		"\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" + |  | ||||||
| 		"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8" + |  | ||||||
| 		"\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1" + |  | ||||||
| 		"\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" |  | ||||||
| 	for i := 0; i < len(b); i++ { |  | ||||||
| 		b[i] = toLowerTable[b[i]] |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
							
								
								
									
										36
									
								
								vendor/codeberg.org/gruf/go-byteutil/reader.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								vendor/codeberg.org/gruf/go-byteutil/reader.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | package byteutil | ||||||
|  | 
 | ||||||
|  | import "bytes" | ||||||
|  | 
 | ||||||
|  | // Reader wraps a bytes.Reader{} to provide Rewind() capabilities. | ||||||
|  | type Reader struct { | ||||||
|  | 	B []byte | ||||||
|  | 	bytes.Reader | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewReader returns a new Reader{} instance reset to b. | ||||||
|  | func NewReader(b []byte) *Reader { | ||||||
|  | 	r := &Reader{} | ||||||
|  | 	r.Reset(b) | ||||||
|  | 	return r | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Reset resets the Reader{} to be reading from b and sets Reader{}.B. | ||||||
|  | func (r *Reader) Reset(b []byte) { | ||||||
|  | 	r.B = b | ||||||
|  | 	r.Rewind() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Rewind resets the Reader{} to be reading from the start of Reader{}.B. | ||||||
|  | func (r *Reader) Rewind() { | ||||||
|  | 	r.Reader.Reset(r.B) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ReadNopCloser wraps a Reader{} to provide nop close method. | ||||||
|  | type ReadNopCloser struct { | ||||||
|  | 	Reader | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (*ReadNopCloser) Close() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										2
									
								
								vendor/modules.txt
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/modules.txt
									
										
									
									
										vendored
									
									
								
							|  | @ -10,7 +10,7 @@ codeberg.org/gruf/go-bytes | ||||||
| # codeberg.org/gruf/go-bytesize v1.0.2 | # codeberg.org/gruf/go-bytesize v1.0.2 | ||||||
| ## explicit; go 1.17 | ## explicit; go 1.17 | ||||||
| codeberg.org/gruf/go-bytesize | codeberg.org/gruf/go-bytesize | ||||||
| # codeberg.org/gruf/go-byteutil v1.0.2 | # codeberg.org/gruf/go-byteutil v1.1.2 | ||||||
| ## explicit; go 1.16 | ## explicit; go 1.16 | ||||||
| codeberg.org/gruf/go-byteutil | codeberg.org/gruf/go-byteutil | ||||||
| # codeberg.org/gruf/go-cache/v3 v3.2.2 | # codeberg.org/gruf/go-cache/v3 v3.2.2 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue