Ctrl K
ComunidadGuías…

internal/api/response_compression_server_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package api

import (
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/router-for-me/CLIProxyAPI/v8/internal/config"
	"github.com/router-for-me/CLIProxyAPI/v8/internal/httpwire"
	log "github.com/sirupsen/logrus"
)

func TestResponseCompressionServerHotReload(t *testing.T) {
	cfg := &config.Config{}
	server := newTestServerWithConfig(t, cfg)
	body := strings.Repeat("response text ", 128)
	server.engine.GET("/compression-test", func(c *gin.Context) { c.String(http.StatusOK, body) })
	for _, tc := range []struct {
		enabled  bool
		minBytes int
		encoding string
	}{
		{enabled: true, encoding: "zstd"},
		{enabled: false},
		{enabled: true, minBytes: len(body) + 1},
		{enabled: true, minBytes: 1, encoding: "zstd"},
	} {
		cfg.ServerResponseCompression.Enabled = &tc.enabled
		cfg.ServerResponseCompression.MinBytes = tc.minBytes
		server.UpdateClients(cfg)
		request := httptest.NewRequest(http.MethodGet, "/compression-test", nil)
		request.Header.Set("Accept-Encoding", "zstd")
		response := httptest.NewRecorder()
		server.engine.ServeHTTP(response, request)
		if got := response.Header().Get("Content-Encoding"); got != tc.encoding {
			t.Fatalf("after reload Content-Encoding = %q, want %q", got, tc.encoding)
		}
		if got := string(compressionTestBody(t, tc.encoding, response.Body.Bytes())); got != body {
			t.Fatal("body differs after config reload")
		}
	}
}

func TestWebsocketCompressionServerHotReload(t *testing.T) {
	cfg := &config.Config{}
	server := newTestServerWithConfig(t, cfg)
	httpServer := httptest.NewServer(server.engine)
	defer httpServer.Close()
	dialer := websocket.Dialer{EnableCompression: true}
	for _, enabled := range []bool{true, false, true} {
		cfg.Codex.WebsocketCompression = &enabled
		server.UpdateClients(cfg)
		conn, response, errDial := dialer.Dial("ws"+strings.TrimPrefix(httpServer.URL, "http")+"/v1/responses", nil)
		if errDial != nil {
			t.Fatal(errDial)
		}
		negotiated := strings.Contains(response.Header.Get("Sec-WebSocket-Extensions"), "permessage-deflate")
		if errClose := conn.Close(); errClose != nil {
			log.Errorf("close server compression test websocket: %v", errClose)
		}
		if negotiated != enabled {
			t.Fatalf("after reload compression negotiated = %v, want %v", negotiated, enabled)
		}
	}
}

type compressionErrorWriter struct {
	gin.ResponseWriter
}

func (w *compressionErrorWriter) Write(data []byte) (int, error) { return 0, io.ErrClosedPipe }
func (w *compressionErrorWriter) FlushError() error              { return io.ErrClosedPipe }

func TestResponseCompressionFlushError(t *testing.T) {
	for _, encoding := range []string{"gzip", "zstd"} {
		t.Run(encoding, func(t *testing.T) {
			context, _ := gin.CreateTestContext(httptest.NewRecorder())
			context.Header("Content-Type", "text/event-stream")
			writer := &responseCompressionWriter{
				ResponseWriter: &compressionErrorWriter{ResponseWriter: context.Writer},
				encoding:       encoding,
				minBytes:       1024,
			}
			_, _ = writer.WriteString("data: test\n\n")
			if errFlush := httpwire.FlushResponse(writer); !errors.Is(errFlush, io.ErrClosedPipe) {
				t.Fatalf("flush error = %v", errFlush)
			}
			if _, errWrite := writer.WriteString("data: later\n\n"); !errors.Is(errWrite, io.ErrClosedPipe) {
				t.Fatalf("subsequent write error = %v", errWrite)
			}
			writer.finish()
		})
	}
}