Files
apinto/node/http-context/context.go

286 lines
7.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package http_context
import (
"context"
"fmt"
"net"
"time"
http_entry "github.com/eolinker/apinto/entries/http-entry"
"github.com/eolinker/eosc"
"github.com/eolinker/apinto/entries/ctx_key"
"github.com/eolinker/eosc/utils/config"
fasthttp_client "github.com/eolinker/apinto/node/fasthttp-client"
eoscContext "github.com/eolinker/eosc/eocontext"
http_service "github.com/eolinker/eosc/eocontext/http-context"
"github.com/google/uuid"
"github.com/valyala/fasthttp"
)
var _ http_service.IHttpContext = (*HttpContext)(nil)
// HttpContext fasthttpRequestCtx
type HttpContext struct {
fastHttpRequestCtx *fasthttp.RequestCtx
proxyRequest ProxyRequest
proxyRequests []http_service.IProxy
requestID string
response Response
requestReader RequestReader
ctx context.Context
completeHandler eoscContext.CompleteHandler
finishHandler eoscContext.FinishHandler
balance eoscContext.BalanceHandler
upstreamHostHandler eoscContext.UpstreamHostHandler
labels map[string]string
port int
entry eosc.IEntry
}
func (ctx *HttpContext) RealIP() string {
return ctx.Request().RealIp()
}
func (ctx *HttpContext) GetUpstreamHostHandler() eoscContext.UpstreamHostHandler {
return ctx.upstreamHostHandler
}
func (ctx *HttpContext) SetUpstreamHostHandler(handler eoscContext.UpstreamHostHandler) {
ctx.upstreamHostHandler = handler
}
func (ctx *HttpContext) LocalIP() net.IP {
return ctx.fastHttpRequestCtx.LocalIP()
}
func (ctx *HttpContext) LocalAddr() net.Addr {
return ctx.fastHttpRequestCtx.LocalAddr()
}
func (ctx *HttpContext) LocalPort() int {
return ctx.port
}
func (ctx *HttpContext) GetBalance() eoscContext.BalanceHandler {
return ctx.balance
}
func (ctx *HttpContext) SetBalance(handler eoscContext.BalanceHandler) {
ctx.balance = handler
}
func (ctx *HttpContext) GetEntry() eosc.IEntry {
if ctx.entry == nil {
ctx.entry = http_entry.NewEntry(ctx)
}
return ctx.entry
}
func (ctx *HttpContext) SetLabel(name, value string) {
ctx.labels[name] = value
}
func (ctx *HttpContext) GetLabel(name string) string {
return ctx.labels[name]
}
func (ctx *HttpContext) Labels() map[string]string {
return ctx.labels
}
func (ctx *HttpContext) GetComplete() eoscContext.CompleteHandler {
return ctx.completeHandler
}
func (ctx *HttpContext) SetCompleteHandler(handler eoscContext.CompleteHandler) {
ctx.completeHandler = handler
}
func (ctx *HttpContext) GetFinish() eoscContext.FinishHandler {
return ctx.finishHandler
}
func (ctx *HttpContext) SetFinish(handler eoscContext.FinishHandler) {
ctx.finishHandler = handler
}
func (ctx *HttpContext) Scheme() string {
return string(ctx.fastHttpRequestCtx.Request.URI().Scheme())
}
func (ctx *HttpContext) Assert(i interface{}) error {
if v, ok := i.(*http_service.IHttpContext); ok {
*v = ctx
return nil
}
return fmt.Errorf("not suport:%s", config.TypeNameOf(i))
}
func (ctx *HttpContext) Proxies() []http_service.IProxy {
return ctx.proxyRequests
}
func (ctx *HttpContext) Response() http_service.IResponse {
return &ctx.response
}
func (ctx *HttpContext) SendTo(scheme string, node eoscContext.INode, timeout time.Duration) error {
host := node.Addr()
request := ctx.proxyRequest.Request()
passHost, targetHost := ctx.GetUpstreamHostHandler().PassHost()
switch passHost {
case eoscContext.PassHost:
case eoscContext.NodeHost:
request.URI().SetHost(host)
case eoscContext.ReWriteHost:
request.URI().SetHost(targetHost)
}
beginTime := time.Now()
ctx.response.responseError = fasthttp_client.ProxyTimeout(scheme, node, request, &ctx.fastHttpRequestCtx.Response, timeout)
agent := newRequestAgent(&ctx.proxyRequest, host, scheme, beginTime, time.Now())
if ctx.response.responseError != nil {
agent.setStatusCode(504)
} else {
ctx.response.ResponseHeader.refresh()
agent.setStatusCode(ctx.fastHttpRequestCtx.Response.StatusCode())
}
agent.responseBody = string(ctx.response.Response.Body())
agent.setResponseLength(ctx.fastHttpRequestCtx.Response.Header.ContentLength())
ctx.proxyRequests = append(ctx.proxyRequests, agent)
return ctx.response.responseError
}
func (ctx *HttpContext) Context() context.Context {
if ctx.ctx == nil {
ctx.ctx = context.Background()
}
return ctx.ctx
}
func (ctx *HttpContext) AcceptTime() time.Time {
return ctx.fastHttpRequestCtx.Time()
}
func (ctx *HttpContext) Value(key interface{}) interface{} {
return ctx.Context().Value(key)
}
func (ctx *HttpContext) WithValue(key, val interface{}) {
ctx.ctx = context.WithValue(ctx.Context(), key, val)
}
func (ctx *HttpContext) Proxy() http_service.IRequest {
return &ctx.proxyRequest
}
func (ctx *HttpContext) Request() http_service.IRequestReader {
return &ctx.requestReader
}
func (ctx *HttpContext) IsCloneable() bool {
return true
}
func (ctx *HttpContext) Clone() (eoscContext.EoContext, error) {
copyContext := copyPool.Get().(*cloneContext)
copyContext.org = ctx
copyContext.proxyRequests = make([]http_service.IProxy, 0, 2)
req := fasthttp.AcquireRequest()
// 当body未读取调用Body方法读出stream中当所有body内容避免请求体被截断
ctx.proxyRequest.req.Body()
ctx.proxyRequest.req.CopyTo(req)
resp := fasthttp.AcquireResponse()
//ctx.fastHttpRequestCtx.Response.CopyTo(resp)
copyContext.proxyRequest.reset(req, ctx.requestReader.remoteAddr)
copyContext.response.reset(resp)
copyContext.completeHandler = ctx.completeHandler
copyContext.finishHandler = ctx.finishHandler
cloneLabels := make(map[string]string, len(ctx.labels))
for k, v := range ctx.labels {
cloneLabels[k] = v
}
copyContext.labels = cloneLabels
//记录请求时间
copyContext.ctx = context.WithValue(ctx.Context(), http_service.KeyCloneCtx, true)
copyContext.WithValue(ctx_key.CtxKeyRetry, 0)
copyContext.WithValue(ctx_key.CtxKeyRetry, time.Duration(0))
return copyContext, nil
}
// NewContext 创建Context
func NewContext(ctx *fasthttp.RequestCtx, port int) *HttpContext {
remoteAddr := ctx.RemoteAddr().String()
httpContext := pool.Get().(*HttpContext)
httpContext.fastHttpRequestCtx = ctx
httpContext.requestID = uuid.New().String()
// 原始请求最大读取body为8k使用clone request
request := fasthttp.AcquireRequest()
ctx.Request.CopyTo(request)
httpContext.requestReader.reset(request, remoteAddr)
// proxyRequest保留原始请求
httpContext.proxyRequest.reset(&ctx.Request, remoteAddr)
httpContext.proxyRequests = httpContext.proxyRequests[:0]
httpContext.response.reset(&ctx.Response)
httpContext.labels = make(map[string]string)
httpContext.port = port
//记录请求时间
httpContext.ctx = context.Background()
httpContext.WithValue("request_time", ctx.Time())
return httpContext
}
// RequestId 请求ID
func (ctx *HttpContext) RequestId() string {
return ctx.requestID
}
// Finish finish
func (ctx *HttpContext) FastFinish() {
if ctx.response.responseError != nil {
ctx.fastHttpRequestCtx.SetStatusCode(504)
ctx.fastHttpRequestCtx.SetBodyString(ctx.response.responseError.Error())
return
}
ctx.port = 0
ctx.ctx = nil
ctx.balance = nil
ctx.upstreamHostHandler = nil
ctx.finishHandler = nil
ctx.completeHandler = nil
fasthttp.ReleaseRequest(ctx.requestReader.req)
ctx.requestReader.Finish()
ctx.proxyRequest.Finish()
ctx.response.Finish()
ctx.fastHttpRequestCtx = nil
pool.Put(ctx)
}