From 9c2ad91230d72fe6d661450cc78300ea223ae2bc Mon Sep 17 00:00:00 2001 From: Vidhu Kant Sharma Date: Mon, 19 Oct 2020 11:05:36 +0530 Subject: make it better --- vendor/github.com/gin-gonic/gin/context.go | 477 +++++++++++++++++++++++++++++ 1 file changed, 477 insertions(+) create mode 100644 vendor/github.com/gin-gonic/gin/context.go (limited to 'vendor/github.com/gin-gonic/gin/context.go') diff --git a/vendor/github.com/gin-gonic/gin/context.go b/vendor/github.com/gin-gonic/gin/context.go new file mode 100644 index 0000000..12920fd --- /dev/null +++ b/vendor/github.com/gin-gonic/gin/context.go @@ -0,0 +1,477 @@ +// Copyright 2014 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package gin + +import ( + "errors" + "io" + "math" + "net/http" + "strings" + "time" + + "github.com/gin-gonic/gin/binding" + "github.com/gin-gonic/gin/render" + "github.com/manucorporat/sse" + "golang.org/x/net/context" +) + +const ( + MIMEJSON = binding.MIMEJSON + MIMEHTML = binding.MIMEHTML + MIMEXML = binding.MIMEXML + MIMEXML2 = binding.MIMEXML2 + MIMEPlain = binding.MIMEPlain + MIMEPOSTForm = binding.MIMEPOSTForm + MIMEMultipartPOSTForm = binding.MIMEMultipartPOSTForm +) + +const AbortIndex int8 = math.MaxInt8 / 2 + +// Context is the most important part of gin. It allows us to pass variables between middleware, +// manage the flow, validate the JSON of a request and render a JSON response for example. +type Context struct { + writermem responseWriter + Request *http.Request + Writer ResponseWriter + + Params Params + handlers HandlersChain + index int8 + + engine *Engine + Keys map[string]interface{} + Errors errorMsgs + Accepted []string +} + +var _ context.Context = &Context{} + +/************************************/ +/********** CONTEXT CREATION ********/ +/************************************/ + +func (c *Context) reset() { + c.Writer = &c.writermem + c.Params = c.Params[0:0] + c.handlers = nil + c.index = -1 + c.Keys = nil + c.Errors = c.Errors[0:0] + c.Accepted = nil +} + +func (c *Context) Copy() *Context { + var cp Context = *c + cp.writermem.ResponseWriter = nil + cp.Writer = &cp.writermem + cp.index = AbortIndex + cp.handlers = nil + return &cp +} + +func (c *Context) HandlerName() string { + return nameOfFunction(c.handlers.Last()) +} + +/************************************/ +/*********** FLOW CONTROL ***********/ +/************************************/ + +// Next should be used only in the middlewares. +// It executes the pending handlers in the chain inside the calling handler. +// See example in github. +func (c *Context) Next() { + c.index++ + s := int8(len(c.handlers)) + for ; c.index < s; c.index++ { + c.handlers[c.index](c) + } +} + +// Returns if the currect context was aborted. +func (c *Context) IsAborted() bool { + return c.index == AbortIndex +} + +// Stops the system to continue calling the pending handlers in the chain. +// Let's say you have an authorization middleware that validates if the request is authorized +// if the authorization fails (the password does not match). This method (Abort()) should be called +// in order to stop the execution of the actual handler. +func (c *Context) Abort() { + c.index = AbortIndex +} + +// It calls Abort() and writes the headers with the specified status code. +// For example, a failed attempt to authentificate a request could use: context.AbortWithStatus(401). +func (c *Context) AbortWithStatus(code int) { + c.Writer.WriteHeader(code) + c.Abort() +} + +// It calls AbortWithStatus() and Error() internally. This method stops the chain, writes the status code and +// pushes the specified error to `c.Errors`. +// See Context.Error() for more details. +func (c *Context) AbortWithError(code int, err error) *Error { + c.AbortWithStatus(code) + return c.Error(err) +} + +/************************************/ +/********* ERROR MANAGEMENT *********/ +/************************************/ + +// Attaches an error to the current context. The error is pushed to a list of errors. +// It's a good idea to call Error for each error that occurred during the resolution of a request. +// A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response. +func (c *Context) Error(err error) *Error { + var parsedError *Error + switch err.(type) { + case *Error: + parsedError = err.(*Error) + default: + parsedError = &Error{ + Err: err, + Type: ErrorTypePrivate, + } + } + c.Errors = append(c.Errors, parsedError) + return parsedError +} + +/************************************/ +/******** METADATA MANAGEMENT********/ +/************************************/ + +// Sets a new pair key/value just for this context. +// It also lazy initializes the hashmap if it was not used previously. +func (c *Context) Set(key string, value interface{}) { + if c.Keys == nil { + c.Keys = make(map[string]interface{}) + } + c.Keys[key] = value +} + +// Returns the value for the given key, ie: (value, true). +// If the value does not exists it returns (nil, false) +func (c *Context) Get(key string) (value interface{}, exists bool) { + if c.Keys != nil { + value, exists = c.Keys[key] + } + return +} + +// Returns the value for the given key if it exists, otherwise it panics. +func (c *Context) MustGet(key string) interface{} { + if value, exists := c.Get(key); exists { + return value + } + panic("Key \"" + key + "\" does not exist") +} + +/************************************/ +/************ INPUT DATA ************/ +/************************************/ + +// Shortcut for c.Request.URL.Query().Get(key) +func (c *Context) Query(key string) (va string) { + va, _ = c.query(key) + return +} + +// Shortcut for c.Request.PostFormValue(key) +func (c *Context) PostForm(key string) (va string) { + va, _ = c.postForm(key) + return +} + +// Shortcut for c.Params.ByName(key) +func (c *Context) Param(key string) string { + return c.Params.ByName(key) +} + +func (c *Context) DefaultPostForm(key, defaultValue string) string { + if va, ok := c.postForm(key); ok { + return va + } + return defaultValue +} + +func (c *Context) DefaultQuery(key, defaultValue string) string { + if va, ok := c.query(key); ok { + return va + } + return defaultValue +} + +func (c *Context) query(key string) (string, bool) { + req := c.Request + if values, ok := req.URL.Query()[key]; ok && len(values) > 0 { + return values[0], true + } + return "", false +} + +func (c *Context) postForm(key string) (string, bool) { + req := c.Request + req.ParseMultipartForm(32 << 20) // 32 MB + if values := req.PostForm[key]; len(values) > 0 { + return values[0], true + } + if req.MultipartForm != nil && req.MultipartForm.File != nil { + if values := req.MultipartForm.Value[key]; len(values) > 0 { + return values[0], true + } + } + return "", false +} + +// This function checks the Content-Type to select a binding engine automatically, +// Depending the "Content-Type" header different bindings are used: +// "application/json" --> JSON binding +// "application/xml" --> XML binding +// else --> returns an error +// if Parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer.Like ParseBody() but this method also writes a 400 error if the json is not valid. +func (c *Context) Bind(obj interface{}) error { + b := binding.Default(c.Request.Method, c.ContentType()) + return c.BindWith(obj, b) +} + +// Shortcut for c.BindWith(obj, binding.JSON) +func (c *Context) BindJSON(obj interface{}) error { + return c.BindWith(obj, binding.JSON) +} + +func (c *Context) BindWith(obj interface{}, b binding.Binding) error { + if err := b.Bind(c.Request, obj); err != nil { + c.AbortWithError(400, err).SetType(ErrorTypeBind) + return err + } + return nil +} + +// Best effort algoritm to return the real client IP, it parses +// X-Real-IP and X-Forwarded-For in order to work properly with reverse-proxies such us: nginx or haproxy. +func (c *Context) ClientIP() string { + if c.engine.ForwardedByClientIP { + clientIP := strings.TrimSpace(c.requestHeader("X-Real-Ip")) + if len(clientIP) > 0 { + return clientIP + } + clientIP = c.requestHeader("X-Forwarded-For") + if index := strings.IndexByte(clientIP, ','); index >= 0 { + clientIP = clientIP[0:index] + } + clientIP = strings.TrimSpace(clientIP) + if len(clientIP) > 0 { + return clientIP + } + } + return strings.TrimSpace(c.Request.RemoteAddr) +} + +func (c *Context) ContentType() string { + return filterFlags(c.requestHeader("Content-Type")) +} + +func (c *Context) requestHeader(key string) string { + if values, _ := c.Request.Header[key]; len(values) > 0 { + return values[0] + } + return "" +} + +/************************************/ +/******** RESPONSE RENDERING ********/ +/************************************/ + +// Intelligent shortcut for c.Writer.Header().Set(key, value) +// it writes a header in the response. +// If value == "", this method removes the header `c.Writer.Header().Del(key)` +func (c *Context) Header(key, value string) { + if len(value) == 0 { + c.Writer.Header().Del(key) + } else { + c.Writer.Header().Set(key, value) + } +} + +func (c *Context) Render(code int, r render.Render) { + c.writermem.WriteHeader(code) + if err := r.Render(c.Writer); err != nil { + c.renderError(err) + } +} + +func (c *Context) renderError(err error) { + debugPrintError(err) + c.AbortWithError(500, err).SetType(ErrorTypeRender) +} + +// Renders the HTTP template specified by its file name. +// It also updates the HTTP code and sets the Content-Type as "text/html". +// See http://golang.org/doc/articles/wiki/ +func (c *Context) HTML(code int, name string, obj interface{}) { + instance := c.engine.HTMLRender.Instance(name, obj) + c.Render(code, instance) +} + +// Serializes the given struct as pretty JSON (indented + endlines) into the response body. +// It also sets the Content-Type as "application/json". +// WARNING: we recommend to use this only for development propuses since printing pretty JSON is +// more CPU and bandwidth consuming. Use Context.JSON() instead. +func (c *Context) IndentedJSON(code int, obj interface{}) { + c.Render(code, render.IndentedJSON{Data: obj}) +} + +// Serializes the given struct as JSON into the response body. +// It also sets the Content-Type as "application/json". +func (c *Context) JSON(code int, obj interface{}) { + c.writermem.WriteHeader(code) + if err := render.WriteJSON(c.Writer, obj); err != nil { + c.renderError(err) + } +} + +// Serializes the given struct as XML into the response body. +// It also sets the Content-Type as "application/xml". +func (c *Context) XML(code int, obj interface{}) { + c.Render(code, render.XML{Data: obj}) +} + +// Writes the given string into the response body. +func (c *Context) String(code int, format string, values ...interface{}) { + c.writermem.WriteHeader(code) + render.WriteString(c.Writer, format, values) +} + +// Returns a HTTP redirect to the specific location. +func (c *Context) Redirect(code int, location string) { + c.Render(-1, render.Redirect{ + Code: code, + Location: location, + Request: c.Request, + }) +} + +// Writes some data into the body stream and updates the HTTP code. +func (c *Context) Data(code int, contentType string, data []byte) { + c.Render(code, render.Data{ + ContentType: contentType, + Data: data, + }) +} + +// Writes the specified file into the body stream in a efficient way. +func (c *Context) File(filepath string) { + http.ServeFile(c.Writer, c.Request, filepath) +} + +func (c *Context) SSEvent(name string, message interface{}) { + c.Render(-1, sse.Event{ + Event: name, + Data: message, + }) +} + +func (c *Context) Stream(step func(w io.Writer) bool) { + w := c.Writer + clientGone := w.CloseNotify() + for { + select { + case <-clientGone: + return + default: + keepopen := step(w) + w.Flush() + if !keepopen { + return + } + } + } +} + +/************************************/ +/******** CONTENT NEGOTIATION *******/ +/************************************/ + +type Negotiate struct { + Offered []string + HTMLName string + HTMLData interface{} + JSONData interface{} + XMLData interface{} + Data interface{} +} + +func (c *Context) Negotiate(code int, config Negotiate) { + switch c.NegotiateFormat(config.Offered...) { + case binding.MIMEJSON: + data := chooseData(config.JSONData, config.Data) + c.JSON(code, data) + + case binding.MIMEHTML: + data := chooseData(config.HTMLData, config.Data) + c.HTML(code, config.HTMLName, data) + + case binding.MIMEXML: + data := chooseData(config.XMLData, config.Data) + c.XML(code, data) + + default: + c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) + } +} + +func (c *Context) NegotiateFormat(offered ...string) string { + if len(offered) == 0 { + panic("you must provide at least one offer") + } + if c.Accepted == nil { + c.Accepted = parseAccept(c.requestHeader("Accept")) + } + if len(c.Accepted) == 0 { + return offered[0] + } + for _, accepted := range c.Accepted { + for _, offert := range offered { + if accepted == offert { + return offert + } + } + } + return "" +} + +func (c *Context) SetAccepted(formats ...string) { + c.Accepted = formats +} + +/************************************/ +/***** GOLANG.ORG/X/NET/CONTEXT *****/ +/************************************/ + +func (c *Context) Deadline() (deadline time.Time, ok bool) { + return +} + +func (c *Context) Done() <-chan struct{} { + return nil +} + +func (c *Context) Err() error { + return nil +} + +func (c *Context) Value(key interface{}) interface{} { + if key == 0 { + return c.Request + } + if keyAsString, ok := key.(string); ok { + val, _ := c.Get(keyAsString) + return val + } + return nil +} -- cgit v1.2.3