99 lines
3.1 KiB
Go
99 lines
3.1 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log"
|
|
"mime"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
)
|
|
|
|
// isJSONLike checks if a string looks like it might be JSON
|
|
func isJSONLike(s string) bool {
|
|
s = strings.TrimSpace(s)
|
|
return (strings.HasPrefix(s, "{") && strings.HasSuffix(s, "}")) ||
|
|
(strings.HasPrefix(s, "[") && strings.HasSuffix(s, "]"))
|
|
}
|
|
|
|
// RequestEcho represents the structure of our response
|
|
type RequestEcho struct {
|
|
URL string `json:"url"`
|
|
Method string `json:"method"`
|
|
Path string `json:"path"`
|
|
QueryParams url.Values `json:"query_params"`
|
|
Headers http.Header `json:"headers"` // Use http.Header for canonical keys
|
|
Body interface{} `json:"body"` // Can be structured JSON or raw string
|
|
BodyRaw string `json:"body_raw"` // Original body as string
|
|
RemoteAddr string `json:"remote_addr"`
|
|
ContentType string `json:"content_type"`
|
|
ContentLength int64 `json:"content_length"`
|
|
}
|
|
|
|
func EchoHandler() http.Handler
|
|
{
|
|
// Read body
|
|
bodyBytes := c.Body() // Fiber's way to get the body
|
|
bodyString := string(bodyBytes)
|
|
|
|
// Try to parse body as JSON
|
|
var parsedBody interface{}
|
|
contentTypeHeader := c.Get("Content-Type") // Get Content-Type header
|
|
|
|
if len(bodyBytes) > 0 {
|
|
mediaType, _, err := mime.ParseMediaType(contentTypeHeader)
|
|
// Only attempt JSON parsing if Content-Type is application/json or it looks like JSON
|
|
if (err == nil && mediaType == "application/json") || isJSONLike(bodyString) {
|
|
if jsonErr := json.Unmarshal(bodyBytes, &parsedBody); jsonErr != nil {
|
|
// If JSON parsing fails, parsedBody remains nil.
|
|
// It will be set to bodyString later if it's still nil.
|
|
parsedBody = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
if parsedBody == nil && len(bodyBytes) > 0 {
|
|
// For non-JSON bodies or if JSON parsing failed, use the raw string
|
|
parsedBody = bodyString
|
|
}
|
|
|
|
// Query Parameters
|
|
queryParams, _ := url.ParseQuery(string(c.Request().URI().QueryString()))
|
|
|
|
// Headers - ensuring canonical keys like net/http.Header
|
|
headers := make(http.Header)
|
|
c.Context().Request.Header.VisitAll(func(key, value []byte) {
|
|
k := string(key)
|
|
v := string(value)
|
|
headers.Add(k, v) // http.Header.Add appends, and canonicalizes the key on first Set/Add
|
|
})
|
|
|
|
// Create our response structure
|
|
echo := RequestEcho{
|
|
URL: c.OriginalURL(), // Path and query string
|
|
Method: c.Method(),
|
|
Path: c.Path(),
|
|
QueryParams: queryParams,
|
|
Headers: headers,
|
|
Body: parsedBody,
|
|
BodyRaw: bodyString,
|
|
RemoteAddr: c.Context().RemoteAddr().String(), // IP and Port
|
|
ContentType: contentTypeHeader,
|
|
ContentLength: int64(c.Context().Request.Header.ContentLength()),
|
|
}
|
|
|
|
// Marshal to JSON and write response
|
|
jsonResponse, err := json.MarshalIndent(echo, "", " ")
|
|
if err != nil {
|
|
log.Printf("Error creating JSON response: %v", err)
|
|
return c.Status(http.StatusInternalServerError).SendString("Error creating JSON response")
|
|
}
|
|
|
|
c.Set("Content-Type", "application/json")
|
|
c.Status(http.StatusOK)
|
|
// Log the request to stdout
|
|
// fmt.Printf("Received %s request to %s\n", c.Method(), c.Path())
|
|
return c.Send(jsonResponse)
|
|
}
|