httpCtx 副本包

This commit is contained in:
chenjiekun
2023-03-02 15:31:42 +08:00
parent e9888ae533
commit 44678168a4
11 changed files with 1205 additions and 32 deletions

View File

@@ -3,6 +3,7 @@ package http_context
import ( import (
"context" "context"
"fmt" "fmt"
http_context_copy "github.com/eolinker/apinto/node/http-context/http-context-copy"
"net" "net"
"strings" "strings"
"time" "time"
@@ -189,41 +190,17 @@ func (ctx *HttpContext) Clone() (eoscContext.EoContext, error) {
return nil, fmt.Errorf("%s %w", "HttpContext", eoscContext.ErrEoCtxUnCloneable) return nil, fmt.Errorf("%s %w", "HttpContext", eoscContext.ErrEoCtxUnCloneable)
} }
cloneCtx := pool.Get().(*HttpContext) ctxCopy := http_context_copy.NewContextCopy(ctx.fastHttpRequestCtx, ctx.requestID, ctx.port, ctx.labels)
remoteAddr := ctx.fastHttpRequestCtx.RemoteAddr().String()
cloneReq := fasthttp.AcquireRequest() ctxCopy.SetCompleteHandler(ctx.completeHandler)
ctx.proxyRequest.Request().CopyTo(cloneReq) ctxCopy.SetFinish(ctx.finishHandler)
cloneResp := fasthttp.AcquireResponse() ctxCopy.SetUpstreamHostHandler(ctx.upstreamHostHandler)
ctx.response.Response.CopyTo(cloneResp) ctxCopy.SetApp(ctx.app)
ctxCopy.SetBalance(ctx.balance)
//cloneRequestCtx := new(fasthttp.RequestCtx) //Ctx set retry,timeout TODO
//cloneRequestCtx.Request = *cloneReq
//cloneRequestCtx.Response = *cloneResp
cloneCtx.fastHttpRequestCtx = ctx.fastHttpRequestCtx //TODO
cloneCtx.requestID = ctx.requestID //TODO return ctxCopy, nil
cloneCtx.requestReader.reset(cloneReq, remoteAddr)
cloneCtx.proxyRequest.reset(cloneReq, remoteAddr)
cloneCtx.proxyRequests = cloneCtx.proxyRequests[:0]
cloneCtx.response.reset(cloneResp)
cloneCtx.port = ctx.port
cloneCtx.ctx = ctx.ctx
cloneCtx.completeHandler = ctx.completeHandler
cloneCtx.finishHandler = ctx.finishHandler
cloneCtx.upstreamHostHandler = ctx.upstreamHostHandler
cloneCtx.app = ctx.app
cloneCtx.balance = ctx.balance
cLabels := make(map[string]string, len(ctx.labels))
for k, v := range ctx.labels {
cLabels[k] = v
}
cloneCtx.labels = cLabels
return cloneCtx, nil
} }
// NewContext 创建Context // NewContext 创建Context

View File

