Fix the issue of local model data synchronization errors

This commit is contained in:
Liujian
2025-03-13 22:15:22 +08:00
parent 6329c1441e
commit cbb3328395
19 changed files with 254 additions and 95 deletions

View File

@@ -8,6 +8,7 @@ var (
AIModelTotalTokenLabel = "ai_model_total_token"
AIModelModeLabel = "ai_model_mode"
AIModelLabel = "ai_model"
AIKeyLabel = "ai_key"
AIProviderLabel = "ai_provider"
AIProviderStatusesLabel = "ai_provider_statuses"
AIModelStatusLabel = "ai_model_status"
@@ -69,6 +70,14 @@ func GetAIModel(ctx eocontext.EoContext) string {
return valueString(ctx, AIModelLabel)
}
func SetAIKey(ctx eocontext.EoContext, key string) {
ctx.WithValue(AIKeyLabel, key)
}
func GetAIKey(ctx eocontext.EoContext) string {
return valueString(ctx, AIKeyLabel)
}
func SetAIProvider(ctx eocontext.EoContext, provider string) {
ctx.WithValue(AIProviderLabel, provider)
}

View File

@@ -86,6 +86,7 @@ func (m *KeyPoolManager) Set(id string, resource IKeyResource) {
sort.Slice(keys, func(i, j int) bool {
return keys[i].Priority() < keys[j].Priority()
})
m.keySorts.Set(id, keys)
}

View File

@@ -36,12 +36,12 @@ func TransformData(inputJSON string, mappingRule MappingRule) (map[string]interf
if err != nil {
return nil, fmt.Errorf("类型转换失败 %s -> %s: %v", key, v.Value, err)
}
//if value == "response_format" {
// resultMap[v.Value] = map[string]interface{}{
// "type": convertedValue,
// }
// continue
//}
if value == "response_format" {
resultMap[v.Value] = map[string]interface{}{
"type": convertedValue,
}
continue
}
resultMap[v.Value] = convertedValue
} else {
resultMap[key] = value

View File

@@ -6,7 +6,24 @@ import (
)
// Request 定义客户端统一输入请求格式
type Request openai.ChatCompletionRequest
type Request struct {
Model string `json:"model"`
Messages []openai.ChatCompletionMessage `json:"messages"`
// MaxTokens The maximum number of tokens that can be generated in the chat completion.
// This value can be used to control costs for text generated via API.
// This value is now deprecated in favor of max_completion_tokens, and is not compatible with o1 series models.
// refs: https://platform.openai.com/docs/api-reference/chat/create#chat-create-max_tokens
MaxTokens int `json:"max_tokens,omitempty"`
// MaxCompletionTokens An upper bound for the number of tokens that can be generated for a completion,
// including visible output tokens and reasoning tokens https://platform.openai.com/docs/guides/reasoning
MaxCompletionTokens int `json:"max_completion_tokens,omitempty"`
Temperature float32 `json:"temperature,omitempty"`
TopP float32 `json:"top_p,omitempty"`
N int `json:"n,omitempty"`
Stream bool `json:"stream,omitempty"`
Stop []string `json:"stop,omitempty"`
PresencePenalty float32 `json:"presence_penalty,omitempty"`
}
// Response 定义客户端统一输出响应格式
type Response struct {

View File

@@ -1,8 +1,6 @@
package ai_convert
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"net/url"
@@ -49,6 +47,9 @@ func NewOpenAIConvert(apikey string, baseUrl string, timeout time.Duration, chec
if err != nil {
return nil, err
}
if strings.TrimSuffix(u.Path, "/") == "" {
u.Path = "/v1"
}
c.path = fmt.Sprintf("%s%s", strings.TrimSuffix(u.Path, "/"), OpenAIChatCompletePath)
} else {
c.path = fmt.Sprintf("/v1%s", OpenAIChatCompletePath)
@@ -66,7 +67,7 @@ func (o *OpenAIConvert) RequestConvert(ctx eoscContext.EoContext, extender map[s
return err
}
var promptToken int
chatRequest := eosc.NewBase[openai.ChatCompletionRequest](extender)
chatRequest := eosc.NewBase[Request](extender)
err = json.Unmarshal(body, chatRequest)
if err != nil {
return fmt.Errorf("unmarshal body error: %v, body: %s", err, string(body))
@@ -89,6 +90,7 @@ func (o *OpenAIConvert) RequestConvert(ctx eoscContext.EoContext, extender map[s
if o.balanceHandler != nil {
ctx.SetBalance(o.balanceHandler)
}
httpContext.AppendBodyFinishFunc(o.bodyFinish)
return nil
}
@@ -133,36 +135,63 @@ func (o *OpenAIConvert) ResponseConvert(ctx eoscContext.EoContext) error {
return ResponseConvert(ctx, o.checkErr, o.errorCallback)
}
func (o *OpenAIConvert) bodyFinish(ctx http_service.IHttpContext) {
body := ctx.Response().GetBody()
defer func() {
SetAIProviderStatuses(ctx, AIProviderStatus{
Provider: GetAIProvider(ctx),
Model: GetAIModel(ctx),
Key: GetAIKey(ctx),
Status: GetAIStatus(ctx),
})
}()
if o.checkErr != nil && !o.checkErr(ctx, body) {
o.errorCallback(ctx, body)
return
}
encoding := ctx.Response().Headers().Get("content-encoding")
if encoding == "gzip" {
tmp, err := encoder.ToUTF8(encoding, body)
if err != nil {
log.Errorf("convert to utf-8 error: %v, body: %s", err, string(body))
return
}
var resp openai.ChatCompletionResponse
err = json.Unmarshal(tmp, &resp)
if err != nil {
log.Errorf("unmarshal body error: %v, body: %s", err, string(tmp))
return
}
SetAIModelInputToken(ctx, resp.Usage.PromptTokens)
SetAIModelOutputToken(ctx, resp.Usage.CompletionTokens)
SetAIModelTotalToken(ctx, resp.Usage.TotalTokens)
}
}
func (o *OpenAIConvert) streamHandler(ctx http_service.IHttpContext, p []byte) ([]byte, error) {
encoding := ctx.Response().Headers().Get("content-encoding")
if encoding == "gzip" {
return p, nil
}
// 对响应数据进行划分
inputToken := GetAIModelInputToken(ctx)
outputToken := 0
totalToken := inputToken
scanner := bufio.NewScanner(bytes.NewReader(p))
// Check the content encoding and convert to UTF-8 if necessary.
encoding := ctx.Response().Headers().Get("content-encoding")
for scanner.Scan() {
line := scanner.Text()
line := string(p)
if encoding != "utf-8" && encoding != "" {
tmp, err := encoder.ToUTF8(encoding, []byte(line))
tmp, err := encoder.ToUTF8(encoding, p)
if err != nil {
log.Errorf("convert to utf-8 error: %v, line: %s", err, line)
return p, nil
}
if ctx.Response().StatusCode() != 200 || (o.checkErr != nil && !o.checkErr(ctx, tmp)) {
if o.errorCallback != nil {
o.errorCallback(ctx, tmp)
}
return p, nil
}
line = string(tmp)
}
line = strings.TrimPrefix(line, "data:")
if line == "" || strings.Trim(line, " ") == "[DONE]" {
return p, nil
}
var resp openai.ChatCompletionResponse
err := json.Unmarshal([]byte(line), &resp)
err = json.Unmarshal([]byte(line), &resp)
if err != nil {
return p, nil
}
@@ -171,10 +200,6 @@ func (o *OpenAIConvert) streamHandler(ctx http_service.IHttpContext, p []byte) (
totalToken += outputToken
}
}
if err := scanner.Err(); err != nil {
log.Errorf("scan error: %v", err)
return p, nil
}
SetAIModelInputToken(ctx, inputToken)
SetAIModelOutputToken(ctx, outputToken)

View File

@@ -142,6 +142,7 @@ func (c *Convert) RequestConvert(ctx eocontext.EoContext, extender map[string]in
}
httpContext.Proxy().Body().SetRaw("application/json", body)
httpContext.Response().AppendStreamFunc(c.streamHandler)
ctx.SetLabel("response-content-type", "text/event-stream")
return nil
}

View File

@@ -15,6 +15,7 @@ import (
_ "github.com/eolinker/apinto/drivers/ai-provider/hugging-face"
_ "github.com/eolinker/apinto/drivers/ai-provider/hunyuan"
_ "github.com/eolinker/apinto/drivers/ai-provider/lm-studio"
_ "github.com/eolinker/apinto/drivers/ai-provider/local-model"
_ "github.com/eolinker/apinto/drivers/ai-provider/minimax"
_ "github.com/eolinker/apinto/drivers/ai-provider/mistralai"
_ "github.com/eolinker/apinto/drivers/ai-provider/moonshot"

View File

@@ -0,0 +1,71 @@
package local_model
import (
"encoding/json"
"fmt"
"net/url"
ai_convert "github.com/eolinker/apinto/ai-convert"
http_service "github.com/eolinker/eosc/eocontext/http-context"
)
func init() {
ai_convert.RegisterConverterCreateFunc("LocalModel", Create)
}
type Config struct {
BaseUrl string `json:"base"`
}
// checkConfig validates the provided configuration.
// It ensures the required fields are set and checks the validity of the Base URL if provided.
//
// Parameters:
// - v: An interface{} expected to be a pointer to a Config struct.
//
// Returns:
// - *Config: The validated configuration cast to *Config.
// - error: An error if the validation fails, or nil if it succeeds.
func checkConfig(conf *Config) error {
if conf.BaseUrl == "" {
return fmt.Errorf("base url is required")
}
u, err := url.Parse(conf.BaseUrl)
if err != nil {
// Return an error if the Base URL cannot be parsed.
return fmt.Errorf("base url is invalid")
}
// Ensure the parsed URL contains both a scheme and a host.
if u.Scheme == "" || u.Host == "" {
return fmt.Errorf("base url is invalid")
}
return nil
}
func Create(cfg string) (ai_convert.IConverter, error) {
var conf Config
err := json.Unmarshal([]byte(cfg), &conf)
if err != nil {
return nil, err
}
err = checkConfig(&conf)
if err != nil {
return nil, err
}
return ai_convert.NewOpenAIConvert("", conf.BaseUrl, 0, nil, errorCallback)
}
func errorCallback(ctx http_service.IHttpContext, body []byte) {
switch ctx.Response().StatusCode() {
case 400:
// Handle the bad request error.
ai_convert.SetAIStatusInvalidRequest(ctx)
case 401:
// Handle the key error.
ai_convert.SetAIStatusInvalid(ctx)
default:
ai_convert.SetAIStatusInvalidRequest(ctx)
}
}

View File

@@ -40,10 +40,10 @@ var (
Value: "top_k",
Type: "int",
},
//{
// Value: "response_format",
// Type: "string",
//},
{
Value: "response_format",
Type: "string",
},
{
Value: "stream",
Type: "bool",

View File

@@ -70,7 +70,7 @@ func (o *Output) Reset(conf interface{}, workers map[eosc.RequireId]eosc.IWorker
}
func (o *Output) reset(conf *Config) error {
if reflect.DeepEqual(conf, o.outputChan) {
if reflect.DeepEqual(conf, o.conf) {
return nil
}
//创建formatter
@@ -136,7 +136,7 @@ func (o *Output) Output(entry eosc.IEntry) error {
},
},
}
return eosc.ErrorWorkerNotRunning
return nil
}
func (o *Output) genRequest(data []byte) (*http.Request, error) {

View File

@@ -76,7 +76,10 @@ func (e *executor) doConverter(ctx http_context.IHttpContext, next eocontext.ICh
}
}
if ctx.Response().IsBodyStream() {
ctx.Response().SetHeader("Content-Type", "text/event-stream")
contentType := ctx.GetLabel("response-content-type")
if ctx.GetLabel("response-content-type") != "" {
ctx.Response().SetHeader("Content-Type", contentType)
}
return nil
}
if err := resource.ResponseConvert(ctx); err != nil {
@@ -104,6 +107,7 @@ func (e *executor) tryProvider(ctx http_context.IHttpContext, originProxy http_c
}
ctx.SetProxy(originProxy)
for _, resource := range resources {
ai_convert.SetAIKey(ctx, resource.ID())
err := e.doConverter(ctx, next, resource, provider, extender)
if err != nil {
log.Errorf("try provider error: %v", err)
@@ -138,8 +142,21 @@ func (e *executor) DoHttpFilter(ctx http_context.IHttpContext, next eocontext.IC
if ai_convert.GetAIModel(ctx) == "" {
ai_convert.SetAIModel(ctx, e.model)
}
defer func() {
// If the request is successful, set the AI provider and model in the response headers
ctx.Response().SetHeader("X-AI-Provider", ai_convert.GetAIProvider(ctx))
ctx.Response().SetHeader("X-AI-Model", ai_convert.GetAIModel(ctx))
}()
if err := e.processKeyPool(ctx, provider, cloneProxy, next); err != nil {
balances := ai_convert.Balances()
if len(balances) == 0 {
body := ctx.Response().GetBody()
if len(body) == 0 {
ctx.Response().SetBody([]byte(err.Error()))
ctx.Response().SetStatus(400, "Bad Request")
}
return err
}
err = e.doBalance(ctx, cloneProxy, next) // Fallback to balance logic
if err != nil {
ctx.Response().SetBody([]byte(err.Error()))
@@ -147,9 +164,6 @@ func (e *executor) DoHttpFilter(ctx http_context.IHttpContext, next eocontext.IC
return err
}
}
// If the request is successful, set the AI provider and model in the response headers
ctx.Response().SetHeader("X-AI-Provider", ai_convert.GetAIProvider(ctx))
ctx.Response().SetHeader("X-AI-Model", ai_convert.GetAIModel(ctx))
return nil
}
@@ -176,7 +190,7 @@ func (e *executor) processKeyPool(ctx http_context.IHttpContext, provider string
if !r.Health() {
continue
}
ai_convert.SetAIKey(ctx, r.ID())
if err = r.RequestConvert(ctx, extender); err != nil {
ai_convert.SetAIProviderStatuses(ctx, ai_convert.AIProviderStatus{
Provider: e.provider,
@@ -201,7 +215,10 @@ func (e *executor) processKeyPool(ctx http_context.IHttpContext, provider string
}
}
if ctx.Response().IsBodyStream() {
ctx.Response().SetHeader("Content-Type", "text/event-stream")
contentType := ctx.GetLabel("response-content-type")
if ctx.GetLabel("response-content-type") != "" {
ctx.Response().SetHeader("Content-Type", contentType)
}
return nil
}
if err = r.ResponseConvert(ctx); err != nil {

View File

@@ -15,6 +15,7 @@ func (f *Finisher) Finish(org eocontext.EoContext) error {
if err != nil {
return err
}
ctx.SetLabel("current_running", "false")
ctx.FastFinish()
return nil

View File

@@ -2,6 +2,7 @@ package http_complete
import (
"errors"
"fmt"
"strconv"
"strings"
@@ -34,7 +35,10 @@ func (h *HttpComplete) Complete(org eocontext.EoContext) error {
}
//设置响应开始时间
proxyTime := time.Now()
balance := ctx.GetBalance()
if balance == nil {
return fmt.Errorf("balance not found")
}
defer func() {
//设置原始响应状态码
ctx.Response().SetProxyStatus(ctx.Response().StatusCode(), "")
@@ -42,8 +46,6 @@ func (h *HttpComplete) Complete(org eocontext.EoContext) error {
ctx.SetLabel("handler", "proxy")
}()
balance := ctx.GetBalance()
scheme := balance.Scheme()
switch strings.ToLower(scheme) {

View File

@@ -98,6 +98,7 @@ func (m *Manager) FastHandler(port int, ctx *fasthttp.RequestCtx) {
(*globalFilters).Chain(httpContext, completeCaller)
}
} else {
httpContext.SetLabel("current_running", "true")
log.Debug("match has:", port)
r.Serve(httpContext)
}

View File

@@ -1,35 +1,40 @@
package encoder
import (
"bytes"
"compress/gzip"
"io"
"github.com/valyala/bytebufferpool"
"github.com/valyala/fasthttp"
)
func init() {
//encoderManger.Set("gzip", &Gzip{})
encoderManger.Set("gzip", &Gzip{})
}
type Gzip struct {
}
func (g *Gzip) ToUTF8(data []byte) ([]byte, error) {
var bb bytebufferpool.ByteBuffer
_, err := fasthttp.WriteGunzip(&bb, data)
if err != nil {
return nil, err
}
return bb.B, nil
// 创建一个gzip reader
reader, err := gzip.NewReader(bytes.NewReader(data))
if err != nil {
return nil, err
}
defer reader.Close()
// 读取解压后的数据
var buf bytes.Buffer
_, err = io.Copy(&buf, reader)
if err != nil {
return nil, err
}
// 返回解压后的数据
// 注意这里假设解压后的数据已经是UTF-8编码
// 如果需要处理其他编码转UTF-8需要额外的转换步骤
return buf.Bytes(), nil
//reader, err := gzip.NewReader(bytes.NewReader(data))
//if err != nil {
// return nil, err
//}
//defer reader.Close()
//
//// 读取解压后的数据
//var buf bytes.Buffer
//_, err = io.Copy(&buf, reader)
//if err != nil {
// return nil, err
//}
//
//// 返回解压后的数据
//// 注意这里假设解压后的数据已经是UTF-8编码
//// 如果需要处理其他编码转UTF-8需要额外的转换步骤
//return buf.Bytes(), nil
}

26
go.mod
View File

@@ -10,6 +10,7 @@ require (
github.com/brianvoe/gofakeit/v6 v6.20.1
github.com/clbanning/mxj v1.8.4
github.com/coocood/freecache v1.2.2
//github.com/dgrr/http2 v0.3.5
github.com/dubbogo/gost v1.13.1
github.com/eolinker/eosc v0.20.3
github.com/fasthttp/websocket v1.5.0
@@ -20,7 +21,6 @@ require (
github.com/hashicorp/consul/api v1.9.1
github.com/influxdata/influxdb-client-go/v2 v2.12.1
github.com/jhump/protoreflect v1.16.0
github.com/joho/godotenv v1.3.0
github.com/lestrrat-go/jwx v1.2.28
github.com/nacos-group/nacos-sdk-go/v2 v2.2.3
github.com/nsqio/go-nsq v1.1.0
@@ -35,9 +35,9 @@ require (
github.com/stretchr/testify v1.9.0
github.com/traefik/yaegi v0.16.1
github.com/urfave/cli/v2 v2.23.4
github.com/valyala/fasthttp v1.47.0
golang.org/x/crypto v0.21.0
golang.org/x/net v0.22.0
github.com/valyala/fasthttp v1.59.0
golang.org/x/crypto v0.33.0
golang.org/x/net v0.35.0
golang.org/x/oauth2 v0.14.0
google.golang.org/api v0.149.0
google.golang.org/grpc v1.61.0
@@ -105,7 +105,7 @@ require (
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel/metric v1.20.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sync v0.11.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect
@@ -116,7 +116,7 @@ require (
require (
dubbo.apache.org/dubbo-go/v3 v3.0.2-0.20220519062747-f6405fa79d5c
github.com/andybalholm/brotli v1.0.5
github.com/andybalholm/brotli v1.1.1
github.com/apache/dubbo-go-hessian2 v1.11.6
github.com/armon/go-metrics v0.3.9 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@@ -131,7 +131,7 @@ require (
github.com/eapache/go-resiliency v1.2.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
github.com/eapache/queue v1.1.0 // indirect
github.com/fatih/color v1.9.0 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
@@ -155,13 +155,13 @@ require (
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/julienschmidt/httprouter v1.3.0 // indirect
github.com/klauspost/compress v1.16.3 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/kr/fs v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/mapstructure v1.5.0
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
@@ -173,7 +173,7 @@ require (
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/bytebufferpool v1.0.0
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
go.etcd.io/bbolt v1.3.9 // indirect
@@ -190,8 +190,8 @@ require (
go.uber.org/atomic v1.9.0
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.23.0
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0
golang.org/x/sys v0.30.0 // indirect
golang.org/x/text v0.22.0
golang.org/x/time v0.1.0 // indirect
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
gopkg.in/sourcemap.v1 v1.0.5 // indirect

View File

@@ -12,7 +12,6 @@ import (
"time"
"github.com/eolinker/eosc/eocontext"
"github.com/valyala/fasthttp"
)
@@ -148,6 +147,7 @@ func (c *Client) getHostClient(addr string, rewriteHost string) (*fasthttp.HostC
return false
},
}
//http2.ConfigureClient(hc, http2.ClientOpts{})
m[key] = hc
if len(m) == 1 {
go c.startCleaner(m)

View File

@@ -49,8 +49,11 @@ type HttpContext struct {
}
func (ctx *HttpContext) BodyFinish() {
for _, finishFunc := range ctx.bodyFinishes {
finishFunc(ctx)
bodyFinishes := ctx.bodyFinishes
size := len(bodyFinishes)
// ##倒序执行
for i := size - 1; i >= 0; i-- {
bodyFinishes[i](ctx)
}
}
@@ -219,10 +222,12 @@ func (ctx *HttpContext) SendTo(scheme string, node eoscContext.INode, timeout ti
// 流式传输
ctx.response.Response.SetStatusCode(response.StatusCode())
ctx.SetLabel("stream_running", "true")
ctx.response.Response.SetBodyStreamWriter(func(w *bufio.Writer) {
defer func() {
ctx.SetLabel("stream_running", "false")
ctx.FastFinish()
fasthttp.ReleaseResponse(response)
}()
reader := response.BodyStream()
buffer := make([]byte, 4096) // 4KB 缓冲区
@@ -262,11 +267,11 @@ func (ctx *HttpContext) SendTo(scheme string, node eoscContext.INode, timeout ti
ctx.proxyRequests = append(ctx.proxyRequests, agent)
return nil
}
response.CopyTo(ctx.response.Response)
agent.responseBody.Write(ctx.response.Response.Body())
agent.setResponseLength(ctx.response.Response.Header.ContentLength())
ctx.proxyRequests = append(ctx.proxyRequests, agent)
fasthttp.ReleaseResponse(response)
return ctx.response.responseError
}
@@ -383,15 +388,14 @@ func (ctx *HttpContext) RequestId() string {
// FastFinish finish
func (ctx *HttpContext) FastFinish() {
streamRunning := ctx.GetLabel("stream_running")
if streamRunning == "true" {
if ctx.GetLabel("stream_running") == "true" || ctx.GetLabel("current_running") == "true" {
// 暂时不释放
return
}
if ctx.response.responseError != nil {
ctx.fastHttpRequestCtx.SetStatusCode(504)
ctx.fastHttpRequestCtx.SetBodyString(ctx.response.responseError.Error())
return
}
ctx.port = 0

View File

@@ -77,7 +77,7 @@ func (r *Response) Finish() error {
r.Response = nil
r.responseError = nil
r.proxyStatusCode = 0
r.streamBody = nil
r.streamBody.Reset()
return nil
}
func (r *Response) reset(resp *fasthttp.Response) {
@@ -85,7 +85,10 @@ func (r *Response) reset(resp *fasthttp.Response) {
r.ResponseHeader.reset(&resp.Header)
r.responseError = nil
r.proxyStatusCode = 0
if r.streamBody == nil {
r.streamBody = &bytes.Buffer{}
}
r.streamBody.Reset()
}
func (r *Response) BodyLen() int {
@@ -93,15 +96,15 @@ func (r *Response) BodyLen() int {
}
func (r *Response) GetBody() []byte {
if r.IsBodyStream() {
return r.streamBody.Bytes()
}
if strings.Contains(r.GetHeader("Content-Encoding"), "gzip") {
body, _ := r.BodyGunzip()
r.DelHeader("Content-Encoding")
r.SetHeader("Content-Length", strconv.Itoa(len(body)))
r.Response.SetBody(body)
}
if r.IsBodyStream() {
return r.streamBody.Bytes()
}
return r.Response.Body()
}
@@ -115,6 +118,7 @@ func (r *Response) SetBody(bytes []byte) {
// 不处理
return
}
if strings.Contains(r.GetHeader("Content-Encoding"), "gzip") {
r.DelHeader("Content-Encoding")
}