2021-12-16 20:54:59 +03:00
|
|
|
// Package aghhttp provides some common methods to work with HTTP.
|
|
|
|
package aghhttp
|
|
|
|
|
|
|
|
import (
|
2024-09-09 13:31:54 +03:00
|
|
|
"context"
|
2021-12-16 20:54:59 +03:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2024-09-09 13:31:54 +03:00
|
|
|
"log/slog"
|
2021-12-16 20:54:59 +03:00
|
|
|
"net/http"
|
|
|
|
|
2022-09-29 19:04:26 +03:00
|
|
|
"github.com/AdguardTeam/AdGuardHome/internal/version"
|
2023-04-07 14:21:37 +03:00
|
|
|
"github.com/AdguardTeam/golibs/httphdr"
|
2021-12-16 20:54:59 +03:00
|
|
|
"github.com/AdguardTeam/golibs/log"
|
2024-09-09 13:31:54 +03:00
|
|
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
2021-12-16 20:54:59 +03:00
|
|
|
)
|
|
|
|
|
2022-08-04 19:05:28 +03:00
|
|
|
// RegisterFunc is the function that sets the handler to handle the URL for the
|
|
|
|
// method.
|
|
|
|
//
|
|
|
|
// TODO(e.burkov, a.garipov): Get rid of it.
|
|
|
|
type RegisterFunc func(method, url string, handler http.HandlerFunc)
|
|
|
|
|
2021-12-16 20:54:59 +03:00
|
|
|
// OK responds with word OK.
|
|
|
|
func OK(w http.ResponseWriter) {
|
|
|
|
if _, err := io.WriteString(w, "OK\n"); err != nil {
|
|
|
|
log.Error("couldn't write body: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error writes formatted message to w and also logs it.
|
2024-09-09 13:31:54 +03:00
|
|
|
//
|
|
|
|
// TODO(s.chzhen): Remove it.
|
2022-08-03 14:36:18 +03:00
|
|
|
func Error(r *http.Request, w http.ResponseWriter, code int, format string, args ...any) {
|
2021-12-16 20:54:59 +03:00
|
|
|
text := fmt.Sprintf(format, args...)
|
2022-09-30 14:41:25 +03:00
|
|
|
log.Error("%s %s %s: %s", r.Method, r.Host, r.URL, text)
|
2021-12-16 20:54:59 +03:00
|
|
|
http.Error(w, text, code)
|
|
|
|
}
|
2022-09-29 19:04:26 +03:00
|
|
|
|
2024-09-09 13:31:54 +03:00
|
|
|
// ErrorAndLog writes formatted message to w and also logs it with the specified
|
|
|
|
// logging level.
|
|
|
|
func ErrorAndLog(
|
|
|
|
ctx context.Context,
|
|
|
|
l *slog.Logger,
|
|
|
|
r *http.Request,
|
|
|
|
w http.ResponseWriter,
|
|
|
|
code int,
|
|
|
|
format string,
|
|
|
|
args ...any,
|
|
|
|
) {
|
|
|
|
text := fmt.Sprintf(format, args...)
|
|
|
|
l.ErrorContext(
|
|
|
|
ctx,
|
|
|
|
"http error",
|
|
|
|
"host", r.Host,
|
|
|
|
"method", r.Method,
|
|
|
|
"raddr", r.RemoteAddr,
|
|
|
|
"request_uri", r.RequestURI,
|
|
|
|
slogutil.KeyError, text,
|
|
|
|
)
|
|
|
|
|
|
|
|
http.Error(w, text, code)
|
|
|
|
}
|
|
|
|
|
2022-09-29 19:04:26 +03:00
|
|
|
// UserAgent returns the ID of the service as a User-Agent string. It can also
|
|
|
|
// be used as the value of the Server HTTP header.
|
|
|
|
func UserAgent() (ua string) {
|
|
|
|
return fmt.Sprintf("AdGuardHome/%s", version.Version())
|
|
|
|
}
|
|
|
|
|
|
|
|
// textPlainDeprMsg is the message returned to API users when they try to use
|
|
|
|
// an API that used to accept "text/plain" but doesn't anymore.
|
|
|
|
const textPlainDeprMsg = `using this api with the text/plain content-type is deprecated; ` +
|
|
|
|
`use application/json`
|
|
|
|
|
|
|
|
// WriteTextPlainDeprecated responds to the request with a message about
|
|
|
|
// deprecation and removal of a plain-text API if the request is made with the
|
|
|
|
// "text/plain" content-type.
|
|
|
|
func WriteTextPlainDeprecated(w http.ResponseWriter, r *http.Request) (isPlainText bool) {
|
2023-04-07 14:21:37 +03:00
|
|
|
if r.Header.Get(httphdr.ContentType) != HdrValTextPlain {
|
2022-09-29 19:04:26 +03:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
Error(r, w, http.StatusUnsupportedMediaType, textPlainDeprMsg)
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|