@@ -0,0 +1,293 @@
package http_context_copy
import (
"bytes"
"strings"
http_context "github.com/eolinker/eosc/eocontext/http-context"
"github.com/valyala/fasthttp"
"io/ioutil"
"mime"
"mime/multipart"
"net/url"
)
const defaultMultipartMemory = 32 << 20 // 32 MB
var (
_ http_context.IBodyDataWriter = (*BodyRequestHandler)(nil)
)
const (
MultipartForm = "multipart/form-data"
FormData = "application/x-www-form-urlencoded"
TEXT = "text/plain"
JSON = "application/json"
JavaScript = "application/javascript"
AppLicationXML = "application/xml"
TextXML = "text/xml"
Html = "text/html"
)
// BodyRequestHandler body请求处理器
type BodyRequestHandler struct {
request *fasthttp.Request
formdata *multipart.Form
}
func (b *BodyRequestHandler) MultipartForm() (*multipart.Form, error) {
if b.formdata != nil {
return b.formdata, nil
}
if !strings.Contains(b.ContentType(), MultipartForm) {
return nil, ErrorNotMultipart
}
form, err := b.request.MultipartForm()
if err != nil {
return nil, err
}
b.formdata = &multipart.Form{
Value: form.Value,
File: form.File,
}
b.resetFile()
return form, nil
}
func (b *BodyRequestHandler) Files() (map[string][]*multipart.FileHeader, error) {
form, err := b.MultipartForm()
if err != nil {
return nil, err
}
return form.File, nil
}
func (b *BodyRequestHandler) reset(request *fasthttp.Request) {
b.request = request
b.formdata = nil
}
func NewBodyRequestHandler(request *fasthttp.Request) *BodyRequestHandler {
return &BodyRequestHandler{request: request}
}
// GetForm 获取表单参数
func (b *BodyRequestHandler) GetForm(key string) string {
contentType, _, _ := mime.ParseMediaType(b.ContentType())
switch contentType {
case FormData:
args := b.request.PostArgs()
if args == nil {
return ""
}
return string(args.Peek(key))
case MultipartForm:
form, err := b.MultipartForm()
if err != nil {
return ""
}
vs := form.Value[key]
if len(vs) > 0 {
return vs[0]
}
return ""
}
return ""
}
// ContentType 获取contentType
func (b *BodyRequestHandler) ContentType() string {
return string(b.request.Header.ContentType())
}
// BodyForm 获取表单参数
func (b *BodyRequestHandler) BodyForm() (url.Values, error) {
contentType, _, _ := mime.ParseMediaType(string(b.request.Header.ContentType()))
switch contentType {
case FormData:
return url.ParseQuery(string(b.request.Body()))
case MultipartForm:
multipartForm, err := b.MultipartForm()
if err != nil {
return nil, err
}
return multipartForm.Value, nil
default:
return nil, ErrorNotForm
}
}
// RawBody 获取raw数据
func (b *BodyRequestHandler) RawBody() ([]byte, error) {
return b.request.Body(), nil
}
func (b *BodyRequestHandler) GetFile(key string) ([]*multipart.FileHeader, bool) {
multipartForm, err := b.MultipartForm()
if err != nil {
return nil, false
}
fl, has := multipartForm.File[key]
return fl, has
}
func (b *BodyRequestHandler) SetToForm(key, value string) error {
contentType, _, _ := mime.ParseMediaType(string(b.request.Header.ContentType()))
switch contentType {
case FormData:
b.request.PostArgs().Set(key, value)
b.request.SetBodyRaw(b.request.PostArgs().QueryString())
return nil
case MultipartForm:
multipartForm, err := b.MultipartForm()
if err != nil {
return err
}
multipartForm.Value[key] = []string{value}
return b.resetFile()
default:
return ErrorNotForm
}
}
// AddForm 新增表单参数
func (b *BodyRequestHandler) AddForm(key, value string) error {
contentType, _, _ := mime.ParseMediaType(string(b.request.Header.ContentType()))
switch contentType {
case FormData:
b.request.PostArgs().Add(key, value)
b.request.SetBody(b.request.PostArgs().QueryString())
return nil
case MultipartForm:
multipartForm, err := b.MultipartForm()
if err != nil {
return err
}
multipartForm.Value[key] = append(multipartForm.Value[key], value)
return b.resetFile()
default:
return ErrorNotForm
}
}
// AddFile 新增文件参数
func (b *BodyRequestHandler) AddFile(key string, file *multipart.FileHeader) error {
contentType, _, _ := mime.ParseMediaType(b.ContentType())
if contentType != FormData && contentType != MultipartForm {
return ErrorNotMultipart
}
multipartForm, err := b.MultipartForm()
if err != nil {
return err
}
multipartForm.File[key] = append(multipartForm.File[key], file)
return b.resetFile()
}
// SetFile 设置文件参数
func (b *BodyRequestHandler) SetFile(files map[string][]*multipart.FileHeader) error {
multipartForm, err := b.MultipartForm()
if err != nil {
return err
}
multipartForm.File = files
return b.resetFile()
}
func (b *BodyRequestHandler) resetFile() error {
multipartForm := b.formdata
if multipartForm == nil {
return nil
}
body := new(bytes.Buffer)
writer := multipart.NewWriter(body)
for name, fs := range multipartForm.File {
for _, f := range fs {
fio, err := f.Open()
if err != nil {
return err
}
part, err := writer.CreateFormFile(name, f.Filename)
if err != nil {
fio.Close()
return err
}
data, err := ioutil.ReadAll(fio)
if err != nil {
fio.Close()
return err
}
_, err = part.Write(data)
if err != nil {
fio.Close()
return err
}
fio.Close()
}
}
for key, values := range multipartForm.Value {
//temp := make(url.Values)
//temp[key] = values
//value := temp.Encode()
for _, value := range values {
err := writer.WriteField(key, value)
if err != nil {
return err
}
}
}
err := writer.Close()
if err != nil {
return err
}
b.request.Header.SetContentType(writer.FormDataContentType())
b.request.SetBodyRaw(body.Bytes())
return nil
}
// SetForm 设置表单参数
func (b *BodyRequestHandler) SetForm(values url.Values) error {
contentType, _, _ := mime.ParseMediaType(b.ContentType())
if contentType != FormData && contentType != MultipartForm {
return ErrorNotForm
}
switch contentType {
case FormData:
b.request.SetBodyString(values.Encode())
case MultipartForm:
multipartForm, err := b.MultipartForm()
if err != nil {
return err
}
multipartForm.Value = values
return b.resetFile()
}
return ErrorNotForm
}
// SetRaw 设置raw数据
func (b *BodyRequestHandler) SetRaw(contentType string, body []byte) {
b.request.SetBodyRaw(body)
b.request.Header.SetContentType(contentType)
return
}

