mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2026-03-11 21:55:01 +01:00
Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: qwerty287 <80460567+qwerty287@users.noreply.github.com>
163 lines
5.4 KiB
Go
163 lines
5.4 KiB
Go
// Copyright 2025 Woodpecker Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package httputil
|
|
|
|
import (
|
|
"context"
|
|
"crypto/x509"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
// EnhanceHTTPError adds detailed context to HTTP errors to help with debugging.
|
|
func EnhanceHTTPError(err error, method, endpoint string) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
// parse url to get host information
|
|
parsedURL, parseErr := url.Parse(endpoint)
|
|
var host string
|
|
if parseErr == nil {
|
|
host = parsedURL.Host
|
|
} else {
|
|
host = endpoint
|
|
}
|
|
|
|
// base error message
|
|
baseMsg := fmt.Sprintf("%s %q", method, endpoint)
|
|
|
|
// check for context errors
|
|
// timeout
|
|
if errors.Is(err, context.DeadlineExceeded) {
|
|
if strings.Contains(err.Error(), "Client.Timeout") {
|
|
return fmt.Errorf("connection timeout: %s: %w (the remote server at %s did not respond within the configured timeout)", baseMsg, err, host)
|
|
}
|
|
return fmt.Errorf("request timeout: %s: %w (operation took too long time)", baseMsg, err)
|
|
}
|
|
|
|
// cancellation
|
|
if errors.Is(err, context.Canceled) {
|
|
return fmt.Errorf("request canceled: %s: %w (the operation was canceled before completion)", baseMsg, err)
|
|
}
|
|
|
|
// check for net package errors
|
|
// dns error handling
|
|
var dnsErr *net.DNSError
|
|
if errors.As(err, &dnsErr) {
|
|
if dnsErr.IsNotFound {
|
|
return fmt.Errorf("DNS resolution failed: %s: %w (hostname %s does not exist or cannot be resolved)", baseMsg, err, host)
|
|
}
|
|
if dnsErr.IsTimeout {
|
|
return fmt.Errorf("DNS timeout: %s: %w (DNS server did not respond in time)", baseMsg, err)
|
|
}
|
|
return fmt.Errorf("DNS error: %s: %w", baseMsg, err)
|
|
}
|
|
|
|
// op error handling
|
|
var opErr *net.OpError
|
|
if errors.As(err, &opErr) {
|
|
// connection refused
|
|
if errors.Is(opErr.Err, syscall.ECONNREFUSED) {
|
|
return fmt.Errorf("connection refused: %s: %w (server at %s is not accepting connections - is it running?)", baseMsg, err, host)
|
|
}
|
|
|
|
// connection reset
|
|
if errors.Is(opErr.Err, syscall.ECONNRESET) {
|
|
return fmt.Errorf("connection reset: %s: %w (server at %s closed the connection unexpectedly)", baseMsg, err, host)
|
|
}
|
|
|
|
// network unreachable
|
|
if errors.Is(opErr.Err, syscall.ENETUNREACH) {
|
|
return fmt.Errorf("network unreachable: %s: %w (cannot reach %s - check network connectivity)", baseMsg, err, host)
|
|
}
|
|
|
|
// host unreachable
|
|
if errors.Is(opErr.Err, syscall.EHOSTUNREACH) {
|
|
return fmt.Errorf("host unreachable: %s: %w (cannot reach %s - host may be down or firewall blocking)", baseMsg, err, host)
|
|
}
|
|
|
|
// timeout during operation
|
|
if opErr.Timeout() {
|
|
return fmt.Errorf("network timeout during %s: %s: %w (operation at %s took too long)", opErr.Op, baseMsg, err, host)
|
|
}
|
|
|
|
return fmt.Errorf("network error during %s: %s: %w", opErr.Op, baseMsg, err)
|
|
}
|
|
|
|
// check for url parsing errors
|
|
var urlErr *url.Error
|
|
if errors.As(err, &urlErr) {
|
|
return fmt.Errorf("URL error: %s: %w (check if the endpoint URL is correctly formatted)", baseMsg, err)
|
|
}
|
|
|
|
// check for TLS/certificate errors
|
|
var certErr *x509.CertificateInvalidError
|
|
if errors.As(err, &certErr) {
|
|
return fmt.Errorf("TLS certificate invalid: %s: %w (certificate validation failed for %s)", baseMsg, err, host)
|
|
}
|
|
|
|
var unknownAuthErr *x509.UnknownAuthorityError
|
|
if errors.As(err, &unknownAuthErr) {
|
|
return fmt.Errorf("TLS certificate verification failed: %s: %w (certificate signed by unknown authority for %s)", baseMsg, err, host)
|
|
}
|
|
|
|
var hostErr *x509.HostnameError
|
|
if errors.As(err, &hostErr) {
|
|
return fmt.Errorf("TLS hostname mismatch: %s: %w (certificate is not valid for %s)", baseMsg, err, host)
|
|
}
|
|
|
|
// check for os errors
|
|
if errors.Is(err, os.ErrInvalid) {
|
|
return fmt.Errorf("invalid argument: %s: %w", baseMsg, err)
|
|
}
|
|
if errors.Is(err, os.ErrPermission) {
|
|
return fmt.Errorf("permission denied: %s: %w", baseMsg, err)
|
|
}
|
|
if errors.Is(err, os.ErrExist) {
|
|
return fmt.Errorf("file already exists: %s: %w", baseMsg, err)
|
|
}
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return fmt.Errorf("file does not exist: %s: %w", baseMsg, err)
|
|
}
|
|
if errors.Is(err, os.ErrClosed) {
|
|
return fmt.Errorf("file already closed: %s: %w", baseMsg, err)
|
|
}
|
|
if errors.Is(err, os.ErrNoDeadline) {
|
|
return fmt.Errorf("file type does not support deadline: %s: %w", baseMsg, err)
|
|
}
|
|
if errors.Is(err, os.ErrDeadlineExceeded) {
|
|
return fmt.Errorf("i/o timeout: %s: %w", baseMsg, err)
|
|
}
|
|
|
|
// check for EOF specifically
|
|
if err.Error() == "EOF" || strings.Contains(err.Error(), "EOF") {
|
|
return fmt.Errorf("unexpected connection closure: %s: %w (server at %s closed connection prematurely - possible causes: server crash, request too large, incompatible protocol, or server-side timeout)", baseMsg, err, host)
|
|
}
|
|
|
|
// check for "connection reset by peer"
|
|
if strings.Contains(err.Error(), "connection reset by peer") {
|
|
return fmt.Errorf("connection reset by peer: %s: %w (server at %s forcibly closed the connection)", baseMsg, err, host)
|
|
}
|
|
|
|
// generic error
|
|
return fmt.Errorf("HTTP request failed: %s: %w", baseMsg, err)
|
|
}
|