Files
Paulo DiasandGitHub 7fa201b721 [chore][receiver/otlpreceiver] run make modernize (#13983)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description

Run the `make modernize` tool.

https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/modernize

Signed-off-by: Paulo Dias <[email protected]>
2025-10-10 14:59:21 +00:00

262 lines
7.5 KiB
Go

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package otlpreceiver // import "go.opentelemetry.io/collector/receiver/otlpreceiver"
import (
"fmt"
"io"
"mime"
"net/http"
"strconv"
"time"
"google.golang.org/grpc/status"
"go.opentelemetry.io/collector/internal/statusutil"
"go.opentelemetry.io/collector/receiver/otlpreceiver/internal/errors"
"go.opentelemetry.io/collector/receiver/otlpreceiver/internal/logs"
"go.opentelemetry.io/collector/receiver/otlpreceiver/internal/metrics"
"go.opentelemetry.io/collector/receiver/otlpreceiver/internal/profiles"
"go.opentelemetry.io/collector/receiver/otlpreceiver/internal/trace"
)
// Pre-computed status with code=Internal to be used in case of a marshaling error.
var fallbackMsg = []byte(`{"code": 13, "message": "failed to marshal error message"}`)
const fallbackContentType = "application/json"
func handleTraces(resp http.ResponseWriter, req *http.Request, tracesReceiver *trace.Receiver) {
enc, ok := readContentType(resp, req)
if !ok {
return
}
body, ok := readAndCloseBody(resp, req, enc)
if !ok {
return
}
otlpReq, err := enc.unmarshalTracesRequest(body)
if err != nil {
writeError(resp, enc, err, http.StatusBadRequest)
return
}
otlpResp, err := tracesReceiver.Export(req.Context(), otlpReq)
if err != nil {
writeError(resp, enc, err, http.StatusInternalServerError)
return
}
msg, err := enc.marshalTracesResponse(otlpResp)
if err != nil {
writeError(resp, enc, err, http.StatusInternalServerError)
return
}
writeResponse(resp, enc.contentType(), http.StatusOK, msg)
}
func handleMetrics(resp http.ResponseWriter, req *http.Request, metricsReceiver *metrics.Receiver) {
enc, ok := readContentType(resp, req)
if !ok {
return
}
body, ok := readAndCloseBody(resp, req, enc)
if !ok {
return
}
otlpReq, err := enc.unmarshalMetricsRequest(body)
if err != nil {
writeError(resp, enc, err, http.StatusBadRequest)
return
}
otlpResp, err := metricsReceiver.Export(req.Context(), otlpReq)
if err != nil {
writeError(resp, enc, err, http.StatusInternalServerError)
return
}
msg, err := enc.marshalMetricsResponse(otlpResp)
if err != nil {
writeError(resp, enc, err, http.StatusInternalServerError)
return
}
writeResponse(resp, enc.contentType(), http.StatusOK, msg)
}
func handleLogs(resp http.ResponseWriter, req *http.Request, logsReceiver *logs.Receiver) {
enc, ok := readContentType(resp, req)
if !ok {
return
}
body, ok := readAndCloseBody(resp, req, enc)
if !ok {
return
}
otlpReq, err := enc.unmarshalLogsRequest(body)
if err != nil {
writeError(resp, enc, err, http.StatusBadRequest)
return
}
otlpResp, err := logsReceiver.Export(req.Context(), otlpReq)
if err != nil {
writeError(resp, enc, err, http.StatusInternalServerError)
return
}
msg, err := enc.marshalLogsResponse(otlpResp)
if err != nil {
writeError(resp, enc, err, http.StatusInternalServerError)
return
}
writeResponse(resp, enc.contentType(), http.StatusOK, msg)
}
func handleProfiles(resp http.ResponseWriter, req *http.Request, profilesReceiver *profiles.Receiver) {
enc, ok := readContentType(resp, req)
if !ok {
return
}
body, ok := readAndCloseBody(resp, req, enc)
if !ok {
return
}
otlpReq, err := enc.unmarshalProfilesRequest(body)
if err != nil {
writeError(resp, enc, err, http.StatusBadRequest)
return
}
otlpResp, err := profilesReceiver.Export(req.Context(), otlpReq)
if err != nil {
writeError(resp, enc, err, http.StatusInternalServerError)
return
}
msg, err := enc.marshalProfilesResponse(otlpResp)
if err != nil {
writeError(resp, enc, err, http.StatusInternalServerError)
return
}
writeResponse(resp, enc.contentType(), http.StatusOK, msg)
}
func readContentType(resp http.ResponseWriter, req *http.Request) (encoder, bool) {
if req.Method != http.MethodPost {
handleUnmatchedMethod(resp)
return nil, false
}
switch getMimeTypeFromContentType(req.Header.Get("Content-Type")) {
case pbContentType:
return pbEncoder, true
case jsonContentType:
return jsEncoder, true
default:
handleUnmatchedContentType(resp)
return nil, false
}
}
func readAndCloseBody(resp http.ResponseWriter, req *http.Request, enc encoder) ([]byte, bool) {
body, err := io.ReadAll(req.Body)
if err != nil {
writeError(resp, enc, err, http.StatusBadRequest)
return nil, false
}
if err = req.Body.Close(); err != nil {
writeError(resp, enc, err, http.StatusBadRequest)
return nil, false
}
return body, true
}
// writeError encodes the HTTP error inside a rpc.Status message as required by the OTLP protocol.
func writeError(w http.ResponseWriter, encoder encoder, err error, statusCode int) {
s, ok := status.FromError(err)
if ok {
statusCode = errors.GetHTTPStatusCodeFromStatus(s)
} else {
s = statusutil.NewStatusFromMsgAndHTTPCode(err.Error(), statusCode)
}
writeStatusResponse(w, encoder, statusCode, s)
}
// errorHandler encodes the HTTP error message inside a rpc.Status message as required
// by the OTLP protocol.
func errorHandler(w http.ResponseWriter, r *http.Request, errMsg string, statusCode int) {
s := statusutil.NewStatusFromMsgAndHTTPCode(errMsg, statusCode)
contentType := r.Header.Get("Content-Type")
if contentType == "" {
contentType = fallbackContentType
}
switch getMimeTypeFromContentType(contentType) {
case pbContentType:
writeStatusResponse(w, pbEncoder, statusCode, s)
return
case jsonContentType:
writeStatusResponse(w, jsEncoder, statusCode, s)
return
}
writeResponse(w, fallbackContentType, http.StatusInternalServerError, fallbackMsg)
}
func writeStatusResponse(w http.ResponseWriter, enc encoder, statusCode int, st *status.Status) {
// https://github.com/open-telemetry/opentelemetry-proto/blob/main/docs/specification.md#otlphttp-throttling
if statusCode == http.StatusTooManyRequests || statusCode == http.StatusServiceUnavailable {
retryInfo := statusutil.GetRetryInfo(st)
// Check if server returned throttling information.
if retryInfo != nil {
// We are throttled. Wait before retrying as requested by the server.
// The value of Retry-After field can be either an HTTP-date or a number of
// seconds to delay after the response is received. See https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.3
//
// Retry-After = HTTP-date / delay-seconds
//
// Use delay-seconds since is easier to format as well as does not require clock synchronization.
w.Header().Set("Retry-After", strconv.FormatInt(int64(retryInfo.GetRetryDelay().AsDuration()/time.Second), 10))
}
}
msg, err := enc.marshalStatus(st.Proto())
if err != nil {
writeResponse(w, fallbackContentType, http.StatusInternalServerError, fallbackMsg)
return
}
writeResponse(w, enc.contentType(), statusCode, msg)
}
func writeResponse(w http.ResponseWriter, contentType string, statusCode int, msg []byte) {
w.Header().Set("Content-Type", contentType)
w.WriteHeader(statusCode)
// Nothing we can do with the error if we cannot write to the response.
_, _ = w.Write(msg)
}
func getMimeTypeFromContentType(contentType string) string {
mediatype, _, err := mime.ParseMediaType(contentType)
if err != nil {
return ""
}
return mediatype
}
func handleUnmatchedMethod(resp http.ResponseWriter) {
hst := http.StatusMethodNotAllowed
writeResponse(resp, "text/plain", hst, fmt.Appendf(nil, "%v method not allowed, supported: [POST]", hst))
}
func handleUnmatchedContentType(resp http.ResponseWriter) {
hst := http.StatusUnsupportedMediaType
writeResponse(resp, "text/plain", hst, fmt.Appendf(nil, "%v unsupported media type, supported: [%s, %s]", hst, jsonContentType, pbContentType))
}