View File

@@ -0,0 +1,266 @@
package http_context_copy
import (
"context"
"fmt"
"net"
"strings"
"time"
"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/valyala/fasthttp"
)
var _ http_service.IHttpContext = (*HttpContextCopy)(nil)
// HttpContextCopy fasthttpRequestCtx
type HttpContextCopy struct {
proxyRequest ProxyRequest
proxyRequests []http_service.IProxy
requestID string
response Response
requestReader RequestReader
ctx context.Context
completeHandler eoscContext.CompleteHandler
finishHandler eoscContext.FinishHandler
app eoscContext.EoApp
balance eoscContext.BalanceHandler
upstreamHostHandler eoscContext.UpstreamHostHandler
labels map[string]string
port int
localIP net.IP
netAddr net.Addr
acceptTime time.Time
}
func (ctx *HttpContextCopy) GetUpstreamHostHandler() eoscContext.UpstreamHostHandler {
return ctx.upstreamHostHandler
}
func (ctx *HttpContextCopy) SetUpstreamHostHandler(handler eoscContext.UpstreamHostHandler) {
ctx.upstreamHostHandler = handler
}
func (ctx *HttpContextCopy) LocalIP() net.IP {
return ctx.localIP
}
func (ctx *HttpContextCopy) LocalAddr() net.Addr {
return ctx.netAddr
}
func (ctx *HttpContextCopy) LocalPort() int {
return ctx.port
}
func (ctx *HttpContextCopy) GetApp() eoscContext.EoApp {
return ctx.app
}
func (ctx *HttpContextCopy) SetApp(app eoscContext.EoApp) {
ctx.app = app
}
func (ctx *HttpContextCopy) GetBalance() eoscContext.BalanceHandler {
return ctx.balance
}
func (ctx *HttpContextCopy) SetBalance(handler eoscContext.BalanceHandler) {
ctx.balance = handler
}
func (ctx *HttpContextCopy) SetLabel(name, value string) {
ctx.labels[name] = value
}
func (ctx *HttpContextCopy) GetLabel(name string) string {
return ctx.labels[name]
}
func (ctx *HttpContextCopy) Labels() map[string]string {
return ctx.labels
}
func (ctx *HttpContextCopy) GetComplete() eoscContext.CompleteHandler {
return ctx.completeHandler
}
func (ctx *HttpContextCopy) SetCompleteHandler(handler eoscContext.CompleteHandler) {
ctx.completeHandler = handler
}
func (ctx *HttpContextCopy) GetFinish() eoscContext.FinishHandler {
return ctx.finishHandler
}
func (ctx *HttpContextCopy) SetFinish(handler eoscContext.FinishHandler) {
ctx.finishHandler = handler
}
func (ctx *HttpContextCopy) Scheme() string {
return string(ctx.requestReader.req.URI().Scheme())
}
func (ctx *HttpContextCopy) 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 *HttpContextCopy) Proxies() []http_service.IProxy {
return ctx.proxyRequests
}
func (ctx *HttpContextCopy) Response() http_service.IResponse {
return &ctx.response
}
func (ctx *HttpContextCopy) SendTo(address string, timeout time.Duration) error {
scheme, host := readAddress(address)
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(address, request, ctx.response.Response, timeout)
agent := newRequestAgent(&ctx.proxyRequest, host, scheme, beginTime, time.Now())
if ctx.response.responseError != nil {
agent.setStatusCode(504)
} else {
agent.setStatusCode(ctx.response.Response.StatusCode())
}
agent.setResponseLength(ctx.response.Response.Header.ContentLength())
ctx.proxyRequests = append(ctx.proxyRequests, agent)
return ctx.response.responseError
}
func (ctx *HttpContextCopy) Context() context.Context {
if ctx.ctx == nil {
ctx.ctx = context.Background()
}
return ctx.ctx
}
func (ctx *HttpContextCopy) AcceptTime() time.Time {
return ctx.acceptTime
}
func (ctx *HttpContextCopy) Value(key interface{}) interface{} {
return ctx.Context().Value(key)
}
func (ctx *HttpContextCopy) WithValue(key, val interface{}) {
ctx.ctx = context.WithValue(ctx.Context(), key, val)
}
func (ctx *HttpContextCopy) Proxy() http_service.IRequest {
return &ctx.proxyRequest
}
func (ctx *HttpContextCopy) Request() http_service.IRequestReader {
return &ctx.requestReader
}
func (ctx *HttpContextCopy) IsCloneable() bool {
return false
}
func (ctx *HttpContextCopy) Clone() (eoscContext.EoContext, error) {
return nil, fmt.Errorf("%s %w", "HttpContextCopy", eoscContext.ErrEoCtxUnCloneable)
}
// NewContextCopy 创建Context-Copy
func NewContextCopy(requestCtx *fasthttp.RequestCtx, requestID string, port int, labels map[string]string) *HttpContextCopy {
ctxCopy := pool.Get().(*HttpContextCopy)
remoteAddr := requestCtx.RemoteAddr().String()
cloneReq := fasthttp.AcquireRequest()
requestCtx.Request.CopyTo(cloneReq)
cloneResp := fasthttp.AcquireResponse()
requestCtx.Response.CopyTo(cloneResp)
ctxCopy.requestReader.reset(cloneReq, remoteAddr)
ctxCopy.proxyRequest.reset(cloneReq, remoteAddr)
ctxCopy.proxyRequests = ctxCopy.proxyRequests[:0]
ctxCopy.response.reset(cloneResp)
ctxCopy.localIP = requestCtx.LocalIP()
ctxCopy.netAddr = requestCtx.LocalAddr()
ctxCopy.acceptTime = requestCtx.Time()
ctxCopy.requestID = requestID
ctxCopy.port = port
ctxCopy.ctx = context.Background()
ctxCopy.WithValue("request_time", ctxCopy.acceptTime)
cLabels := make(map[string]string, len(labels))
for k, v := range labels {
cLabels[k] = v
}
ctxCopy.labels = cLabels
return ctxCopy
}
// RequestId 请求ID
func (ctx *HttpContextCopy) RequestId() string {
return ctx.requestID
}
// FastFinish finish
func (ctx *HttpContextCopy) FastFinish() {
if ctx.response.responseError != nil {
ctx.response.Response.SetStatusCode(504)
ctx.response.Response.SetBodyString(ctx.response.responseError.Error())
return
}
ctx.port = 0
ctx.ctx = nil
ctx.app = nil
ctx.balance = nil
ctx.upstreamHostHandler = nil
ctx.finishHandler = nil
ctx.completeHandler = nil
ctx.requestReader.Finish()
ctx.proxyRequest.Finish()
ctx.response.Finish()
pool.Put(ctx)
return
}
func NotFound(ctx *HttpContextCopy) {
ctx.response.Response.SetStatusCode(404)
ctx.response.Response.SetBody([]byte("404 Not Found"))
}
func readAddress(addr string) (scheme, host string) {
if i := strings.Index(addr, "://"); i > 0 {
return strings.ToLower(addr[:i]), addr[i+3:]
}
return "http", addr
}

