github.com/volts-dev/volts@v0.0.0-20240120094013-5e9c65924106/router/http.go (about) 1 package router 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io" 9 "mime" 10 "net/http" 11 "net/url" 12 "os" 13 "path/filepath" 14 "reflect" 15 "strings" 16 "sync" 17 "time" 18 19 "github.com/volts-dev/template" 20 "github.com/volts-dev/utils" 21 "github.com/volts-dev/volts/internal/body" 22 "github.com/volts-dev/volts/logger" 23 "github.com/volts-dev/volts/transport" 24 //httpx "github.com/volts-dev/volts/server/listener/http" 25 ) 26 27 /* 28 Handler 负责处理控制器Request,Response的数据处理和管理 29 30 */ 31 32 var ( 33 HttpContext = "HttpContext" // 标识用于判断String() 34 HttpContextType = reflect.TypeOf(&THttpContext{}) // must be a pointer 35 cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-") 36 cookieValueSanitizer = strings.NewReplacer("\n", " ", "\r", " ", ";", " ") 37 38 // onExitFlushLoop is a callback set by tests to detect the state of the 39 // flushLoop() goroutine. 40 onExitFlushLoop func() 41 hopHeaders = []string{ 42 "Connection", 43 "Proxy-Connection", // non-standard but still sent by libcurl and rejected by e.g. google 44 "Keep-Alive", 45 "Proxy-Authenticate", 46 "Proxy-Authorization", 47 "Te", // canonicalized version of "TE" 48 "Trailer", // not Trailers per URL above; http://www.rfc-editor.org/errata_search.php?eid=4522 49 "Transfer-Encoding", 50 "Upgrade", 51 } 52 ) 53 54 // A BufferPool is an interface for getting and returning temporary 55 // byte slices for use by io.CopyBuffer. 56 type BufferPool interface { 57 Get() []byte 58 Put([]byte) 59 } 60 61 type writeFlusher interface { 62 io.Writer 63 http.Flusher 64 } 65 66 type maxLatencyWriter struct { 67 dst writeFlusher 68 latency time.Duration 69 70 mu sync.Mutex // protects Write + Flush 71 done chan bool 72 } 73 type ( 74 // THttpContext 负责所有请求任务,每个Handle表示有一个请求 75 THttpContext struct { 76 logger.ILogger 77 http.ResponseWriter 78 context context.Context 79 response *transport.THttpResponse //http.ResponseWriter 80 request *transport.THttpRequest // 81 router *TRouter 82 route route //执行本次Handle的Route 83 84 // data set 85 data *TParamsSet // 数据缓存在各个Controler间调用 86 methodParams *TParamsSet //map[string]string // Post Get 传递回来的参数 87 pathParams *TParamsSet //map[string]string // Url 传递回来的参数 88 //body *TContentBody 89 90 // 模板 91 TemplateVar 92 Template *template.TTemplateSet // 概念改进 为Hd添加 hd.Template.Params.Set("模板数据",Val)/Get()/Del() 93 TemplateSrc string // 模板名称 94 95 // 返回 96 ContentType string 97 Result []byte // -- 最终返回数据由Apply提交 98 handler *handler // -- 当前处理器 99 handlerIndex int // -- 提示目前处理器索引 100 isDone bool // -- 已经提交过 101 inited bool // -- 初始化固定值保存进POOL 102 val reflect.Value 103 typ reflect.Type 104 } 105 ) 106 107 func NewHttpContext(router *TRouter) *THttpContext { 108 hd := &THttpContext{ 109 ILogger: log, 110 router: router, 111 //Route: route, 112 //iResponseWriter: writer, 113 //Response: writer, 114 //Request: request, 115 //MethodParams: map[string]string{}, 116 //PathParams: map[string]string{}, 117 //Data: make(map[string]interface{}), 118 } // 这个handle将传递给 请求函数的头一个参数func test(hd *webgo.THttpContext) {} 119 120 // 必须不为nil 121 // hd.MethodParams=NewParamsSet(hd) 122 hd.pathParams = NewParamsSet(hd) 123 hd.data = NewParamsSet(hd) 124 hd.val = reflect.ValueOf(hd) 125 hd.typ = hd.val.Type() 126 return hd 127 } 128 129 func (self *THttpContext) Router() IRouter { 130 return self.router 131 } 132 133 func (self *THttpContext) Request() *transport.THttpRequest { 134 return self.request 135 } 136 137 func (self *THttpContext) Response() *transport.THttpResponse { 138 return self.response 139 } 140 141 func (self *THttpContext) IsDone() bool { 142 return self.isDone 143 } 144 145 // the reflect model of Value 146 func (self *THttpContext) ValueModel() reflect.Value { 147 return self.val 148 } 149 150 // the reflect model of Type 151 func (self *THttpContext) TypeModel() reflect.Type { 152 return self.typ 153 } 154 func (self *THttpContext) Next() { 155 self.handler.Invoke(self) 156 } 157 158 func (self *THttpContext) setHandler(h *handler) { 159 self.handler = h 160 } 161 162 // TODO 添加验证Request 防止多次解析 163 func (self *THttpContext) MethodParams(blank ...bool) *TParamsSet { 164 var useBlank bool 165 if len(blank) > 0 { 166 useBlank = blank[0] 167 } 168 169 if self.methodParams == nil { 170 self.methodParams = NewParamsSet(self) 171 172 if !useBlank && self.methodParams.Length() == 0 { 173 // # parse the data from GET method # 174 q := self.request.URL.Query() 175 for key := range q { 176 self.methodParams.SetByField(key, q.Get(key)) 177 } 178 179 // # parse the data from POST method # 180 var err error 181 ct := self.request.Header().Get("Content-Type") 182 ct, _, err = mime.ParseMediaType(ct) 183 if err != nil { 184 logger.Err(err) 185 return self.methodParams 186 } else { 187 if ct == "multipart/form-data" { 188 self.request.ParseMultipartForm(int64(self.router.Config().UploadBuf) << 20) // 32m 189 } else { 190 self.request.ParseForm() //#Go通过r.ParseForm之后,把用户POST和GET的数据全部放在了r.Form里面 191 } 192 193 for key := range self.request.Form { 194 //Debug("key2:", key) 195 self.methodParams.SetByField(key, self.request.FormValue(key)) 196 } 197 } 198 } 199 } 200 201 return self.methodParams 202 } 203 204 func (self *THttpContext) Body() *body.TBody { 205 return self.request.Body() 206 } 207 208 func (self *THttpContext) Write(data []byte) (int, error) { 209 return self.response.Write(data) 210 } 211 212 func (self *THttpContext) WriteStream(data interface{}) error { 213 self.isDone = true 214 return self.response.WriteStream(data) 215 } 216 217 // TODO 改为Route Params 218 // 如果返回 nil 代表 Url 不含改属性 219 func (self *THttpContext) PathParams() *TParamsSet { 220 return self.pathParams 221 } 222 223 func (self *THttpContext) String() string { 224 return HttpContext 225 } 226 227 // 值由Router 赋予 228 // func (self *THttpContext) setPathParams(name, val string) { 229 func (self *THttpContext) setPathParams(p Params) { 230 // init dy url parm to handler 231 if len(p) > 0 { 232 self.pathParams = NewParamsSet(self) 233 } 234 235 for _, param := range p { 236 self.pathParams.SetByField(param.Name, param.Value) 237 } 238 } 239 240 /* 241 func (self *THttpContext) UpdateSession() { 242 self.Router.Sessions.UpdateSession(self.COOKIE[self.Router.Sessions.CookieName], self.SESSION) 243 } 244 */ 245 246 /* 247 刷新 248 #刷新Handler的新请求数据 249 */ 250 // Inite and Connect a new ResponseWriter when a new request is coming 251 func (self *THttpContext) reset(rw *transport.THttpResponse, req *transport.THttpRequest) { 252 self.TemplateVar = *newTemplateVar() // 清空 253 self.data = nil // 清空 254 self.pathParams = nil 255 self.methodParams = nil 256 self.request = req 257 self.response = rw 258 self.ResponseWriter = rw 259 self.TemplateSrc = "" 260 self.ContentType = req.Header().Get("Content-Type") 261 self.Result = nil 262 self.handlerIndex = 0 // -- 提示目前控制器Index 263 self.isDone = false // -- 已经提交过 264 } 265 266 // TODO 修改API名称 设置response数据 267 func (self *THttpContext) setData(v interface{}) { 268 // self.Result = v.([]byte) 269 } 270 271 func (self *THttpContext) setControllerIndex(num int) { 272 self.handlerIndex = num 273 } 274 275 func (self *THttpContext) HandlerIndex() int { 276 return self.handlerIndex 277 } 278 279 func (self *THttpContext) Context() context.Context { 280 return self.context 281 } 282 283 // apply all changed to data 284 func (self *THttpContext) Apply() { 285 if !self.isDone { 286 if self.TemplateSrc != "" { 287 self.SetHeader(true, "Content-Type", self.ContentType) 288 if err := self.Template.RenderToWriter(self.TemplateSrc, self.templateVar, self.response); err != nil { 289 http.Error(self.response, "Apply fail:"+err.Error(), http.StatusInternalServerError) 290 } 291 } else if !self.response.Written() { // STEP:只许一次返回 292 self.Write(self.Result) 293 } 294 295 self.isDone = true 296 } 297 298 return 299 } 300 301 func (self *THttpContext) Route() route { 302 return self.route 303 } 304 305 func (self *THttpContext) Handler(index ...int) *handler { 306 idx := self.handlerIndex 307 if len(index) > 0 { 308 idx = index[0] 309 } 310 311 if idx == self.handlerIndex { 312 return self.handler 313 } 314 315 return self.route.handlers[idx] 316 } 317 318 // 数据缓存供传递用 319 func (self *THttpContext) Data() *TParamsSet { 320 if self.data == nil { 321 self.data = NewParamsSet(self) 322 } 323 324 return self.data 325 } 326 327 func (self *THttpContext) GetCookie(name, key string) (value string, err error) { 328 ck, err := self.request.Cookie(name) 329 if err != nil { 330 return "", err 331 } 332 333 return url.QueryUnescape(ck.Value) 334 } 335 336 func (self *THttpContext) IP() (res []string) { 337 ip := strings.Split(self.request.RemoteAddr, ":") 338 if len(ip) > 0 { 339 if ip[0] != "[" { 340 res = append(res, ip[0]) 341 } 342 } 343 344 proxy := make([]string, 0) 345 if ips := self.request.Header().Get("X-Forwarded-For"); ips != "" { 346 proxy = strings.Split(ips, ",") 347 } 348 if len(proxy) > 0 && proxy[0] != "" { 349 res = append(res, proxy[0]) 350 } 351 352 if len(res) == 0 { 353 res = append(res, "127.0.0.1") 354 } 355 return 356 } 357 358 // RemoteAddr returns more real IP address of visitor. 359 func (self *THttpContext) RemoteAddr() string { 360 addr := self.request.Header().Get("X-Real-IP") 361 if len(addr) == 0 { 362 addr = self.request.Header().Get("X-Forwarded-For") 363 if addr == "" { 364 addr = self.request.RemoteAddr 365 if i := strings.LastIndex(addr, ":"); i > -1 { 366 addr = addr[:i] 367 } 368 } 369 } 370 return addr 371 } 372 373 // SetCookie Sets the header entries associated with key to the single element value. It replaces any existing values associated with key. 374 // 一个cookie 有名称,内容,原始值,域,大小,过期时间,安全 375 // cookie[0] => name string 376 // cookie[1] => value string 377 // cookie[2] => expires string 378 // cookie[3] => path string 379 // cookie[4] => domain string 380 func (self *THttpContext) SetCookie(name string, value string, others ...interface{}) { 381 var b bytes.Buffer 382 fmt.Fprintf(&b, "%s=%s", sanitizeCookieName(name), sanitizeCookieValue(value)) 383 384 if len(others) > 0 { 385 switch others[0].(type) { 386 case int: 387 if others[0].(int) > 0 { 388 fmt.Fprintf(&b, "; Max-Age=%d", others[0].(int)) 389 } else if others[0].(int) < 0 { 390 fmt.Fprintf(&b, "; Max-Age=0") 391 } 392 case int64: 393 if others[0].(int64) > 0 { 394 fmt.Fprintf(&b, "; Max-Age=%d", others[0].(int64)) 395 } else if others[0].(int64) < 0 { 396 fmt.Fprintf(&b, "; Max-Age=0") 397 } 398 case int32: 399 if others[0].(int32) > 0 { 400 fmt.Fprintf(&b, "; Max-Age=%d", others[0].(int32)) 401 } else if others[0].(int32) < 0 { 402 fmt.Fprintf(&b, "; Max-Age=0") 403 } 404 } 405 } 406 if len(others) > 1 { 407 fmt.Fprintf(&b, "; Path=%s", sanitizeCookieValue(others[1].(string))) 408 } 409 if len(others) > 2 { 410 fmt.Fprintf(&b, "; Domain=%s", sanitizeCookieValue(others[2].(string))) 411 } 412 if len(others) > 3 { 413 if others[3].(bool) { 414 fmt.Fprintf(&b, "; Secure") 415 } 416 } 417 418 if len(others) > 4 { 419 if others[4].(bool) { 420 fmt.Fprintf(&b, "; HttpOnly") 421 } 422 } 423 self.response.Header().Add("Set-Cookie", b.String()) 424 /* 425 if aName == "" && aValue == "" { // 不能少于两个参数 426 return 427 } 428 var ( 429 //name string 430 //value string 431 expires int 432 path string 433 domain string 434 ) 435 if len(args) > 0 { 436 if v, ok := args[0].(int); ok { 437 expires = v 438 } 439 } 440 if len(args) > 1 { 441 if v, ok := args[1].(string); ok { 442 path = v 443 } 444 } 445 if len(args) > 2 { 446 if v, ok := args[2].(string); ok { 447 domain = v 448 } 449 } 450 451 lpCookie := &http.Cookie{ 452 Name: aName, 453 Value: url.QueryEscape(aValue), 454 Path: path, 455 Domain: domain, 456 } 457 458 if expires > 0 { //设置过期时间 459 d, _ := time.ParseDuration(strconv.Itoa(expires) + "s") 460 lpCookie.Expires = time.Now().Add(d) 461 } 462 if unique { 463 self.response.Header().Set("Set-Cookie", lpCookie.String()) // 等同http.SetCookie() 464 465 } else { 466 self.response.Header().Add("Set-Cookie", lpCookie.String()) // 等同http.SetCookie() 467 468 } 469 */ 470 /* 471 if expires > 0 { 472 p.COOKIE[pCookie.Name] = pCookie.Value 473 } else { 474 delete(p.COOKIE, pCookie.Name) 475 } 476 */ 477 } 478 479 // set the header of response 480 func (self *THttpContext) SetHeader(unique bool, hdr string, val string) { 481 if unique { 482 self.response.Header().Set(hdr, val) 483 } else { 484 self.response.Header().Add(hdr, val) 485 } 486 } 487 488 func (self *THttpContext) Abort(body string, code ...int) { 489 if len(code) > 0 { 490 self.response.WriteHeader(code[0]) 491 } else { 492 self.response.WriteHeader(http.StatusInternalServerError) 493 } 494 self.response.Write([]byte(body)) 495 self.isDone = true 496 } 497 498 func (self *THttpContext) Respond(content []byte) { 499 self.Result = content 500 } 501 502 func (self *THttpContext) RespondError(error string) { 503 self.Header().Set("Content-Type", "text/plain; charset=utf-8") 504 self.Header().Set("X-Content-Type-Options", "nosniff") 505 self.WriteHeader(http.StatusInternalServerError) 506 fmt.Fprintln(self.response, error) 507 508 // stop run next ctrl 509 self.isDone = true 510 } 511 512 func (self *THttpContext) NotModified() { 513 self.response.WriteHeader(304) 514 } 515 516 // NOTE default EscapeHTML=false 517 // Respond content by Json mode 518 func (self *THttpContext) RespondByJson(data interface{}) { 519 buf := bytes.NewBuffer([]byte{}) 520 js := json.NewEncoder(buf) 521 js.SetEscapeHTML(false) 522 if err := js.Encode(data); err != nil { 523 self.response.Write([]byte(err.Error())) 524 return 525 } 526 527 self.response.Header().Set("Content-Type", "application/json; charset=UTF-8") 528 self.Result = buf.Bytes() 529 } 530 531 // Ck 532 func (self *THttpContext) Redirect(urlStr string, status ...int) { 533 //http.Redirect(self, self.request, urlStr, code) 534 lStatusCode := http.StatusFound 535 if len(status) > 0 { 536 lStatusCode = status[0] 537 } 538 539 self.Header().Set("Location", urlStr) 540 self.WriteHeader(lStatusCode) 541 self.Result = []byte("Redirecting to: " + urlStr) 542 543 // stop run next ctrl 544 self.isDone = true 545 } 546 547 // return a download file redirection for client 548 func (self *THttpContext) Download(file_path string) error { 549 f, err := os.Open(file_path) 550 if err != nil { 551 return err 552 } 553 defer f.Close() 554 555 fName := filepath.Base(file_path) 556 self.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%v\"", fName)) 557 _, err = io.Copy(self.response, f) 558 return err 559 } 560 561 func (self *THttpContext) ServeFile(file_path string) { 562 http.ServeFile(self.response, self.request.Request, file_path) 563 } 564 565 // TODO 自定义文件夹 566 // render the template and return to the end 567 func (self *THttpContext) RenderTemplate(tmpl string, args interface{}) { 568 self.ContentType = "text/html; charset=utf-8" 569 if vars, ok := args.(map[string]interface{}); ok { 570 self.templateVar = utils.MergeMaps(self.router.templateVar, self.route.group.templateVar, vars) // 添加Router的全局变量到Templete 571 } else { 572 self.templateVar = utils.MergeMaps(self.router.templateVar, self.route.group.templateVar) // 添加Router的全局变量到Templete 573 } 574 575 if self.route.FilePath == "" { 576 self.TemplateSrc = filepath.Join(TEMPLATE_DIR, tmpl) 577 } else { 578 self.TemplateSrc = filepath.Join(self.route.FilePath, TEMPLATE_DIR, tmpl) 579 } 580 } 581 582 // remove the var from the template 583 func (self *THttpContext) DelTemplateVar(key string) { 584 delete(self.templateVar, key) 585 } 586 587 func (self *THttpContext) GetTemplateVar() map[string]interface{} { 588 return self.templateVar 589 } 590 591 // set the var of the template 592 func (self *THttpContext) SetTemplateVar(key string, value interface{}) { 593 self.templateVar[key] = value 594 } 595 596 // Responds with 404 Not Found 597 func (self *THttpContext) NotFound(message ...string) { 598 self.isDone = true 599 self.response.WriteHeader(http.StatusNotFound) 600 if len(message) > 0 { 601 self.response.Write([]byte(message[0])) 602 return 603 } 604 self.response.Write([]byte(http.StatusText(http.StatusNotFound))) 605 } 606 607 func singleJoiningSlash(a, b string) string { 608 aslash := strings.HasSuffix(a, "/") 609 bslash := strings.HasPrefix(b, "/") 610 switch { 611 case aslash && bslash: 612 return a + b[1:] 613 case !aslash && !bslash: 614 return a + "/" + b 615 } 616 return a + b 617 } 618 619 func sanitizeCookieName(n string) string { 620 return cookieNameSanitizer.Replace(n) 621 } 622 623 func sanitizeCookieValue(v string) string { 624 return cookieValueSanitizer.Replace(v) 625 //return sanitizeOrWarn("Cookie.Value", validCookieValueByte, v) 626 } 627 628 func sanitizeOrWarn(fieldName string, valid func(byte) bool, v string) string { 629 ok := true 630 for i := 0; i < len(v); i++ { 631 if valid(v[i]) { 632 continue 633 } 634 fmt.Printf("net/http: invalid byte %q in %s; dropping invalid bytes", v[i], fieldName) 635 ok = false 636 break 637 } 638 if ok { 639 return v 640 } 641 buf := make([]byte, 0, len(v)) 642 for i := 0; i < len(v); i++ { 643 if b := v[i]; valid(b) { 644 buf = append(buf, b) 645 } 646 } 647 return string(buf) 648 } 649 650 func validCookieValueByte(b byte) bool { 651 return 0x20 < b && b < 0x7f && b != '"' && b != ',' && b != ';' && b != '\\' 652 } 653 654 func (m *maxLatencyWriter) Write(p []byte) (int, error) { 655 m.mu.Lock() 656 defer m.mu.Unlock() 657 return m.dst.Write(p) 658 } 659 660 func (m *maxLatencyWriter) flushLoop() { 661 t := time.NewTicker(m.latency) 662 defer t.Stop() 663 for { 664 select { 665 case <-m.done: 666 if onExitFlushLoop != nil { 667 onExitFlushLoop() 668 } 669 return 670 case <-t.C: 671 m.mu.Lock() 672 m.dst.Flush() 673 m.mu.Unlock() 674 } 675 } 676 } 677 678 func (m *maxLatencyWriter) stop() { m.done <- true }