Moonshark/http/server.go
2025-06-06 18:57:47 -05:00

168 lines
4.4 KiB
Go

// server.go - Simplified HTTP server
package http
import (
"fmt"
"strings"
"time"
"Moonshark/config"
"Moonshark/metadata"
"Moonshark/runner"
"Moonshark/utils"
"github.com/goccy/go-json"
"github.com/valyala/bytebufferpool"
"github.com/valyala/fasthttp"
)
func NewHttpServer(cfg *config.Config, handler fasthttp.RequestHandler, dbg bool) *fasthttp.Server {
return &fasthttp.Server{
Handler: handler,
Name: "Moonshark/" + metadata.Version,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
MaxRequestBodySize: 16 << 20,
TCPKeepalive: true,
ReduceMemoryUsage: true,
StreamRequestBody: true,
NoDefaultServerHeader: true,
}
}
func NewPublicHandler(pubDir, prefix string) fasthttp.RequestHandler {
if !strings.HasPrefix(prefix, "/") {
prefix = "/" + prefix
}
if !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
fs := &fasthttp.FS{
Root: pubDir,
IndexNames: []string{"index.html"},
AcceptByteRange: true,
Compress: true,
CompressedFileSuffix: ".gz",
CompressBrotli: true,
PathRewrite: fasthttp.NewPathPrefixStripper(len(prefix) - 1),
}
return fs.NewRequestHandler()
}
func Send404(ctx *fasthttp.RequestCtx) {
ctx.SetContentType("text/html; charset=utf-8")
ctx.SetStatusCode(fasthttp.StatusNotFound)
ctx.SetBody([]byte(utils.NotFoundPage(ctx.URI().String())))
}
func Send500(ctx *fasthttp.RequestCtx, err error) {
ctx.SetContentType("text/html; charset=utf-8")
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
if err == nil {
ctx.SetBody([]byte(utils.InternalErrorPage(string(ctx.Path()), "")))
} else {
ctx.SetBody([]byte(utils.InternalErrorPage(string(ctx.Path()), err.Error())))
}
}
// ApplyResponse applies a Response to a fasthttp.RequestCtx
func ApplyResponse(resp *runner.Response, ctx *fasthttp.RequestCtx) {
// Set status code
ctx.SetStatusCode(resp.Status)
// Set headers
for name, value := range resp.Headers {
ctx.Response.Header.Set(name, value)
}
// Set cookies
for _, cookie := range resp.Cookies {
ctx.Response.Header.SetCookie(cookie)
}
// Process the body based on its type
if resp.Body == nil {
return
}
// Check if Content-Type was manually set
contentTypeSet := false
for name := range resp.Headers {
if strings.ToLower(name) == "content-type" {
contentTypeSet = true
break
}
}
// Get a buffer from the pool
buf := bytebufferpool.Get()
defer bytebufferpool.Put(buf)
// Set body based on type
switch body := resp.Body.(type) {
case string:
if !contentTypeSet {
ctx.Response.Header.SetContentType("text/plain; charset=utf-8")
}
ctx.SetBodyString(body)
case []byte:
if !contentTypeSet {
ctx.Response.Header.SetContentType("text/plain; charset=utf-8")
}
ctx.SetBody(body)
case map[string]any, map[any]any, []any, []float64, []string, []int, []map[string]any:
// Marshal JSON
if err := json.NewEncoder(buf).Encode(body); err == nil {
if !contentTypeSet {
ctx.Response.Header.SetContentType("application/json")
}
ctx.SetBody(buf.Bytes())
} else {
// Fallback to string representation
if !contentTypeSet {
ctx.Response.Header.SetContentType("text/plain; charset=utf-8")
}
ctx.SetBodyString(fmt.Sprintf("%v", body))
}
default:
// Check if it's any other map or slice type
typeStr := fmt.Sprintf("%T", body)
if typeStr[0] == '[' || (len(typeStr) > 3 && typeStr[:3] == "map") {
if err := json.NewEncoder(buf).Encode(body); err == nil {
if !contentTypeSet {
ctx.Response.Header.SetContentType("application/json")
}
ctx.SetBody(buf.Bytes())
} else {
if !contentTypeSet {
ctx.Response.Header.SetContentType("text/plain; charset=utf-8")
}
ctx.SetBodyString(fmt.Sprintf("%v", body))
}
} else {
// Default to string representation
if !contentTypeSet {
ctx.Response.Header.SetContentType("text/plain; charset=utf-8")
}
ctx.SetBodyString(fmt.Sprintf("%v", body))
}
}
}
/*
func HandleDebugStats(ctx *fasthttp.RequestCtx) {
stats := utils.CollectSystemStats(s.cfg)
stats.Components = utils.ComponentStats{
RouteCount: 0, // TODO: Get from router
BytecodeBytes: 0, // TODO: Get from router
SessionStats: s.sessionManager.GetCacheStats(),
}
ctx.SetContentType("text/html; charset=utf-8")
ctx.SetStatusCode(fasthttp.StatusOK)
ctx.SetBody([]byte(utils.DebugStatsPage(stats)))
}
*/