View File

@@ -0,0 +1,10 @@
package http_context_copy
import "errors"
var (
ErrorNotForm = errors.New("contentType is not Form")
ErrorNotMultipart = errors.New("contentType is not Multipart")
ErrorNotAllowRaw = errors.New("contentType is not allow Raw")
ErrorNotSend = errors.New("not send")
)

View File

@@ -0,0 +1,140 @@
package http_context_copy
import (
"bytes"
"net/http"
"strings"
http_service "github.com/eolinker/eosc/eocontext/http-context"
"github.com/valyala/fasthttp"
)
var _ http_service.IHeaderWriter = (*RequestHeader)(nil)
type RequestHeader struct {
header *fasthttp.RequestHeader
tmp http.Header
}
func (h *RequestHeader) RawHeader() string {
return h.header.String()
}
func (h *RequestHeader) reset(header *fasthttp.RequestHeader) {
h.header = header
h.tmp = nil
}
func (h *RequestHeader) initHeader() {
if h.tmp == nil {
h.tmp = make(http.Header)
h.header.VisitAll(func(key, value []byte) {
bytes.SplitN(value, []byte(":"), 2)
h.tmp[string(key)] = []string{string(value)}
})
}
}
func (h *RequestHeader) Host() string {
return string(h.header.Host())
}
func (h *RequestHeader) GetHeader(name string) string {
return h.Headers().Get(name)
}
func (h *RequestHeader) Headers() http.Header {
h.initHeader()
return h.tmp
}
func (h *RequestHeader) SetHeader(key, value string) {
if h.tmp != nil {
h.tmp.Set(key, value)
}
h.header.Set(key, value)
}
func (h *RequestHeader) AddHeader(key, value string) {
if h.tmp != nil {
h.tmp.Add(key, value)
}
h.header.Add(key, value)
}
func (h *RequestHeader) DelHeader(key string) {
if h.tmp != nil {
h.tmp.Del(key)
}
h.header.Del(key)
}
func (h *RequestHeader) SetHost(host string) {
if h.tmp != nil {
h.tmp.Set("Host", host)
}
h.header.SetHost(host)
}
type ResponseHeader struct {
header *fasthttp.ResponseHeader
tmp http.Header
}
func (r *ResponseHeader) reset(header *fasthttp.ResponseHeader) {
r.header = header
r.tmp = nil
}
func NewResponseHeader(header *fasthttp.ResponseHeader) *ResponseHeader {
return &ResponseHeader{header: header}
}
func (r *ResponseHeader) GetHeader(name string) string {
return r.Headers().Get(name)
}
func (r *ResponseHeader) Headers() http.Header {
if r.tmp == nil {
r.tmp = make(http.Header)
hs := strings.Split(r.header.String(), "\r\n")
for _, t := range hs {
vs := strings.Split(t, ":")
if len(vs) < 2 {
if vs[0] == "" {
continue
}
r.tmp[vs[0]] = []string{""}
continue
}
r.tmp[vs[0]] = []string{strings.TrimSpace(vs[1])}
}
}
return r.tmp
}
func (r *ResponseHeader) SetHeader(key, value string) {
if r.tmp != nil {
r.tmp.Set(key, value)
}
r.header.Set(key, value)
}
func (r *ResponseHeader) AddHeader(key, value string) {
if r.tmp != nil {
r.tmp.Add(key, value)
}
r.header.Add(key, value)
}
func (r *ResponseHeader) DelHeader(key string) {
if r.tmp != nil {
r.tmp.Del(key)
}
r.header.Del(key)
}
func (h *RequestHeader) GetCookie(key string) string {
return string(h.header.Cookie(key))
}

