mirror of
https://github.com/eolinker/apinto
synced 2025-09-27 05:06:11 +08:00
146 lines
3.2 KiB
Go
146 lines
3.2 KiB
Go
package http_complete
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/eolinker/apinto/entries/ctx_key"
|
|
"github.com/eolinker/apinto/entries/router"
|
|
|
|
"github.com/eolinker/eosc/eocontext"
|
|
http_service "github.com/eolinker/eosc/eocontext/http-context"
|
|
"github.com/eolinker/eosc/log"
|
|
)
|
|
|
|
var (
|
|
ErrorTimeoutComplete = errors.New("complete timeout")
|
|
)
|
|
|
|
type HttpComplete struct {
|
|
}
|
|
|
|
func NewHttpComplete() *HttpComplete {
|
|
return &HttpComplete{}
|
|
}
|
|
|
|
func (h *HttpComplete) Complete(org eocontext.EoContext) error {
|
|
|
|
ctx, err := http_service.Assert(org)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//设置响应开始时间
|
|
proxyTime := time.Now()
|
|
balance := ctx.GetBalance()
|
|
if balance == nil {
|
|
return fmt.Errorf("balance not found")
|
|
}
|
|
defer func() {
|
|
//设置原始响应状态码
|
|
ctx.Response().SetProxyStatus(ctx.Response().StatusCode(), "")
|
|
ctx.Response().SetResponseTime(time.Since(proxyTime))
|
|
ctx.SetLabel("handler", "proxy")
|
|
}()
|
|
|
|
scheme := balance.Scheme()
|
|
|
|
switch strings.ToLower(scheme) {
|
|
case "", "tcp":
|
|
scheme = "http"
|
|
case "tsl", "ssl", "https":
|
|
scheme = "https"
|
|
|
|
}
|
|
|
|
retryValue := ctx.Value(ctx_key.CtxKeyRetry)
|
|
retry, ok := retryValue.(int)
|
|
if !ok {
|
|
retry = router.DefaultRetry
|
|
}
|
|
|
|
timeoutValue := ctx.Value(ctx_key.CtxKeyTimeout)
|
|
timeout, ok := timeoutValue.(time.Duration)
|
|
if !ok {
|
|
timeout = router.DefaultTimeout
|
|
}
|
|
balanceTimeout := balance.TimeOut()
|
|
if balanceTimeout == 0 {
|
|
balanceTimeout = timeout
|
|
}
|
|
var lastErr error
|
|
for index := 0; index <= retry; index++ {
|
|
|
|
if timeout > 0 && time.Since(proxyTime) > timeout {
|
|
return ErrorTimeoutComplete
|
|
}
|
|
node, _, err := balance.Select(ctx)
|
|
if err != nil {
|
|
log.Error("select error: ", err)
|
|
ctx.Response().SetStatus(501, "501")
|
|
ctx.Response().SetBody([]byte(err.Error()))
|
|
return err
|
|
}
|
|
lastErr = ctx.SendTo(scheme, node, balanceTimeout)
|
|
if lastErr == nil {
|
|
|
|
return nil
|
|
}
|
|
log.Error("http upstream send error: ", lastErr)
|
|
}
|
|
|
|
return lastErr
|
|
}
|
|
|
|
type NoServiceCompleteHandler struct {
|
|
status int
|
|
header map[string]string
|
|
body string
|
|
}
|
|
|
|
func NewNoServiceCompleteHandler(status int, header map[string]string, body string) *NoServiceCompleteHandler {
|
|
return &NoServiceCompleteHandler{status: status, header: header, body: body}
|
|
}
|
|
|
|
func (n *NoServiceCompleteHandler) Complete(org eocontext.EoContext) error {
|
|
ctx, err := http_service.Assert(org)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
//设置响应开始时间
|
|
proxyTime := time.Now()
|
|
|
|
defer func() {
|
|
//设置原始响应状态码
|
|
ctx.Response().SetProxyStatus(ctx.Response().StatusCode(), "")
|
|
//设置上游响应总时间, 单位为毫秒
|
|
//ctx.WithValue("response_time", time.Now().Sub(proxyTime).Milliseconds())
|
|
ctx.Response().SetResponseTime(time.Since(proxyTime))
|
|
ctx.SetLabel("handler", "proxy")
|
|
}()
|
|
for key, value := range n.header {
|
|
ctx.Response().SetHeader(key, value)
|
|
}
|
|
ctx.Response().SetBody([]byte(n.body))
|
|
ctx.Response().SetStatus(n.status, strconv.Itoa(n.status))
|
|
return nil
|
|
}
|
|
|
|
type httpCompleteCaller struct {
|
|
}
|
|
|
|
func NewHttpCompleteCaller() *httpCompleteCaller {
|
|
return &httpCompleteCaller{}
|
|
}
|
|
|
|
func (h *httpCompleteCaller) DoFilter(ctx eocontext.EoContext, next eocontext.IChain) (err error) {
|
|
return ctx.GetComplete().Complete(ctx)
|
|
}
|
|
|
|
func (h *httpCompleteCaller) Destroy() {
|
|
|
|
}
|