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 }