View File

@@ -0,0 +1,19 @@
package http_context_copy
import (
"sync"
http_service "github.com/eolinker/eosc/eocontext/http-context"
)
var (
pool = sync.Pool{
New: newContext,
}
)
func newContext() interface{} {
h := new(HttpContextCopy)
h.proxyRequests = make([]http_service.IProxy, 0, 5)
return h
}

View File

@@ -0,0 +1,89 @@
package http_context_copy
import (
"strconv"
"time"
http_service "github.com/eolinker/eosc/eocontext/http-context"
)
var _ http_service.IProxy = (*requestAgent)(nil)
type requestAgent struct {
http_service.IRequest
host string
scheme string
statusCode int
status string
responseLength int
beginTime time.Time
endTime time.Time
hostAgent *UrlAgent
}
func (a *requestAgent) ProxyTime() time.Time {
return a.beginTime
}
func (a *requestAgent) StatusCode() int {
return a.statusCode
}
func (a *requestAgent) Status() string {
return a.status
}
func (a *requestAgent) setStatusCode(code int) {
a.statusCode = code
a.status = strconv.Itoa(code)
}
func (a *requestAgent) ResponseLength() int {
return a.responseLength
}
func (a *requestAgent) setResponseLength(length int) {
if length > 0 {
a.responseLength = length
}
}
func newRequestAgent(IRequest http_service.IRequest, host string, scheme string, beginTime, endTime time.Time) *requestAgent {
return &requestAgent{IRequest: IRequest, host: host, scheme: scheme, beginTime: beginTime, endTime: endTime}
}
func (a *requestAgent) ResponseTime() int64 {
return a.endTime.Sub(a.beginTime).Milliseconds()
}
func (a *requestAgent) URI() http_service.IURIWriter {
if a.hostAgent == nil {
a.hostAgent = NewUrlAgent(a.IRequest.URI(), a.host, a.scheme)
}
return a.hostAgent
}
type UrlAgent struct {
http_service.IURIWriter
host string
scheme string
}
func (u *UrlAgent) SetScheme(scheme string) {
u.scheme = scheme
}
func (u *UrlAgent) Scheme() string {
return u.scheme
}
func (u *UrlAgent) Host() string {
return u.host
}
func (u *UrlAgent) SetHost(host string) {
u.host = host
}
func NewUrlAgent(IURIWriter http_service.IURIWriter, host string, scheme string) *UrlAgent {
return &UrlAgent{IURIWriter: IURIWriter, host: host, scheme: scheme}
}

View File

@@ -0,0 +1,72 @@
package http_context_copy
import (
"bytes"
"fmt"
http_service "github.com/eolinker/eosc/eocontext/http-context"
"github.com/valyala/fasthttp"
)
var _ http_service.IRequest = (*ProxyRequest)(nil)
type ProxyRequest struct {
RequestReader
}
//func (r *ProxyRequest) clone() *ProxyRequest {
// return NewProxyRequest(r.Request(), r.remoteAddr)
//}
func (r *ProxyRequest) Finish() error {
fasthttp.ReleaseRequest(r.req)
r.RequestReader.Finish()
return nil
}
func (r *ProxyRequest) Header() http_service.IHeaderWriter {
return &r.headers
}
func (r *ProxyRequest) Body() http_service.IBodyDataWriter {
return &r.body
}
func (r *ProxyRequest) URI() http_service.IURIWriter {
return &r.uri
}
var (
xforwardedforKey = []byte("x-forwarded-for")
)
func (r *ProxyRequest) reset(request *fasthttp.Request, remoteAddr string) {
proxyRequest := fasthttp.AcquireRequest()
request.CopyTo(proxyRequest)
forwardedFor := proxyRequest.Header.PeekBytes(xforwardedforKey)
if len(forwardedFor) > 0 {
if i := bytes.IndexByte(forwardedFor, ','); i > 0 {
r.realIP = string(forwardedFor[:i])
} else {
r.realIP = string(forwardedFor)
}
proxyRequest.Header.Set("x-forwarded-for", fmt.Sprint(string(forwardedFor), ",", r.remoteAddr))
} else {
proxyRequest.Header.Set("x-forwarded-for", r.remoteAddr)
r.realIP = r.remoteAddr
}
r.RequestReader.reset(proxyRequest, remoteAddr)
}
//func NewProxyRequest(request *fasthttp.Request, remoteAddr string) *ProxyRequest {
// proxyRequest := fasthttp.AcquireRequest()
// request.CopyTo(proxyRequest)
// return &ProxyRequest{
// RequestReader: NewRequestReader(proxyRequest, remoteAddr),
// }
//}
func (r *ProxyRequest) SetMethod(s string) {
r.Request().Header.SetMethod(s)
}

View File

@@ -0,0 +1,104 @@
package http_context_copy
import (
"strings"
http_service "github.com/eolinker/eosc/eocontext/http-context"
"github.com/valyala/fasthttp"
)
var _ http_service.IRequestReader = (*RequestReader)(nil)
type RequestReader struct {
body BodyRequestHandler
req *fasthttp.Request
headers RequestHeader
uri URIRequest
remoteAddr string
remotePort string
realIP string
length int
}
func (r *RequestReader) ContentLength() int {
return r.length
}
func (r *RequestReader) ContentType() string {
return string(r.req.Header.ContentType())
}
func (r *RequestReader) String() string {
return r.req.String()
}
func (r *RequestReader) Method() string {
return string(r.req.Header.Method())
}
func (r *RequestReader) Header() http_service.IHeaderReader {
return &r.headers
}
func (r *RequestReader) Body() http_service.IBodyDataReader {
return &r.body
}
func (r *RequestReader) URI() http_service.IURIReader {
return &r.uri
}
func (r *RequestReader) ReadIP() string {
if r.realIP == "" {
realIP := r.headers.GetHeader("x-real-ip")
if realIP == "" {
realIP = r.remoteAddr
}
r.realIP = realIP
}
return r.realIP
}
func (r *RequestReader) ForwardIP() string {
return r.headers.GetHeader("x-forwarded-for")
}
func (r *RequestReader) RemoteAddr() string {
return r.remoteAddr
}
func (r *RequestReader) RemotePort() string {
return r.remotePort
}
func (r *RequestReader) Finish() error {
r.req = nil
r.body.reset(nil)
r.headers.reset(nil)
r.uri.reset(nil)
return nil
}
func (r *RequestReader) reset(req *fasthttp.Request, remoteAddr string) {
r.req = req
r.remoteAddr = remoteAddr
r.body.reset(req)
r.headers.reset(&req.Header)
r.uri.uri = req.URI()
idx := strings.LastIndex(remoteAddr, ":")
if idx != -1 {
r.remoteAddr = remoteAddr[:idx]
r.remotePort = remoteAddr[idx+1:]
}
length := r.req.Header.ContentLength()
if length > 0 {
r.length = length
}
}
func (r *RequestReader) Request() *fasthttp.Request {
return r.req
}

View File

