(new): expand runtime APIs
(fix): harden concurrent lifecycle (tests): add regression coverage (doc): update v1.2 guidance
This commit is contained in:
+1
-1
@@ -272,7 +272,7 @@ func (r TelegramRequest[R, P]) doRequest(ctx context.Context, api *API) (R, erro
|
||||
api.logger.Debugln("REQ", url, redactRequestLog(reqData))
|
||||
resp, err := api.client.Do(req)
|
||||
if err != nil {
|
||||
return zero, fmt.Errorf("HTTP request failed: %w", err)
|
||||
return zero, fmt.Errorf("HTTP request failed: %w", redactHTTPError(err, api.token))
|
||||
}
|
||||
|
||||
respData, err := readBody(resp.Body)
|
||||
|
||||
@@ -2,7 +2,9 @@ package tgapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -55,3 +57,30 @@ func redactLogValue(value any) {
|
||||
func responseLogSummary(method string, size int) string {
|
||||
return fmt.Sprintf("method=%s bytes=%d body=omitted", method, size)
|
||||
}
|
||||
|
||||
type redactedError struct {
|
||||
err error
|
||||
secret string
|
||||
}
|
||||
|
||||
func (e *redactedError) Error() string {
|
||||
return strings.ReplaceAll(e.err.Error(), e.secret, redactedLogValue)
|
||||
}
|
||||
|
||||
func (e *redactedError) Unwrap() error { return e.err }
|
||||
|
||||
func redactHTTPError(err error, token string) error {
|
||||
if err == nil || token == "" || !strings.Contains(err.Error(), token) {
|
||||
return err
|
||||
}
|
||||
|
||||
var urlErr *url.Error
|
||||
if !errors.As(err, &urlErr) {
|
||||
return &redactedError{err: err, secret: token}
|
||||
}
|
||||
|
||||
redactedURL := *urlErr
|
||||
redactedURL.URL = strings.ReplaceAll(redactedURL.URL, token, redactedLogValue)
|
||||
redactedURL.Err = &redactedError{err: urlErr.Err, secret: token}
|
||||
return &redactedURL
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package tgapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -37,3 +40,29 @@ func TestResponseLogSummaryNeverContainsBody(t *testing.T) {
|
||||
t.Fatalf("response summary does not explain omission: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRedactHTTPErrorRemovesTokenAndPreservesCause(t *testing.T) {
|
||||
const token = "123456:secret-token"
|
||||
cause := errors.New("transport failed")
|
||||
original := &url.Error{
|
||||
Op: "Post",
|
||||
URL: "https://api.telegram.org/bot" + token + "/sendMessage",
|
||||
Err: fmt.Errorf("request for %s failed: %w", token, cause),
|
||||
}
|
||||
|
||||
got := redactHTTPError(original, token)
|
||||
if strings.Contains(got.Error(), token) {
|
||||
t.Fatalf("redacted HTTP error contains bot token: %v", got)
|
||||
}
|
||||
if !errors.Is(got, cause) {
|
||||
t.Fatalf("redacted HTTP error lost its cause: %v", got)
|
||||
}
|
||||
|
||||
var gotURLError *url.Error
|
||||
if !errors.As(got, &gotURLError) {
|
||||
t.Fatalf("redacted HTTP error lost url.Error type: %T", got)
|
||||
}
|
||||
if strings.Contains(gotURLError.Error(), token) {
|
||||
t.Fatalf("redacted url.Error contains bot token: %v", gotURLError)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -336,7 +336,7 @@ func (api *API) openFileByLink(ctx context.Context, link string) (io.ReadCloser,
|
||||
|
||||
res, err := api.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, redactHTTPError(err, api.token)
|
||||
}
|
||||
if res.StatusCode < http.StatusOK || res.StatusCode >= http.StatusMultipleChoices {
|
||||
defer func() {
|
||||
|
||||
@@ -156,7 +156,7 @@ func (r UploaderRequest[R, P]) doRequest(ctx context.Context, up *Uploader) (R,
|
||||
resp, err := up.api.client.Do(req)
|
||||
_ = requestBody.Close()
|
||||
if err != nil {
|
||||
return zero, fmt.Errorf("HTTP upload request failed: %w", err)
|
||||
return zero, fmt.Errorf("HTTP upload request failed: %w", redactHTTPError(err, up.api.token))
|
||||
}
|
||||
|
||||
body, err := readBody(resp.Body)
|
||||
|
||||
Reference in New Issue
Block a user