github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/http/http.go (about)

     1  /*
     2   * Copyright 2021 CloudWeGo Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package http
    18  
    19  import (
    20  	"bytes"
    21  	"io"
    22  	"io/ioutil"
    23  	"net/http"
    24  	"net/url"
    25  	"sync"
    26  
    27  	"github.com/bytedance/sonic/ast"
    28  	"github.com/cloudwego/dynamicgo/internal/rt"
    29  )
    30  
    31  // Endpoint a http endpoint.
    32  type Endpoint struct {
    33  	Method, Path string
    34  }
    35  
    36  var annoMethoMap = map[string]string{
    37  	"api.get":     http.MethodGet,
    38  	"api.post":    http.MethodPost,
    39  	"api.put":     http.MethodPut,
    40  	"api.delete":  http.MethodDelete,
    41  	"api.patch":   http.MethodPatch,
    42  	"api.head":    http.MethodHead,
    43  	"api.options": http.MethodOptions,
    44  	"api.connect": http.MethodConnect,
    45  	"api.trace":   http.MethodTrace,
    46  }
    47  
    48  // AnnoToMethod maps annotation to corresponding http method
    49  func AnnoToMethod(annoKey string) string {
    50  	return annoMethoMap[annoKey]
    51  }
    52  
    53  // Param in url path
    54  //
    55  // e.g. /user/:id + /user/123 => Param{Key: "id", Value: "123"}
    56  type Param struct {
    57  	Key   string
    58  	Value string
    59  }
    60  
    61  // RequestGetter is a interface for getting request parameters
    62  type RequestGetter interface {
    63  	// GetMethod returns the http method.
    64  	GetMethod() string
    65  	// GetHost returns the host.
    66  	GetHost() string
    67  	// GetUri returns entire uri.
    68  	GetUri() string
    69  	// Header returns the value of the header with the given key.
    70  	GetHeader(string) string
    71  	// Cookie returns the value of the cookie with the given key.
    72  	GetCookie(string) string
    73  	// Query returns the value of the query with the given key.
    74  	GetQuery(string) string
    75  	// Param returns the value of the url-path param with the given key.
    76  	GetParam(string) string
    77  	// PostForm returns the value of the post-form body with the given key.
    78  	GetPostForm(string) string
    79  	// MapBody returns the value of body with the given key.
    80  	GetMapBody(string) string
    81  	// Body returns the raw body in bytes.
    82  	GetBody() []byte
    83  }
    84  
    85  // Request is a implementation of RequestGetter.
    86  // It wraps http.Request.
    87  type HTTPRequest struct {
    88  	*http.Request
    89  	rawBody []byte
    90  	Params  Params
    91  	BodyMap interface{}
    92  }
    93  
    94  // NewHTTPRequest creates a new HTTPRequest.
    95  func NewHTTPRequest() *HTTPRequest {
    96  	return &HTTPRequest{}
    97  }
    98  
    99  // NewHTTPRequestFromUrl creates a new HTTPRequest from url, body and url-path param.
   100  func NewHTTPRequestFromUrl(method, url string, body io.Reader, params ...Param) (*HTTPRequest, error) {
   101  	req, err := http.NewRequest(method, url, body)
   102  	if err != nil {
   103  		return nil, err
   104  	}
   105  	ret := &HTTPRequest{
   106  		Request: req,
   107  	}
   108  	for _, p := range params {
   109  		ret.Params.Set(p.Key, p.Value)
   110  	}
   111  	return ret, nil
   112  }
   113  
   114  var (
   115  	// DefaultJsonPairSize is the default size of json.Pair slice.
   116  	DefaultJsonPairSize = 16
   117  )
   118  
   119  var jsonPairsPool = sync.Pool{
   120  	New: func() interface{} {
   121  		ret := make([]ast.Pair, DefaultJsonPairSize)
   122  		return &ret
   123  	},
   124  }
   125  
   126  type jsonCache struct {
   127  	root ast.Node
   128  	m   map[string]string
   129  }
   130  
   131  // NewHTTPRequestFromStdReq creates a new HTTPRequest from http.Request.
   132  // It will check the content-type of the request and parse the body if the type one of following:
   133  //   - application/json
   134  //   - application/x-www-form-urlencoded
   135  func NewHTTPRequestFromStdReq(req *http.Request, params ...Param) (ret *HTTPRequest, err error) {
   136  	ret = &HTTPRequest{}
   137  	ret.Request = req
   138  	for _, p := range params {
   139  		ret.Params.Set(p.Key, p.Value)
   140  	}
   141  
   142  	switch req.Header.Get(HeaderContentType) {
   143  	case "application/json":
   144  		{
   145  			body, err := ioutil.ReadAll(req.Body)
   146  			if err != nil {
   147  				return nil, err
   148  			}
   149  			ret.rawBody = body
   150  			if len(body) == 0 {
   151  				return ret, nil
   152  			}
   153  			node := ast.NewRaw(rt.Mem2Str(body))
   154  			cache := &jsonCache{
   155  				root: node,
   156  				m:    make(map[string]string),
   157  			}
   158  			ret.BodyMap = cache
   159  			// parser := json.NewParser(rt.Mem2Str(body))
   160  			// parser.Set(true, false)
   161  			// vs := jsonPairsPool.New().(*[]json.Pair)
   162  			// if err := parser.DecodeObject(vs); err != nil {
   163  			// 	return nil, err
   164  			// }
   165  			// // TODO: reuse map memory?
   166  			// ret.BodyMap = make(map[string]string, len(*vs))
   167  			// for _, kv := range *vs {
   168  			// 	js, _ := kv.Value.Raw()
   169  			// 	ret.BodyMap[kv.Key] = js
   170  			// }
   171  			// (*vs) = (*vs)[:0]
   172  			// jsonPairsPool.Put(vs)
   173  		}
   174  	case "application/x-www-form-urlencoded":
   175  		if err := req.ParseForm(); err != nil {
   176  			return nil, err
   177  		}
   178  		ret.BodyMap = req.PostForm
   179  	}
   180  	return ret, nil
   181  }
   182  
   183  // Header implements RequestGetter.Header.
   184  func (self HTTPRequest) GetHeader(key string) string {
   185  	return self.Request.Header.Get(key)
   186  }
   187  
   188  // Cookie implements RequestGetter.Cookie.
   189  func (self HTTPRequest) GetCookie(key string) string {
   190  	if c, err := self.Request.Cookie(key); err == nil {
   191  		return c.Value
   192  	}
   193  	return ""
   194  }
   195  
   196  // Query implements RequestGetter.Query.
   197  func (self HTTPRequest) GetQuery(key string) string {
   198  	return self.Request.URL.Query().Get(key)
   199  }
   200  
   201  // Body implements RequestGetter.Body.
   202  func (self HTTPRequest) GetBody() []byte {
   203  	if self.rawBody != nil {
   204  		return self.rawBody
   205  	}
   206  	buf, err := ioutil.ReadAll(self.Request.Body)
   207  	if err != nil {
   208  		return nil
   209  	}
   210  	return buf
   211  }
   212  
   213  // Method implements RequestGetter.Method.
   214  func (self HTTPRequest) GetMethod() string {
   215  	return self.Request.Method
   216  }
   217  
   218  // Path implements RequestGetter.Path.
   219  func (self HTTPRequest) GetPath() string {
   220  	return self.Request.URL.Path
   221  }
   222  
   223  // Host implements RequestGetter.Host.
   224  func (self HTTPRequest) GetHost() string {
   225  	return self.Request.URL.Host
   226  }
   227  
   228  // Param implements RequestGetter.Param.
   229  func (self HTTPRequest) GetParam(key string) string {
   230  	return self.Params.ByName(key)
   231  }
   232  
   233  // MapBody implements RequestGetter.MapBody.
   234  func (self *HTTPRequest) GetMapBody(key string) string {
   235  	if self.BodyMap == nil && self.Request != nil {
   236  		v, err := NewHTTPRequestFromStdReq(self.Request)
   237  		if err != nil || v.BodyMap == nil {
   238  			return ""
   239  		}
   240  		self.BodyMap = v.BodyMap
   241  	}
   242  	switch t := self.BodyMap.(type) {
   243  	case *jsonCache:
   244  		// fast path
   245  		if v, ok := t.m[key]; ok {
   246  			return v
   247  		}
   248  		// slow path
   249  		v := t.root.Get(key)
   250  		if v.Check() != nil {
   251  			return ""
   252  		}
   253  		j, e := v.Raw()
   254  		if e != nil {
   255  			return ""
   256  		}
   257  		if v.Type() == ast.V_STRING {
   258  			j, e = v.String()
   259  			if e != nil {
   260  				return ""
   261  			}
   262  		}
   263  		t.m[key] = j
   264  		return j
   265  	case map[string]string:
   266  		return t[key]
   267  	case url.Values:
   268  		return t.Get(key)
   269  	default:
   270  		return ""
   271  	}
   272  }
   273  
   274  // PostForm implements RequestGetter.PostForm.
   275  func (self HTTPRequest) GetPostForm(key string) string {
   276  	return self.Request.PostFormValue(key)
   277  }
   278  
   279  // Uri implements RequestGetter.Uri.
   280  func (self HTTPRequest) GetUri() string {
   281  	return self.Request.URL.String()
   282  }
   283  
   284  // ResponseSetter is a interface for setting response parameters
   285  type ResponseSetter interface {
   286  	// SetStatusCode sets the status code of the response
   287  	SetStatusCode(int) error
   288  	// SetHeader sets the header of the response
   289  	SetHeader(string, string) error
   290  	// SetCookie sets the cookie of the response
   291  	SetCookie(string, string) error
   292  	// SetRawBody sets the raw body of the response
   293  	SetRawBody([]byte) error
   294  }
   295  
   296  // HTTPResponse is an implementation of ResponseSetter
   297  type HTTPResponse struct {
   298  	*http.Response
   299  }
   300  
   301  // NewHTTPResponse creates a new HTTPResponse
   302  func NewHTTPResponse() *HTTPResponse {
   303  	return &HTTPResponse{
   304  		Response: &http.Response{
   305  			Header: make(http.Header),
   306  		},
   307  	}
   308  }
   309  
   310  // SetStatusCode implements ResponseSetter.SetStatusCode
   311  func (self HTTPResponse) SetStatusCode(code int) error {
   312  	self.Response.StatusCode = code
   313  	return nil
   314  }
   315  
   316  // SetHeader implements ResponseSetter.SetHeader
   317  func (self HTTPResponse) SetHeader(key string, val string) error {
   318  	self.Response.Header.Set(key, val)
   319  	return nil
   320  }
   321  
   322  // SetCookie implements ResponseSetter.SetCookie
   323  func (self HTTPResponse) SetCookie(key string, val string) error {
   324  	c := &http.Cookie{Name: key, Value: val}
   325  	self.Response.Header.Add(HeaderSetCookie, c.String())
   326  	return nil
   327  }
   328  
   329  type wrapBody struct {
   330  	io.Reader
   331  }
   332  
   333  func (wrapBody) Close() error { return nil }
   334  
   335  func (self HTTPResponse) SetRawBody(body []byte) error {
   336  	self.Response.Body = wrapBody{bytes.NewReader(body)}
   337  	return nil
   338  }
   339  
   340  const (
   341  	// HeaderContentType is the key of Content-Type header
   342  	HeaderContentType = "Content-Type"
   343  	// HeaderSetCookie is the key of Set-Cookie header
   344  	HeaderSetCookie = "Set-Cookie"
   345  )
   346  
   347  // Http url-path params
   348  type Params struct {
   349  	params   []Param
   350  	recycle  func(*Params)
   351  	recycled bool
   352  }
   353  
   354  // Recycle the Params
   355  func (ps *Params) Recycle() {
   356  	if ps.recycled {
   357  		return
   358  	}
   359  	ps.recycled = true
   360  	ps.recycle(ps)
   361  }
   362  
   363  // ByName search Param by given name
   364  func (ps *Params) ByName(name string) string {
   365  	for _, p := range ps.params {
   366  		if p.Key == name {
   367  			return p.Value
   368  		}
   369  	}
   370  	return ""
   371  }
   372  
   373  // Set set Param by given name and value, return true if Param exists
   374  func (ps *Params) Set(name string, val string) bool {
   375  	for i, p := range ps.params {
   376  		if p.Key == name {
   377  			ps.params[i].Value = val
   378  			return true
   379  		}
   380  	}
   381  	ps.params = append(ps.params, Param{Key: name, Value: val})
   382  	return false
   383  }