Skip to content

Instantly share code, notes, and snippets.

@ad3n
Created April 4, 2025 13:21
Show Gist options
  • Save ad3n/bc04d465ba5b0970936ae7d44e03e254 to your computer and use it in GitHub Desktop.
Save ad3n/bc04d465ba5b0970936ae7d44e03e254 to your computer and use it in GitHub Desktop.
package requests
import (
"io"
"lawang"
"lawang/bytes"
"lawang/configs"
"lawang/jsons"
"lawang/utils"
"net/http"
"net/url"
"path/filepath"
"strings"
"github.com/gofiber/fiber/v3"
core "github.com/valyala/fasthttp"
)
type Context struct {
cacheKey string
requestID string
clientIp string
params map[string]string
queries map[string]string
headers http.Header
files map[string]*File
credential *lawang.Client
endpoint *configs.Endpoint
route *fiber.Route
fasthttpRequest *core.Request
request *Request
response *Response
timeout int
}
var contextPool = lawang.NewPool(func() *Context {
return &Context{}
})
func (c *Context) CacheKey() string { return c.cacheKey }
func (c *Context) Credential() *lawang.Client { return c.credential }
func (c *Context) ClientIp() string { return c.clientIp }
func (c *Context) Endpoint() *configs.Endpoint { return c.endpoint }
func (c *Context) Files() map[string]*File { return c.files }
func (c *Context) Params() map[string]string { return c.params }
func (c *Context) Headers() http.Header { return c.headers }
func (c *Context) Queries() map[string]string { return c.queries }
func (c *Context) FastHttpRequest() *core.Request { return c.fasthttpRequest }
func (c *Context) RequestID() string { return c.requestID }
func (c *Context) Request() *Request { return c.request }
func (c *Context) Response() *Response { return c.response }
func (c *Context) Route() *fiber.Route { return c.route }
func (c *Context) SetCacheKey(cacheKey string) { c.cacheKey = cacheKey }
func (c *Context) SetCredential(credential *lawang.Client) {
if c.credential != nil {
lawang.ClientPool.Put(c.credential)
}
c.credential = credential
}
func (c *Context) SetRequest(request *Request) {
if c.request != nil {
RequestPool.Put(c.request)
}
c.request = request
}
func (c *Context) SetResponse(response *Response) {
if c.response != nil {
ResponsePool.Put(c.response)
}
c.response = response
}
func (c *Context) Reset() {
c.cacheKey, c.requestID, c.clientIp = "", "", ""
if c.params != nil {
lawang.MapStringPool.Put(c.params)
}
if c.queries != nil {
lawang.MapStringPool.Put(c.queries)
}
if c.files != nil {
for _, f := range c.files {
FilePool.Put(f)
}
clear(c.files)
}
c.credential, c.endpoint, c.route, c.fasthttpRequest = nil, nil, nil, nil
if c.request != nil {
RequestPool.Put(c.request)
}
if c.response != nil {
ResponsePool.Put(c.response)
}
c.timeout = 0
clear(c.headers)
}
func ReleaseContext(ctx *Context) {
contextPool.Put(ctx)
}
func AcquireContext(cache *lawang.Cache, ctx fiber.Ctx, endpoint *configs.Endpoint) *Context {
context := contextPool.Get()
context.requestID = RequestID(ctx)
context.endpoint = endpoint
context.params = extractParams(ctx)
context.headers = extractHeaders(ctx)
context.queries = extractQueries(ctx)
context.files = parseFormData(ctx, endpoint)
context.route = ctx.Route()
context.fasthttpRequest = ctx.Request()
context.response = ResponsePool.Get()
context.clientIp = utils.ClientIp(ctx)
context.timeout = resolveTimeout(endpoint)
request := RequestPool.Get()
parse(context, request)
request.RawBody = parseBody(ctx)
context.request = request
return context
}
func parse(ctx *Context, request *Request) {
request.Protocol = bytes.ByteToString(ctx.FastHttpRequest().URI().Scheme())
request.ClientIp = ctx.ClientIp()
request.Path = ctx.Route().Path
request.Uri = bytes.ByteToString(ctx.FastHttpRequest().RequestURI())
request.Method = bytes.ByteToString(ctx.FastHttpRequest().Header.Method())
request.Params = ctx.Params()
request.Headers = ctx.Headers()
request.Queries = ctx.Queries()
request.RawQuery = bytes.ByteToString(ctx.FastHttpRequest().URI().QueryArgs().QueryString())
jsonParams, _ := jsons.Marshal(request.Params)
jsonQuery, _ := jsons.Marshal(request.Queries)
jsonHeaders, _ := jsons.Marshal(request.Headers)
request.ParamBytes = jsonParams
request.QueryBytes = jsonQuery
request.HeaderBytes = jsonHeaders
request.Files = ctx.Files()
}
func extractHeaders(ctx fiber.Ctx) http.Header {
headers := make(http.Header)
ctx.Request().Header.VisitAll(func(k, v []byte) {
headers.Set(bytes.ByteToString(k), bytes.ByteToString(v))
})
return headers
}
func extractQueries(ctx fiber.Ctx) map[string]string {
queries := lawang.MapStringPool.Get()
ctx.Request().URI().QueryArgs().VisitAll(func(k, v []byte) {
queries[bytes.ByteToString(k)] = bytes.ByteToString(v)
})
return queries
}
func extractParams(ctx fiber.Ctx) map[string]string {
params := lawang.MapStringPool.Get()
ctx.Bind().URI(params)
return params
}
func resolveTimeout(endpoint *configs.Endpoint) int {
if endpoint.RequestTimeout > 0 {
return int(endpoint.RequestTimeout)
}
return configs.Config.RequestTimeout
}
func parseBody(ctx fiber.Ctx) []byte {
if ctx.Method() != fiber.MethodPost && ctx.Method() != fiber.MethodPut && ctx.Method() != fiber.MethodPatch {
return nil
}
bodyMap := jsons.NewCachableMap[string]()
var bodyBytes []byte
switch bytes.ByteToString(ctx.Request().Header.ContentType()) {
case configs.LAWANG_CONTENT_TYPE_URL_ENCODED:
urlEncoded, err := url.ParseQuery(bytes.ByteToString(ctx.Body()))
if err != nil {
return bodyBytes
}
for k, v := range urlEncoded {
bodyMap.Set(k, v[0])
}
bodyBytes, _ = jsons.Marshal(bodyMap.Data())
return bodyBytes
case configs.LAWANG_CONTENT_TYPE_JSON:
return ctx.Body()
default:
body := bodyMap.Data()
err := ctx.Bind().Body(&body)
if err != nil {
return ctx.Body()
}
bodyBytes, _ = jsons.Marshal(body)
return bodyBytes
}
}
func parseFormData(ctx fiber.Ctx, endpoint *configs.Endpoint) map[string]*File {
if ctx.Method() != fiber.MethodPost && ctx.Method() != fiber.MethodPut && ctx.Method() != fiber.MethodPatch {
return nil
}
if !strings.HasPrefix(bytes.ByteToString(ctx.Request().Header.Peek(configs.LAWANG_CONTENT_TYPE_HEADER)), configs.LAWANG_CONTENT_TYPE_FORM_DATA) {
return nil
}
if endpoint.Backend.Type != configs.LAWANG_JAVASCRIPT_SERVICE && endpoint.Backend.Type != configs.LAWANG_HTTP_SERVICE {
return nil
}
multipartForm, err := ctx.MultipartForm()
if err != nil {
return nil
}
files := make(map[string]*File, len(multipartForm.File))
buffer := bytes.BufferPool.Get()
defer bytes.BufferPool.Put(buffer)
for k, v := range multipartForm.File {
f, err := v[0].Open()
if err != nil {
continue
}
buffer.Reset()
if _, err = io.Copy(buffer, f); err == nil {
file := FilePool.Get()
file.Name = v[0].Filename
file.Ext = filepath.Ext(file.Name)
file.Content = buffer.Bytes()
files[k] = file
}
f.Close()
}
return files
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment