2021-08-12 19:03:24 +00:00
// 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"
"fmt"
"io"
"io/ioutil"
"log"
"math"
"mime/multipart"
"net"
"net/http"
"net/url"
"os"
"strings"
"sync"
"time"
"github.com/gin-contrib/sse"
"github.com/gin-gonic/gin/binding"
"github.com/gin-gonic/gin/render"
)
// Content-Type MIME of the most common data formats.
const (
MIMEJSON = binding . MIMEJSON
MIMEHTML = binding . MIMEHTML
MIMEXML = binding . MIMEXML
MIMEXML2 = binding . MIMEXML2
MIMEPlain = binding . MIMEPlain
MIMEPOSTForm = binding . MIMEPOSTForm
MIMEMultipartPOSTForm = binding . MIMEMultipartPOSTForm
MIMEYAML = binding . MIMEYAML
)
// BodyBytesKey indicates a default body bytes key.
const BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
2021-11-27 14:26:58 +00:00
const abortIndex int8 = math . MaxInt8 / 2
2021-08-12 19:03:24 +00:00
// 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
fullPath string
2021-11-27 14:26:58 +00:00
engine * Engine
params * Params
skippedNodes * [ ] skippedNode
2021-08-12 19:03:24 +00:00
// This mutex protect Keys map
mu sync . RWMutex
// Keys is a key/value pair exclusively for the context of each request.
Keys map [ string ] interface { }
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
Errors errorMsgs
// Accepted defines a list of manually accepted formats for content negotiation.
Accepted [ ] string
// queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query()
queryCache url . Values
// formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH,
// or PUT body parameters.
formCache url . Values
// SameSite allows a server to define a cookie attribute making it impossible for
// the browser to send this cookie along with cross-site requests.
sameSite http . SameSite
}
/************************************/
/********** CONTEXT CREATION ********/
/************************************/
func ( c * Context ) reset ( ) {
c . Writer = & c . writermem
2021-11-27 14:26:58 +00:00
c . Params = c . Params [ 0 : 0 ]
2021-08-12 19:03:24 +00:00
c . handlers = nil
c . index = - 1
c . fullPath = ""
c . Keys = nil
2021-11-27 14:26:58 +00:00
c . Errors = c . Errors [ 0 : 0 ]
2021-08-12 19:03:24 +00:00
c . Accepted = nil
c . queryCache = nil
c . formCache = nil
* c . params = ( * c . params ) [ : 0 ]
2021-11-27 14:26:58 +00:00
* c . skippedNodes = ( * c . skippedNodes ) [ : 0 ]
2021-08-12 19:03:24 +00:00
}
// Copy returns a copy of the current context that can be safely used outside the request's scope.
// This has to be used when the context has to be passed to a goroutine.
func ( c * Context ) Copy ( ) * Context {
cp := Context {
writermem : c . writermem ,
Request : c . Request ,
Params : c . Params ,
engine : c . engine ,
}
cp . writermem . ResponseWriter = nil
cp . Writer = & cp . writermem
cp . index = abortIndex
cp . handlers = nil
cp . Keys = map [ string ] interface { } { }
for k , v := range c . Keys {
cp . Keys [ k ] = v
}
paramCopy := make ( [ ] Param , len ( cp . Params ) )
copy ( paramCopy , cp . Params )
cp . Params = paramCopy
return & cp
}
// HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()",
// this function will return "main.handleGetUsers".
func ( c * Context ) HandlerName ( ) string {
return nameOfFunction ( c . handlers . Last ( ) )
}
// HandlerNames returns a list of all registered handlers for this context in descending order,
// following the semantics of HandlerName()
func ( c * Context ) HandlerNames ( ) [ ] string {
hn := make ( [ ] string , 0 , len ( c . handlers ) )
for _ , val := range c . handlers {
hn = append ( hn , nameOfFunction ( val ) )
}
return hn
}
// Handler returns the main handler.
func ( c * Context ) Handler ( ) HandlerFunc {
return c . handlers . Last ( )
}
// FullPath returns a matched route full path. For not found routes
// returns an empty string.
// router.GET("/user/:id", func(c *gin.Context) {
// c.FullPath() == "/user/:id" // true
// })
func ( c * Context ) FullPath ( ) string {
return c . fullPath
}
/************************************/
/*********** FLOW CONTROL ***********/
/************************************/
// Next should be used only inside middleware.
// It executes the pending handlers in the chain inside the calling handler.
// See example in GitHub.
func ( c * Context ) Next ( ) {
c . index ++
for c . index < int8 ( len ( c . handlers ) ) {
c . handlers [ c . index ] ( c )
c . index ++
}
}
// IsAborted returns true if the current context was aborted.
func ( c * Context ) IsAborted ( ) bool {
return c . index >= abortIndex
}
// Abort prevents pending handlers from being called. Note that this will not stop the current handler.
// Let's say you have an authorization middleware that validates that the current request is authorized.
// If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers
// for this request are not called.
func ( c * Context ) Abort ( ) {
c . index = abortIndex
}
// AbortWithStatus calls `Abort()` and writes the headers with the specified status code.
// For example, a failed attempt to authenticate a request could use: context.AbortWithStatus(401).
func ( c * Context ) AbortWithStatus ( code int ) {
c . Status ( code )
c . Writer . WriteHeaderNow ( )
c . Abort ( )
}
// AbortWithStatusJSON calls `Abort()` and then `JSON` internally.
// This method stops the chain, writes the status code and return a JSON body.
// It also sets the Content-Type as "application/json".
func ( c * Context ) AbortWithStatusJSON ( code int , jsonObj interface { } ) {
c . Abort ( )
c . JSON ( code , jsonObj )
}
// AbortWithError 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 *********/
/************************************/
// Error 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.
// Error will panic if err is nil.
func ( c * Context ) Error ( err error ) * Error {
if err == nil {
panic ( "err is nil" )
}
parsedError , ok := err . ( * Error )
if ! ok {
parsedError = & Error {
Err : err ,
Type : ErrorTypePrivate ,
}
}
c . Errors = append ( c . Errors , parsedError )
return parsedError
}
/************************************/
/******** METADATA MANAGEMENT********/
/************************************/
// Set is used to store a new key/value pair exclusively for this context.
// It also lazy initializes c.Keys if it was not used previously.
func ( c * Context ) Set ( key string , value interface { } ) {
c . mu . Lock ( )
if c . Keys == nil {
c . Keys = make ( map [ string ] interface { } )
}
c . Keys [ key ] = value
c . mu . Unlock ( )
}
// Get 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 ) {
c . mu . RLock ( )
value , exists = c . Keys [ key ]
c . mu . RUnlock ( )
return
}
// MustGet 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" )
}
// GetString returns the value associated with the key as a string.
func ( c * Context ) GetString ( key string ) ( s string ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
s , _ = val . ( string )
}
return
}
// GetBool returns the value associated with the key as a boolean.
func ( c * Context ) GetBool ( key string ) ( b bool ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
b , _ = val . ( bool )
}
return
}
// GetInt returns the value associated with the key as an integer.
func ( c * Context ) GetInt ( key string ) ( i int ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
i , _ = val . ( int )
}
return
}
// GetInt64 returns the value associated with the key as an integer.
func ( c * Context ) GetInt64 ( key string ) ( i64 int64 ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
i64 , _ = val . ( int64 )
}
return
}
// GetUint returns the value associated with the key as an unsigned integer.
func ( c * Context ) GetUint ( key string ) ( ui uint ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
ui , _ = val . ( uint )
}
return
}
// GetUint64 returns the value associated with the key as an unsigned integer.
func ( c * Context ) GetUint64 ( key string ) ( ui64 uint64 ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
ui64 , _ = val . ( uint64 )
}
return
}
// GetFloat64 returns the value associated with the key as a float64.
func ( c * Context ) GetFloat64 ( key string ) ( f64 float64 ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
f64 , _ = val . ( float64 )
}
return
}
// GetTime returns the value associated with the key as time.
func ( c * Context ) GetTime ( key string ) ( t time . Time ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
t , _ = val . ( time . Time )
}
return
}
// GetDuration returns the value associated with the key as a duration.
func ( c * Context ) GetDuration ( key string ) ( d time . Duration ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
d , _ = val . ( time . Duration )
}
return
}
// GetStringSlice returns the value associated with the key as a slice of strings.
func ( c * Context ) GetStringSlice ( key string ) ( ss [ ] string ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
ss , _ = val . ( [ ] string )
}
return
}
// GetStringMap returns the value associated with the key as a map of interfaces.
func ( c * Context ) GetStringMap ( key string ) ( sm map [ string ] interface { } ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
sm , _ = val . ( map [ string ] interface { } )
}
return
}
// GetStringMapString returns the value associated with the key as a map of strings.
func ( c * Context ) GetStringMapString ( key string ) ( sms map [ string ] string ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
sms , _ = val . ( map [ string ] string )
}
return
}
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
func ( c * Context ) GetStringMapStringSlice ( key string ) ( smss map [ string ] [ ] string ) {
if val , ok := c . Get ( key ) ; ok && val != nil {
smss , _ = val . ( map [ string ] [ ] string )
}
return
}
/************************************/
/************ INPUT DATA ************/
/************************************/
// Param returns the value of the URL param.
// It is a shortcut for c.Params.ByName(key)
// router.GET("/user/:id", func(c *gin.Context) {
// // a GET request to /user/john
// id := c.Param("id") // id == "john"
// })
func ( c * Context ) Param ( key string ) string {
return c . Params . ByName ( key )
}
// Query returns the keyed url query value if it exists,
// otherwise it returns an empty string `("")`.
// It is shortcut for `c.Request.URL.Query().Get(key)`
// GET /path?id=1234&name=Manu&value=
// c.Query("id") == "1234"
// c.Query("name") == "Manu"
// c.Query("value") == ""
// c.Query("wtf") == ""
2021-11-27 14:26:58 +00:00
func ( c * Context ) Query ( key string ) string {
value , _ := c . GetQuery ( key )
return value
2021-08-12 19:03:24 +00:00
}
// DefaultQuery returns the keyed url query value if it exists,
// otherwise it returns the specified defaultValue string.
// See: Query() and GetQuery() for further information.
// GET /?name=Manu&lastname=
// c.DefaultQuery("name", "unknown") == "Manu"
// c.DefaultQuery("id", "none") == "none"
// c.DefaultQuery("lastname", "none") == ""
func ( c * Context ) DefaultQuery ( key , defaultValue string ) string {
if value , ok := c . GetQuery ( key ) ; ok {
return value
}
return defaultValue
}
// GetQuery is like Query(), it returns the keyed url query value
// if it exists `(value, true)` (even when the value is an empty string),
// otherwise it returns `("", false)`.
// It is shortcut for `c.Request.URL.Query().Get(key)`
// GET /?name=Manu&lastname=
// ("Manu", true) == c.GetQuery("name")
// ("", false) == c.GetQuery("id")
// ("", true) == c.GetQuery("lastname")
func ( c * Context ) GetQuery ( key string ) ( string , bool ) {
if values , ok := c . GetQueryArray ( key ) ; ok {
return values [ 0 ] , ok
}
return "" , false
}
// QueryArray returns a slice of strings for a given query key.
// The length of the slice depends on the number of params with the given key.
2021-11-27 14:26:58 +00:00
func ( c * Context ) QueryArray ( key string ) [ ] string {
values , _ := c . GetQueryArray ( key )
return values
2021-08-12 19:03:24 +00:00
}
func ( c * Context ) initQueryCache ( ) {
if c . queryCache == nil {
if c . Request != nil {
c . queryCache = c . Request . URL . Query ( )
} else {
c . queryCache = url . Values { }
}
}
}
// GetQueryArray returns a slice of strings for a given query key, plus
// a boolean value whether at least one value exists for the given key.
2021-11-27 14:26:58 +00:00
func ( c * Context ) GetQueryArray ( key string ) ( [ ] string , bool ) {
2021-08-12 19:03:24 +00:00
c . initQueryCache ( )
2021-11-27 14:26:58 +00:00
if values , ok := c . queryCache [ key ] ; ok && len ( values ) > 0 {
return values , true
}
return [ ] string { } , false
2021-08-12 19:03:24 +00:00
}
// QueryMap returns a map for a given query key.
2021-11-27 14:26:58 +00:00
func ( c * Context ) QueryMap ( key string ) map [ string ] string {
dicts , _ := c . GetQueryMap ( key )
return dicts
2021-08-12 19:03:24 +00:00
}
// GetQueryMap returns a map for a given query key, plus a boolean value
// whether at least one value exists for the given key.
func ( c * Context ) GetQueryMap ( key string ) ( map [ string ] string , bool ) {
c . initQueryCache ( )
return c . get ( c . queryCache , key )
}
// PostForm returns the specified key from a POST urlencoded form or multipart form
// when it exists, otherwise it returns an empty string `("")`.
2021-11-27 14:26:58 +00:00
func ( c * Context ) PostForm ( key string ) string {
value , _ := c . GetPostForm ( key )
return value
2021-08-12 19:03:24 +00:00
}
// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form
// when it exists, otherwise it returns the specified defaultValue string.
// See: PostForm() and GetPostForm() for further information.
func ( c * Context ) DefaultPostForm ( key , defaultValue string ) string {
if value , ok := c . GetPostForm ( key ) ; ok {
return value
}
return defaultValue
}
// GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded
// form or multipart form when it exists `(value, true)` (even when the value is an empty string),
// otherwise it returns ("", false).
// For example, during a PATCH request to update the user's email:
// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
// email= --> ("", true) := GetPostForm("email") // set email to ""
// --> ("", false) := GetPostForm("email") // do nothing with email
func ( c * Context ) GetPostForm ( key string ) ( string , bool ) {
if values , ok := c . GetPostFormArray ( key ) ; ok {
return values [ 0 ] , ok
}
return "" , false
}
// PostFormArray returns a slice of strings for a given form key.
// The length of the slice depends on the number of params with the given key.
2021-11-27 14:26:58 +00:00
func ( c * Context ) PostFormArray ( key string ) [ ] string {
values , _ := c . GetPostFormArray ( key )
return values
2021-08-12 19:03:24 +00:00
}
func ( c * Context ) initFormCache ( ) {
if c . formCache == nil {
c . formCache = make ( url . Values )
req := c . Request
if err := req . ParseMultipartForm ( c . engine . MaxMultipartMemory ) ; err != nil {
if err != http . ErrNotMultipart {
debugPrint ( "error on parse multipart form array: %v" , err )
}
}
c . formCache = req . PostForm
}
}
// GetPostFormArray returns a slice of strings for a given form key, plus
// a boolean value whether at least one value exists for the given key.
2021-11-27 14:26:58 +00:00
func ( c * Context ) GetPostFormArray ( key string ) ( [ ] string , bool ) {
2021-08-12 19:03:24 +00:00
c . initFormCache ( )
2021-11-27 14:26:58 +00:00
if values := c . formCache [ key ] ; len ( values ) > 0 {
return values , true
}
return [ ] string { } , false
2021-08-12 19:03:24 +00:00
}
// PostFormMap returns a map for a given form key.
2021-11-27 14:26:58 +00:00
func ( c * Context ) PostFormMap ( key string ) map [ string ] string {
dicts , _ := c . GetPostFormMap ( key )
return dicts
2021-08-12 19:03:24 +00:00
}
// GetPostFormMap returns a map for a given form key, plus a boolean value
// whether at least one value exists for the given key.
func ( c * Context ) GetPostFormMap ( key string ) ( map [ string ] string , bool ) {
c . initFormCache ( )
return c . get ( c . formCache , key )
}
// get is an internal method and returns a map which satisfy conditions.
func ( c * Context ) get ( m map [ string ] [ ] string , key string ) ( map [ string ] string , bool ) {
dicts := make ( map [ string ] string )
exist := false
for k , v := range m {
if i := strings . IndexByte ( k , '[' ) ; i >= 1 && k [ 0 : i ] == key {
if j := strings . IndexByte ( k [ i + 1 : ] , ']' ) ; j >= 1 {
exist = true
dicts [ k [ i + 1 : ] [ : j ] ] = v [ 0 ]
}
}
}
return dicts , exist
}
// FormFile returns the first file for the provided form key.
func ( c * Context ) FormFile ( name string ) ( * multipart . FileHeader , error ) {
if c . Request . MultipartForm == nil {
if err := c . Request . ParseMultipartForm ( c . engine . MaxMultipartMemory ) ; err != nil {
return nil , err
}
}
f , fh , err := c . Request . FormFile ( name )
if err != nil {
return nil , err
}
f . Close ( )
return fh , err
}
// MultipartForm is the parsed multipart form, including file uploads.
func ( c * Context ) MultipartForm ( ) ( * multipart . Form , error ) {
err := c . Request . ParseMultipartForm ( c . engine . MaxMultipartMemory )
return c . Request . MultipartForm , err
}
// SaveUploadedFile uploads the form file to specific dst.
func ( c * Context ) SaveUploadedFile ( file * multipart . FileHeader , dst string ) error {
src , err := file . Open ( )
if err != nil {
return err
}
defer src . Close ( )
out , err := os . Create ( dst )
if err != nil {
return err
}
defer out . Close ( )
_ , err = io . Copy ( out , src )
return err
}
// Bind 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
// otherwise --> returns an error.
// It 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.
// It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.
func ( c * Context ) Bind ( obj interface { } ) error {
b := binding . Default ( c . Request . Method , c . ContentType ( ) )
return c . MustBindWith ( obj , b )
}
// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).
func ( c * Context ) BindJSON ( obj interface { } ) error {
return c . MustBindWith ( obj , binding . JSON )
}
// BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML).
func ( c * Context ) BindXML ( obj interface { } ) error {
return c . MustBindWith ( obj , binding . XML )
}
// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).
func ( c * Context ) BindQuery ( obj interface { } ) error {
return c . MustBindWith ( obj , binding . Query )
}
// BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML).
func ( c * Context ) BindYAML ( obj interface { } ) error {
return c . MustBindWith ( obj , binding . YAML )
}
// BindHeader is a shortcut for c.MustBindWith(obj, binding.Header).
func ( c * Context ) BindHeader ( obj interface { } ) error {
return c . MustBindWith ( obj , binding . Header )
}
// BindUri binds the passed struct pointer using binding.Uri.
// It will abort the request with HTTP 400 if any error occurs.
func ( c * Context ) BindUri ( obj interface { } ) error {
if err := c . ShouldBindUri ( obj ) ; err != nil {
c . AbortWithError ( http . StatusBadRequest , err ) . SetType ( ErrorTypeBind ) // nolint: errcheck
return err
}
return nil
}
// MustBindWith binds the passed struct pointer using the specified binding engine.
// It will abort the request with HTTP 400 if any error occurs.
// See the binding package.
func ( c * Context ) MustBindWith ( obj interface { } , b binding . Binding ) error {
if err := c . ShouldBindWith ( obj , b ) ; err != nil {
c . AbortWithError ( http . StatusBadRequest , err ) . SetType ( ErrorTypeBind ) // nolint: errcheck
return err
}
return nil
}
// ShouldBind 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
// otherwise --> returns an error
// It 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 c.Bind() but this method does not set the response status code to 400 and abort if the json is not valid.
func ( c * Context ) ShouldBind ( obj interface { } ) error {
b := binding . Default ( c . Request . Method , c . ContentType ( ) )
return c . ShouldBindWith ( obj , b )
}
// ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
func ( c * Context ) ShouldBindJSON ( obj interface { } ) error {
return c . ShouldBindWith ( obj , binding . JSON )
}
// ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML).
func ( c * Context ) ShouldBindXML ( obj interface { } ) error {
return c . ShouldBindWith ( obj , binding . XML )
}
// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
func ( c * Context ) ShouldBindQuery ( obj interface { } ) error {
return c . ShouldBindWith ( obj , binding . Query )
}
// ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
func ( c * Context ) ShouldBindYAML ( obj interface { } ) error {
return c . ShouldBindWith ( obj , binding . YAML )
}
// ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).
func ( c * Context ) ShouldBindHeader ( obj interface { } ) error {
return c . ShouldBindWith ( obj , binding . Header )
}
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
func ( c * Context ) ShouldBindUri ( obj interface { } ) error {
m := make ( map [ string ] [ ] string )
for _ , v := range c . Params {
m [ v . Key ] = [ ] string { v . Value }
}
return binding . Uri . BindUri ( m , obj )
}
// ShouldBindWith binds the passed struct pointer using the specified binding engine.
// See the binding package.
func ( c * Context ) ShouldBindWith ( obj interface { } , b binding . Binding ) error {
return b . Bind ( c . Request , obj )
}
// ShouldBindBodyWith is similar with ShouldBindWith, but it stores the request
// body into the context, and reuse when it is called again.
//
// NOTE: This method reads the body before binding. So you should use
// ShouldBindWith for better performance if you need to call only once.
func ( c * Context ) ShouldBindBodyWith ( obj interface { } , bb binding . BindingBody ) ( err error ) {
var body [ ] byte
if cb , ok := c . Get ( BodyBytesKey ) ; ok {
if cbb , ok := cb . ( [ ] byte ) ; ok {
body = cbb
}
}
if body == nil {
body , err = ioutil . ReadAll ( c . Request . Body )
if err != nil {
return err
}
c . Set ( BodyBytesKey , body )
}
return bb . BindBody ( body , obj )
}
2021-11-27 14:26:58 +00:00
// ClientIP implements one best effort algorithm to return the real client IP.
2021-08-12 19:03:24 +00:00
// It called c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
2021-11-27 14:26:58 +00:00
// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
// If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy,
2021-08-12 19:03:24 +00:00
// the remote IP (coming form Request.RemoteAddr) is returned.
func ( c * Context ) ClientIP ( ) string {
2021-11-27 14:26:58 +00:00
// Check if we're running on a trusted platform, continue running backwards if error
if c . engine . TrustedPlatform != "" {
// Developers can define their own header of Trusted Platform or use predefined constants
if addr := c . requestHeader ( c . engine . TrustedPlatform ) ; addr != "" {
2021-08-12 19:03:24 +00:00
return addr
}
}
// Legacy "AppEngine" flag
if c . engine . AppEngine {
log . Println ( ` The AppEngine flag is going to be deprecated. Please check issues #2723 and #2739 and use 'TrustedPlatform: gin.PlatformGoogleAppEngine' instead. ` )
if addr := c . requestHeader ( "X-Appengine-Remote-Addr" ) ; addr != "" {
return addr
}
}
remoteIP , trusted := c . RemoteIP ( )
if remoteIP == nil {
return ""
}
if trusted && c . engine . ForwardedByClientIP && c . engine . RemoteIPHeaders != nil {
for _ , headerName := range c . engine . RemoteIPHeaders {
2021-11-27 14:26:58 +00:00
ip , valid := c . engine . validateHeader ( c . requestHeader ( headerName ) )
2021-08-12 19:03:24 +00:00
if valid {
return ip
}
}
}
return remoteIP . String ( )
}
2021-11-27 14:26:58 +00:00
func ( e * Engine ) isTrustedProxy ( ip net . IP ) bool {
if e . trustedCIDRs != nil {
for _ , cidr := range e . trustedCIDRs {
if cidr . Contains ( ip ) {
return true
}
}
}
return false
}
2021-08-12 19:03:24 +00:00
// RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port).
// It also checks if the remoteIP is a trusted proxy or not.
// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks
2021-11-27 14:26:58 +00:00
// defined by Engine.SetTrustedProxies()
2021-08-12 19:03:24 +00:00
func ( c * Context ) RemoteIP ( ) ( net . IP , bool ) {
ip , _ , err := net . SplitHostPort ( strings . TrimSpace ( c . Request . RemoteAddr ) )
if err != nil {
return nil , false
}
remoteIP := net . ParseIP ( ip )
if remoteIP == nil {
return nil , false
}
2021-11-27 14:26:58 +00:00
return remoteIP , c . engine . isTrustedProxy ( remoteIP )
2021-08-12 19:03:24 +00:00
}
2021-11-27 14:26:58 +00:00
func ( e * Engine ) validateHeader ( header string ) ( clientIP string , valid bool ) {
2021-08-12 19:03:24 +00:00
if header == "" {
return "" , false
}
items := strings . Split ( header , "," )
2021-11-27 14:26:58 +00:00
for i := len ( items ) - 1 ; i >= 0 ; i -- {
ipStr := strings . TrimSpace ( items [ i ] )
2021-08-12 19:03:24 +00:00
ip := net . ParseIP ( ipStr )
if ip == nil {
return "" , false
}
2021-11-27 14:26:58 +00:00
// X-Forwarded-For is appended by proxy
// Check IPs in reverse order and stop when find untrusted proxy
if ( i == 0 ) || ( ! e . isTrustedProxy ( ip ) ) {
return ipStr , true
2021-08-12 19:03:24 +00:00
}
}
return
}
// ContentType returns the Content-Type header of the request.
func ( c * Context ) ContentType ( ) string {
return filterFlags ( c . requestHeader ( "Content-Type" ) )
}
// IsWebsocket returns true if the request headers indicate that a websocket
// handshake is being initiated by the client.
func ( c * Context ) IsWebsocket ( ) bool {
if strings . Contains ( strings . ToLower ( c . requestHeader ( "Connection" ) ) , "upgrade" ) &&
strings . EqualFold ( c . requestHeader ( "Upgrade" ) , "websocket" ) {
return true
}
return false
}
func ( c * Context ) requestHeader ( key string ) string {
return c . Request . Header . Get ( key )
}
/************************************/
/******** RESPONSE RENDERING ********/
/************************************/
// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function.
func bodyAllowedForStatus ( status int ) bool {
switch {
case status >= 100 && status <= 199 :
return false
case status == http . StatusNoContent :
return false
case status == http . StatusNotModified :
return false
}
return true
}
// Status sets the HTTP response code.
func ( c * Context ) Status ( code int ) {
c . Writer . WriteHeader ( code )
}
// Header is a 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 value == "" {
c . Writer . Header ( ) . Del ( key )
return
}
c . Writer . Header ( ) . Set ( key , value )
}
// GetHeader returns value from request headers.
func ( c * Context ) GetHeader ( key string ) string {
return c . requestHeader ( key )
}
// GetRawData return stream data.
func ( c * Context ) GetRawData ( ) ( [ ] byte , error ) {
return ioutil . ReadAll ( c . Request . Body )
}
// SetSameSite with cookie
func ( c * Context ) SetSameSite ( samesite http . SameSite ) {
c . sameSite = samesite
}
// SetCookie adds a Set-Cookie header to the ResponseWriter's headers.
// The provided cookie must have a valid Name. Invalid cookies may be
// silently dropped.
func ( c * Context ) SetCookie ( name , value string , maxAge int , path , domain string , secure , httpOnly bool ) {
if path == "" {
path = "/"
}
http . SetCookie ( c . Writer , & http . Cookie {
Name : name ,
Value : url . QueryEscape ( value ) ,
MaxAge : maxAge ,
Path : path ,
Domain : domain ,
SameSite : c . sameSite ,
Secure : secure ,
HttpOnly : httpOnly ,
} )
}
// Cookie returns the named cookie provided in the request or
// ErrNoCookie if not found. And return the named cookie is unescaped.
// If multiple cookies match the given name, only one cookie will
// be returned.
func ( c * Context ) Cookie ( name string ) ( string , error ) {
cookie , err := c . Request . Cookie ( name )
if err != nil {
return "" , err
}
val , _ := url . QueryUnescape ( cookie . Value )
return val , nil
}
// Render writes the response headers and calls render.Render to render data.
func ( c * Context ) Render ( code int , r render . Render ) {
c . Status ( code )
if ! bodyAllowedForStatus ( code ) {
r . WriteContentType ( c . Writer )
c . Writer . WriteHeaderNow ( )
return
}
if err := r . Render ( c . Writer ) ; err != nil {
panic ( err )
}
}
// HTML 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 )
}
// IndentedJSON 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 purposes 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 } )
}
// SecureJSON serializes the given struct as Secure JSON into the response body.
// Default prepends "while(1)," to response body if the given struct is array values.
// It also sets the Content-Type as "application/json".
func ( c * Context ) SecureJSON ( code int , obj interface { } ) {
c . Render ( code , render . SecureJSON { Prefix : c . engine . secureJSONPrefix , Data : obj } )
}
// JSONP serializes the given struct as JSON into the response body.
// It adds padding to response body to request data from a server residing in a different domain than the client.
// It also sets the Content-Type as "application/javascript".
func ( c * Context ) JSONP ( code int , obj interface { } ) {
callback := c . DefaultQuery ( "callback" , "" )
if callback == "" {
c . Render ( code , render . JSON { Data : obj } )
return
}
c . Render ( code , render . JsonpJSON { Callback : callback , Data : obj } )
}
// JSON 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 . Render ( code , render . JSON { Data : obj } )
}
// AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string.
// It also sets the Content-Type as "application/json".
func ( c * Context ) AsciiJSON ( code int , obj interface { } ) {
c . Render ( code , render . AsciiJSON { Data : obj } )
}
// PureJSON serializes the given struct as JSON into the response body.
// PureJSON, unlike JSON, does not replace special html characters with their unicode entities.
func ( c * Context ) PureJSON ( code int , obj interface { } ) {
c . Render ( code , render . PureJSON { Data : obj } )
}
// XML 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 } )
}
// YAML serializes the given struct as YAML into the response body.
func ( c * Context ) YAML ( code int , obj interface { } ) {
c . Render ( code , render . YAML { Data : obj } )
}
// ProtoBuf serializes the given struct as ProtoBuf into the response body.
func ( c * Context ) ProtoBuf ( code int , obj interface { } ) {
c . Render ( code , render . ProtoBuf { Data : obj } )
}
// String writes the given string into the response body.
func ( c * Context ) String ( code int , format string , values ... interface { } ) {
c . Render ( code , render . String { Format : format , Data : values } )
}
// Redirect 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 ,
} )
}
// Data 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 ,
} )
}
// DataFromReader writes the specified reader into the body stream and updates the HTTP code.
func ( c * Context ) DataFromReader ( code int , contentLength int64 , contentType string , reader io . Reader , extraHeaders map [ string ] string ) {
c . Render ( code , render . Reader {
Headers : extraHeaders ,
ContentType : contentType ,
ContentLength : contentLength ,
Reader : reader ,
} )
}
// File writes the specified file into the body stream in an efficient way.
func ( c * Context ) File ( filepath string ) {
http . ServeFile ( c . Writer , c . Request , filepath )
}
// FileFromFS writes the specified file from http.FileSystem into the body stream in an efficient way.
func ( c * Context ) FileFromFS ( filepath string , fs http . FileSystem ) {
defer func ( old string ) {
c . Request . URL . Path = old
} ( c . Request . URL . Path )
c . Request . URL . Path = filepath
http . FileServer ( fs ) . ServeHTTP ( c . Writer , c . Request )
}
// FileAttachment writes the specified file into the body stream in an efficient way
// On the client side, the file will typically be downloaded with the given filename
func ( c * Context ) FileAttachment ( filepath , filename string ) {
c . Writer . Header ( ) . Set ( "Content-Disposition" , fmt . Sprintf ( "attachment; filename=\"%s\"" , filename ) )
http . ServeFile ( c . Writer , c . Request , filepath )
}
// SSEvent writes a Server-Sent Event into the body stream.
func ( c * Context ) SSEvent ( name string , message interface { } ) {
c . Render ( - 1 , sse . Event {
Event : name ,
Data : message ,
} )
}
// Stream sends a streaming response and returns a boolean
// indicates "Is client disconnected in middle of stream"
func ( c * Context ) Stream ( step func ( w io . Writer ) bool ) bool {
w := c . Writer
clientGone := w . CloseNotify ( )
for {
select {
case <- clientGone :
return true
default :
keepOpen := step ( w )
w . Flush ( )
if ! keepOpen {
return false
}
}
}
}
/************************************/
/******** CONTENT NEGOTIATION *******/
/************************************/
// Negotiate contains all negotiations data.
type Negotiate struct {
Offered [ ] string
HTMLName string
HTMLData interface { }
JSONData interface { }
XMLData interface { }
YAMLData interface { }
Data interface { }
}
// Negotiate calls different Render according acceptable Accept format.
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 )
case binding . MIMEYAML :
data := chooseData ( config . YAMLData , config . Data )
c . YAML ( code , data )
default :
c . AbortWithError ( http . StatusNotAcceptable , errors . New ( "the accepted formats are not offered by the server" ) ) // nolint: errcheck
}
}
// NegotiateFormat returns an acceptable Accept format.
func ( c * Context ) NegotiateFormat ( offered ... string ) string {
assert1 ( len ( offered ) > 0 , "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 _ , offer := range offered {
// According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers,
// therefore we can just iterate over the string without casting it into []rune
i := 0
for ; i < len ( accepted ) ; i ++ {
if accepted [ i ] == '*' || offer [ i ] == '*' {
return offer
}
if accepted [ i ] != offer [ i ] {
break
}
}
if i == len ( accepted ) {
return offer
}
}
}
return ""
}
// SetAccepted sets Accept header data.
func ( c * Context ) SetAccepted ( formats ... string ) {
c . Accepted = formats
}
/************************************/
/***** GOLANG.ORG/X/NET/CONTEXT *****/
/************************************/
2021-11-27 14:26:58 +00:00
// Deadline always returns that there is no deadline (ok==false),
// maybe you want to use Request.Context().Deadline() instead.
2021-08-12 19:03:24 +00:00
func ( c * Context ) Deadline ( ) ( deadline time . Time , ok bool ) {
2021-11-27 14:26:58 +00:00
return
2021-08-12 19:03:24 +00:00
}
2021-11-27 14:26:58 +00:00
// Done always returns nil (chan which will wait forever),
// if you want to abort your work when the connection was closed
// you should use Request.Context().Done() instead.
2021-08-12 19:03:24 +00:00
func ( c * Context ) Done ( ) <- chan struct { } {
2021-11-27 14:26:58 +00:00
return nil
2021-08-12 19:03:24 +00:00
}
2021-11-27 14:26:58 +00:00
// Err always returns nil, maybe you want to use Request.Context().Err() instead.
2021-08-12 19:03:24 +00:00
func ( c * Context ) Err ( ) error {
2021-11-27 14:26:58 +00:00
return nil
2021-08-12 19:03:24 +00:00
}
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
func ( c * Context ) Value ( key interface { } ) interface { } {
if key == 0 {
return c . Request
}
if keyAsString , ok := key . ( string ) ; ok {
2021-11-27 14:26:58 +00:00
val , _ := c . Get ( keyAsString )
return val
2021-08-12 19:03:24 +00:00
}
2021-11-27 14:26:58 +00:00
return nil
2021-08-12 19:03:24 +00:00
}