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 }