@@ -0,0 +1,121 @@
package http_context_copy
import (
"strconv"
"strings"
"time"
http_service "github.com/eolinker/eosc/eocontext/http-context"
"github.com/valyala/fasthttp"
)
var _ http_service.IResponse = (*Response)(nil)
type Response struct {
ResponseHeader
*fasthttp.Response
length int
responseTime time.Duration
proxyStatusCode int
responseError error
}
func (r *Response) ContentLength() int {
if r.length == 0 {
return r.Response.Header.ContentLength()
}
return r.length
}
func (r *Response) ContentType() string {
return string(r.Response.Header.ContentType())
}
func (r *Response) HeadersString() string {
return r.header.String()
}
func (r *Response) ResponseError() error {
return r.responseError
}
func (r *Response) ClearError() {
r.responseError = nil
}
func (r *Response) Finish() error {
r.Response = nil
r.ResponseHeader.reset(nil)
r.responseError = nil
r.proxyStatusCode = 0
return nil
}
func (r *Response) reset(resp *fasthttp.Response) {
r.Response = resp
r.ResponseHeader.reset(&resp.Header)
r.responseError = nil
r.proxyStatusCode = 0
}
func (r *Response) BodyLen() int {
return r.header.ContentLength()
}
func (r *Response) GetBody() []byte {
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)
}
return r.Response.Body()
}
func (r *Response) SetBody(bytes []byte) {
if strings.Contains(r.GetHeader("Content-Encoding"), "gzip") {
r.DelHeader("Content-Encoding")
}
r.Response.SetBody(bytes)
r.length = len(bytes)
r.SetHeader("Content-Length", strconv.Itoa(r.length))
r.responseError = nil
}
func (r *Response) StatusCode() int {
if r.responseError != nil {
return 504
}
return r.Response.StatusCode()
}
func (r *Response) Status() string {
return strconv.Itoa(r.StatusCode())
}
func (r *Response) SetStatus(code int, status string) {
r.Response.SetStatusCode(code)
r.responseError = nil
}
// 原始的响应状态码
func (r *Response) ProxyStatusCode() int {
return r.proxyStatusCode
}
func (r *Response) ProxyStatus() string {
return strconv.Itoa(r.proxyStatusCode)
}
func (r *Response) SetProxyStatus(code int, status string) {
r.proxyStatusCode = code
}
func (r *Response) SetResponseTime(t time.Duration) {
r.responseTime = t
}
func (r *Response) ResponseTime() time.Duration {
return r.responseTime
}

View File

@@ -0,0 +1,82 @@
package http_context_copy
import (
http_service "github.com/eolinker/eosc/eocontext/http-context"
"github.com/valyala/fasthttp"
)
var _ http_service.IURIWriter = (*URIRequest)(nil)
type URIRequest struct {
uri *fasthttp.URI
}
func (ur *URIRequest) reset(uri *fasthttp.URI) {
ur.uri = uri
}
func (ur *URIRequest) Path() string {
return string(ur.uri.Path())
}
func (ur *URIRequest) SetScheme(scheme string) {
ur.uri.SetScheme(scheme)
}
func (ur *URIRequest) Host() string {
return string(ur.uri.Host())
}
func (ur *URIRequest) SetQuery(key, value string) {
ur.uri.QueryArgs().Set(key, value)
}
func (ur *URIRequest) AddQuery(key, value string) {
ur.uri.QueryArgs().Add(key, value)
}
func (ur *URIRequest) DelQuery(key string) {
queryArgs := ur.uri.QueryArgs()
queryArgs.Del(key)
if queryArgs.Len() == 0 {
ur.uri.SetQueryStringBytes(nil)
}
}
func (ur *URIRequest) SetRawQuery(raw string) {
ur.uri.SetQueryString(raw)
}
func NewURIRequest(uri *fasthttp.URI) *URIRequest {
return &URIRequest{uri: uri}
}
func (ur *URIRequest) RequestURI() string {
return string(ur.uri.RequestURI())
}
func (ur *URIRequest) Scheme() string {
return string(ur.uri.Scheme())
}
func (ur *URIRequest) RawURL() string {
return string(ur.uri.FullURI())
}
func (ur *URIRequest) GetQuery(key string) string {
return string(ur.uri.QueryArgs().Peek(key))
}
func (ur *URIRequest) RawQuery() string {
return string(ur.uri.QueryString())
}
func (ur *URIRequest) SetPath(s string) {
ur.uri.SetPath(s)
}
func (ur *URIRequest) SetHost(host string) {
ur.uri.SetHost(host)
}