context.go 149 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663
  1. package context
  2. import (
  3. "bytes"
  4. stdContext "context"
  5. "encoding/json"
  6. "encoding/xml"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "mime"
  12. "mime/multipart"
  13. "net"
  14. "net/http"
  15. "net/url"
  16. "os"
  17. "path"
  18. "path/filepath"
  19. "reflect"
  20. "regexp"
  21. "strconv"
  22. "strings"
  23. "sync/atomic"
  24. "time"
  25. "unsafe"
  26. "github.com/kataras/iris/core/memstore"
  27. "github.com/kataras/iris/core/netutil"
  28. "github.com/Shopify/goreferrer"
  29. "github.com/fatih/structs"
  30. "github.com/iris-contrib/schema"
  31. jsoniter "github.com/json-iterator/go"
  32. "github.com/microcosm-cc/bluemonday"
  33. "github.com/russross/blackfriday/v2"
  34. "github.com/vmihailenco/msgpack/v5"
  35. "golang.org/x/net/publicsuffix"
  36. "golang.org/x/time/rate"
  37. "google.golang.org/protobuf/encoding/protojson"
  38. "google.golang.org/protobuf/proto"
  39. "gopkg.in/yaml.v3"
  40. )
  41. type (
  42. // BodyDecoder is an interface which any struct can implement in order to customize the decode action
  43. // from ReadJSON and ReadXML
  44. //
  45. // Trivial example of this could be:
  46. // type User struct { Username string }
  47. //
  48. // func (u *User) Decode(data []byte) error {
  49. // return json.Unmarshal(data, u)
  50. // }
  51. //
  52. // the 'context.ReadJSON/ReadXML(&User{})' will call the User's
  53. // Decode option to decode the request body
  54. //
  55. // Note: This is totally optionally, the default decoders
  56. // for ReadJSON is the encoding/json and for ReadXML is the encoding/xml.
  57. //
  58. // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-custom-per-type/main.go
  59. BodyDecoder interface {
  60. Decode(data []byte) error
  61. }
  62. // Unmarshaler is the interface implemented by types that can unmarshal any raw data.
  63. // TIP INFO: Any pointer to a value which implements the BodyDecoder can be override the unmarshaler.
  64. Unmarshaler interface {
  65. Unmarshal(data []byte, outPtr interface{}) error
  66. }
  67. // UnmarshalerFunc a shortcut for the Unmarshaler interface
  68. //
  69. // See 'Unmarshaler' and 'BodyDecoder' for more.
  70. //
  71. // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-custom-via-unmarshaler/main.go
  72. UnmarshalerFunc func(data []byte, outPtr interface{}) error
  73. )
  74. // Unmarshal parses the X-encoded data and stores the result in the value pointed to by v.
  75. // Unmarshal uses the inverse of the encodings that Marshal uses, allocating maps,
  76. // slices, and pointers as necessary.
  77. func (u UnmarshalerFunc) Unmarshal(data []byte, v interface{}) error {
  78. return u(data, v)
  79. }
  80. // LimitRequestBodySize is a middleware which sets a request body size limit
  81. // for all next handlers in the chain.
  82. var LimitRequestBodySize = func(maxRequestBodySizeBytes int64) Handler {
  83. return func(ctx *Context) {
  84. ctx.SetMaxRequestBodySize(maxRequestBodySizeBytes)
  85. ctx.Next()
  86. }
  87. }
  88. // Map is just a type alias of the map[string]interface{} type.
  89. type Map = map[string]interface{}
  90. // Context is the midle-man server's "object" dealing with incoming requests.
  91. //
  92. // A New context is being acquired from a sync.Pool on each connection.
  93. // The Context is the most important thing on the iris's http flow.
  94. //
  95. // Developers send responses to the client's request through a Context.
  96. // Developers get request information from the client's request a Context.
  97. type Context struct {
  98. // the http.ResponseWriter wrapped by custom writer.
  99. writer ResponseWriter
  100. // the original http.Request
  101. request *http.Request
  102. // the current route registered to this request path.
  103. currentRoute RouteReadOnly
  104. // the local key-value storage
  105. params RequestParams // url named parameters.
  106. values memstore.Store // generic storage, middleware communication.
  107. // the underline application app.
  108. app Application
  109. // the route's handlers
  110. handlers Handlers
  111. // the current position of the handler's chain
  112. currentHandlerIndex int
  113. }
  114. // NewContext returns a new Context instance.
  115. func NewContext(app Application) *Context {
  116. return &Context{app: app}
  117. }
  118. // Clone returns a copy of the context that
  119. // can be safely used outside the request's scope.
  120. // Note that if the request-response lifecycle terminated
  121. // or request canceled by the client (can be checked by `ctx.IsCanceled()`)
  122. // then the response writer is totally useless.
  123. // The http.Request pointer value is shared.
  124. func (ctx *Context) Clone() *Context {
  125. valuesCopy := make(memstore.Store, len(ctx.values))
  126. copy(valuesCopy, ctx.values)
  127. paramsCopy := make(memstore.Store, len(ctx.params.Store))
  128. copy(paramsCopy, ctx.params.Store)
  129. return &Context{
  130. app: ctx.app,
  131. values: valuesCopy,
  132. params: RequestParams{Store: paramsCopy},
  133. writer: ctx.writer.Clone(),
  134. request: ctx.request,
  135. currentHandlerIndex: stopExecutionIndex,
  136. currentRoute: ctx.currentRoute,
  137. }
  138. }
  139. // BeginRequest is executing once for each request
  140. // it should prepare the (new or acquired from pool) context's fields for the new request.
  141. // Do NOT call it manually. Framework calls it automatically.
  142. //
  143. // Resets
  144. // 1. handlers to nil.
  145. // 2. values to empty.
  146. // 3. the defer function.
  147. // 4. response writer to the http.ResponseWriter.
  148. // 5. request to the *http.Request.
  149. func (ctx *Context) BeginRequest(w http.ResponseWriter, r *http.Request) {
  150. ctx.currentRoute = nil
  151. ctx.handlers = nil // will be filled by router.Serve/HTTP
  152. ctx.values = ctx.values[0:0] // >> >> by context.Values().Set
  153. ctx.params.Store = ctx.params.Store[0:0]
  154. ctx.request = r
  155. ctx.currentHandlerIndex = 0
  156. ctx.writer = AcquireResponseWriter()
  157. ctx.writer.BeginResponse(w)
  158. }
  159. // EndRequest is executing once after a response to the request was sent and this context is useless or released.
  160. // Do NOT call it manually. Framework calls it automatically.
  161. //
  162. // 1. executes the OnClose function (if any).
  163. // 2. flushes the response writer's result or fire any error handler.
  164. // 3. releases the response writer.
  165. func (ctx *Context) EndRequest() {
  166. if !ctx.app.ConfigurationReadOnly().GetDisableAutoFireStatusCode() &&
  167. StatusCodeNotSuccessful(ctx.GetStatusCode()) {
  168. ctx.app.FireErrorCode(ctx)
  169. }
  170. ctx.writer.FlushResponse()
  171. ctx.writer.EndResponse()
  172. }
  173. // IsCanceled reports whether the client canceled the request
  174. // or the underlying connection has gone.
  175. // Note that it will always return true
  176. // when called from a goroutine after the request-response lifecycle.
  177. func (ctx *Context) IsCanceled() bool {
  178. if reqCtx := ctx.request.Context(); reqCtx != nil {
  179. err := reqCtx.Err()
  180. if err != nil && errors.Is(err, stdContext.Canceled) {
  181. return true
  182. }
  183. }
  184. return false
  185. }
  186. // OnConnectionClose registers the "cb" Handler
  187. // which will be fired on its on goroutine on a cloned Context
  188. // when the underlying connection has gone away.
  189. //
  190. // The code inside the given callback is running on its own routine,
  191. // as explained above, therefore the callback should NOT
  192. // try to access to handler's Context response writer.
  193. //
  194. // This mechanism can be used to cancel long operations on the server
  195. // if the client has disconnected before the response is ready.
  196. //
  197. // It depends on the Request's Context.Done() channel.
  198. //
  199. // Finally, it reports whether the protocol supports pipelines (HTTP/1.1 with pipelines disabled is not supported).
  200. // The "cb" will not fire for sure if the output value is false.
  201. //
  202. // Note that you can register only one callback per route.
  203. //
  204. // See `OnClose` too.
  205. func (ctx *Context) OnConnectionClose(cb Handler) bool {
  206. if cb == nil {
  207. return false
  208. }
  209. reqCtx := ctx.Request().Context()
  210. if reqCtx == nil {
  211. return false
  212. }
  213. notifyClose := reqCtx.Done()
  214. if notifyClose == nil {
  215. return false
  216. }
  217. go func() {
  218. <-notifyClose
  219. // Note(@kataras): No need to clone if not canceled,
  220. // EndRequest will be called on the end of the handler chain,
  221. // no matter the cancelation.
  222. // therefore the context will still be there.
  223. cb(ctx.Clone())
  224. }()
  225. return true
  226. }
  227. // OnClose registers a callback which
  228. // will be fired when the underlying connection has gone away(request canceled)
  229. // on its own goroutine or in the end of the request-response lifecylce
  230. // on the handler's routine itself (Context access).
  231. //
  232. // See `OnConnectionClose` too.
  233. func (ctx *Context) OnClose(cb Handler) {
  234. if cb == nil {
  235. return
  236. }
  237. // Note(@kataras):
  238. // - on normal request-response lifecycle
  239. // the `SetBeforeFlush` will be called first
  240. // and then `OnConnectionClose`,
  241. // - when request was canceled before handler finish its job
  242. // then the `OnConnectionClose` will be called first instead,
  243. // and when the handler function completed then `SetBeforeFlush` is fired.
  244. // These are synchronized, they cannot be executed the same exact time,
  245. // below we just make sure the "cb" is executed once
  246. // by simple boolean check or an atomic one.
  247. var executed uint32
  248. callback := func(ctx *Context) {
  249. if atomic.CompareAndSwapUint32(&executed, 0, 1) {
  250. cb(ctx)
  251. }
  252. }
  253. ctx.OnConnectionClose(callback)
  254. onFlush := func() {
  255. callback(ctx)
  256. }
  257. ctx.writer.SetBeforeFlush(onFlush)
  258. }
  259. /* Note(@kataras): just leave end-developer decide.
  260. const goroutinesContextKey = "iris.goroutines"
  261. type goroutines struct {
  262. wg *sync.WaitGroup
  263. length int
  264. mu sync.RWMutex
  265. }
  266. var acquireGoroutines = func() interface{} {
  267. return &goroutines{wg: new(sync.WaitGroup)}
  268. }
  269. func (ctx *Context) Go(fn func(cancelCtx stdContext.Context)) (running int) {
  270. g := ctx.values.GetOrSet(goroutinesContextKey, acquireGoroutines).(*goroutines)
  271. if fn != nil {
  272. g.wg.Add(1)
  273. g.mu.Lock()
  274. g.length++
  275. g.mu.Unlock()
  276. ctx.waitFunc = g.wg.Wait
  277. go func(reqCtx stdContext.Context) {
  278. fn(reqCtx)
  279. g.wg.Done()
  280. g.mu.Lock()
  281. g.length--
  282. g.mu.Unlock()
  283. }(ctx.request.Context())
  284. }
  285. g.mu.RLock()
  286. running = g.length
  287. g.mu.RUnlock()
  288. return
  289. }
  290. */
  291. // ResponseWriter returns an http.ResponseWriter compatible response writer, as expected.
  292. func (ctx *Context) ResponseWriter() ResponseWriter {
  293. return ctx.writer
  294. }
  295. // ResetResponseWriter sets a new ResponseWriter implementation
  296. // to this Context to use as its writer.
  297. // Note, to change the underline http.ResponseWriter use
  298. // ctx.ResponseWriter().SetWriter(http.ResponseWRiter) instead.
  299. func (ctx *Context) ResetResponseWriter(newResponseWriter ResponseWriter) {
  300. ctx.writer = newResponseWriter
  301. }
  302. // Request returns the original *http.Request, as expected.
  303. func (ctx *Context) Request() *http.Request {
  304. return ctx.request
  305. }
  306. // ResetRequest sets the Context's Request,
  307. // It is useful to store the new request created by a std *http.Request#WithContext() into Iris' Context.
  308. // Use `ResetRequest` when for some reason you want to make a full
  309. // override of the *http.Request.
  310. // Note that: when you just want to change one of each fields you can use the Request() which returns a pointer to Request,
  311. // so the changes will have affect without a full override.
  312. // Usage: you use a native http handler which uses the standard "context" package
  313. // to get values instead of the Iris' Context#Values():
  314. // r := ctx.Request()
  315. // stdCtx := context.WithValue(r.Context(), key, val)
  316. // ctx.ResetRequest(r.WithContext(stdCtx)).
  317. func (ctx *Context) ResetRequest(r *http.Request) {
  318. ctx.request = r
  319. }
  320. // SetCurrentRoute sets the route internally,
  321. // See `GetCurrentRoute()` method too.
  322. // It's being initialized by the Router.
  323. // See `Exec` or `SetHandlers/AddHandler` methods to simulate a request.
  324. func (ctx *Context) SetCurrentRoute(route RouteReadOnly) {
  325. ctx.currentRoute = route
  326. }
  327. // GetCurrentRoute returns the current "read-only" route that
  328. // was registered to this request's path.
  329. func (ctx *Context) GetCurrentRoute() RouteReadOnly {
  330. return ctx.currentRoute
  331. }
  332. // Do sets the "handlers" as the chain
  333. // and executes the first handler,
  334. // handlers should not be empty.
  335. //
  336. // It's used by the router, developers may use that
  337. // to replace and execute handlers immediately.
  338. func (ctx *Context) Do(handlers Handlers) {
  339. if len(handlers) == 0 {
  340. return
  341. }
  342. ctx.handlers = handlers
  343. handlers[0](ctx)
  344. }
  345. // AddHandler can add handler(s)
  346. // to the current request in serve-time,
  347. // these handlers are not persistenced to the router.
  348. //
  349. // Router is calling this function to add the route's handler.
  350. // If AddHandler called then the handlers will be inserted
  351. // to the end of the already-defined route's handler.
  352. //
  353. func (ctx *Context) AddHandler(handlers ...Handler) {
  354. ctx.handlers = append(ctx.handlers, handlers...)
  355. }
  356. // SetHandlers replaces all handlers with the new.
  357. func (ctx *Context) SetHandlers(handlers Handlers) {
  358. ctx.handlers = handlers
  359. }
  360. // Handlers keeps tracking of the current handlers.
  361. func (ctx *Context) Handlers() Handlers {
  362. return ctx.handlers
  363. }
  364. // HandlerIndex sets the current index of the
  365. // current context's handlers chain.
  366. // If n < 0 or the current handlers length is 0 then it just returns the
  367. // current handler index without change the current index.
  368. //
  369. // Look Handlers(), Next() and StopExecution() too.
  370. func (ctx *Context) HandlerIndex(n int) (currentIndex int) {
  371. if n < 0 || n > len(ctx.handlers)-1 {
  372. return ctx.currentHandlerIndex
  373. }
  374. ctx.currentHandlerIndex = n
  375. return n
  376. }
  377. // Proceed is an alternative way to check if a particular handler
  378. // has been executed and called the `ctx.Next` function inside it.
  379. // This is useful only when you run a handler inside
  380. // another handler. It justs checks for before index and the after index.
  381. //
  382. // A usecase example is when you want to execute a middleware
  383. // inside controller's `BeginRequest` that calls the `ctx.Next` inside it.
  384. // The Controller looks the whole flow (BeginRequest, method handler, EndRequest)
  385. // as one handler, so `ctx.Next` will not be reflected to the method handler
  386. // if called from the `BeginRequest`.
  387. //
  388. // Although `BeginRequest` should NOT be used to call other handlers,
  389. // the `BeginRequest` has been introduced to be able to set
  390. // common data to all method handlers before their execution.
  391. // Controllers can accept middleware(s) from the MVC's Application's Router as normally.
  392. //
  393. // That said let's see an example of `ctx.Proceed`:
  394. //
  395. // var authMiddleware = basicauth.New(basicauth.Config{
  396. // Users: map[string]string{
  397. // "admin": "password",
  398. // },
  399. // })
  400. //
  401. // func (c *UsersController) BeginRequest(ctx iris.Context) {
  402. // if !ctx.Proceed(authMiddleware) {
  403. // ctx.StopExecution()
  404. // }
  405. // }
  406. // This Get() will be executed in the same handler as `BeginRequest`,
  407. // internally controller checks for `ctx.StopExecution`.
  408. // So it will not be fired if BeginRequest called the `StopExecution`.
  409. // func(c *UsersController) Get() []models.User {
  410. // return c.Service.GetAll()
  411. //}
  412. // Alternative way is `!ctx.IsStopped()` if middleware make use of the `ctx.StopExecution()` on failure.
  413. func (ctx *Context) Proceed(h Handler) bool {
  414. beforeIdx := ctx.currentHandlerIndex
  415. h(ctx)
  416. if ctx.currentHandlerIndex > beforeIdx && !ctx.IsStopped() {
  417. return true
  418. }
  419. return false
  420. }
  421. // HandlerName returns the current handler's name, helpful for debugging.
  422. func (ctx *Context) HandlerName() string {
  423. return HandlerName(ctx.handlers[ctx.currentHandlerIndex])
  424. }
  425. // HandlerFileLine returns the current running handler's function source file and line information.
  426. // Useful mostly when debugging.
  427. func (ctx *Context) HandlerFileLine() (file string, line int) {
  428. return HandlerFileLine(ctx.handlers[ctx.currentHandlerIndex])
  429. }
  430. // RouteName returns the route name that this handler is running on.
  431. // Note that it may return empty on not found handlers.
  432. func (ctx *Context) RouteName() string {
  433. if ctx.currentRoute == nil {
  434. return ""
  435. }
  436. return ctx.currentRoute.Name()
  437. }
  438. // Next calls the next handler from the handlers chain,
  439. // it should be used inside a middleware.
  440. func (ctx *Context) Next() {
  441. if ctx.IsStopped() {
  442. return
  443. }
  444. nextIndex := ctx.currentHandlerIndex + 1
  445. handlers := ctx.handlers
  446. if nextIndex < len(handlers) {
  447. ctx.currentHandlerIndex = nextIndex
  448. handlers[nextIndex](ctx)
  449. }
  450. }
  451. // NextOr checks if chain has a next handler, if so then it executes it
  452. // otherwise it sets a new chain assigned to this Context based on the given handler(s)
  453. // and executes its first handler.
  454. //
  455. // Returns true if next handler exists and executed, otherwise false.
  456. //
  457. // Note that if no next handler found and handlers are missing then
  458. // it sends a Status Not Found (404) to the client and it stops the execution.
  459. func (ctx *Context) NextOr(handlers ...Handler) bool {
  460. if next := ctx.NextHandler(); next != nil {
  461. ctx.Skip() // skip this handler from the chain.
  462. next(ctx)
  463. return true
  464. }
  465. if len(handlers) == 0 {
  466. ctx.NotFound()
  467. ctx.StopExecution()
  468. return false
  469. }
  470. ctx.Do(handlers)
  471. return false
  472. }
  473. // NextOrNotFound checks if chain has a next handler, if so then it executes it
  474. // otherwise it sends a Status Not Found (404) to the client and stops the execution.
  475. //
  476. // Returns true if next handler exists and executed, otherwise false.
  477. func (ctx *Context) NextOrNotFound() bool { return ctx.NextOr() }
  478. // NextHandler returns (it doesn't execute) the next handler from the handlers chain.
  479. //
  480. // Use .Skip() to skip this handler if needed to execute the next of this returning handler.
  481. func (ctx *Context) NextHandler() Handler {
  482. if ctx.IsStopped() {
  483. return nil
  484. }
  485. nextIndex := ctx.currentHandlerIndex + 1
  486. // check if it has a next middleware
  487. if nextIndex < len(ctx.handlers) {
  488. return ctx.handlers[nextIndex]
  489. }
  490. return nil
  491. }
  492. // Skip skips/ignores the next handler from the handlers chain,
  493. // it should be used inside a middleware.
  494. func (ctx *Context) Skip() {
  495. ctx.HandlerIndex(ctx.currentHandlerIndex + 1)
  496. }
  497. const stopExecutionIndex = -1 // I don't set to a max value because we want to be able to reuse the handlers even if stopped with .Skip
  498. // StopExecution stops the handlers chain of this request.
  499. // Meaning that any following `Next` calls are ignored,
  500. // as a result the next handlers in the chain will not be fire.
  501. func (ctx *Context) StopExecution() {
  502. ctx.currentHandlerIndex = stopExecutionIndex
  503. }
  504. // IsStopped reports whether the current position of the context's handlers is -1,
  505. // means that the StopExecution() was called at least once.
  506. func (ctx *Context) IsStopped() bool {
  507. return ctx.currentHandlerIndex == stopExecutionIndex
  508. }
  509. // StopWithStatus stops the handlers chain and writes the "statusCode".
  510. //
  511. // If the status code is a failure one then
  512. // it will also fire the specified error code handler.
  513. func (ctx *Context) StopWithStatus(statusCode int) {
  514. ctx.StopExecution()
  515. ctx.StatusCode(statusCode)
  516. }
  517. // StopWithText stops the handlers chain and writes the "statusCode"
  518. // among with a message "plainText".
  519. //
  520. // If the status code is a failure one then
  521. // it will also fire the specified error code handler.
  522. func (ctx *Context) StopWithText(statusCode int, plainText string) {
  523. ctx.StopWithStatus(statusCode)
  524. ctx.WriteString(plainText)
  525. }
  526. // StopWithError stops the handlers chain and writes the "statusCode"
  527. // among with the error "err".
  528. // It Calls the `SetErr` method so error handlers can access the given error.
  529. //
  530. // If the status code is a failure one then
  531. // it will also fire the specified error code handler.
  532. func (ctx *Context) StopWithError(statusCode int, err error) {
  533. if err == nil {
  534. return
  535. }
  536. ctx.SetErr(err)
  537. ctx.StopWithText(statusCode, err.Error())
  538. }
  539. // StopWithPlainError like `StopWithError` but it does NOT
  540. // write anything to the response writer, it stores the error
  541. // so any error handler matching the given "statusCode" can handle it by its own.
  542. func (ctx *Context) StopWithPlainError(statusCode int, err error) {
  543. if err == nil {
  544. return
  545. }
  546. ctx.SetErr(err)
  547. ctx.StopWithStatus(statusCode)
  548. }
  549. // StopWithJSON stops the handlers chain, writes the status code
  550. // and sends a JSON response.
  551. //
  552. // If the status code is a failure one then
  553. // it will also fire the specified error code handler.
  554. func (ctx *Context) StopWithJSON(statusCode int, jsonObject interface{}) {
  555. ctx.StopWithStatus(statusCode)
  556. ctx.JSON(jsonObject)
  557. }
  558. // StopWithProblem stops the handlers chain, writes the status code
  559. // and sends an application/problem+json response.
  560. // See `iris.NewProblem` to build a "problem" value correctly.
  561. //
  562. // If the status code is a failure one then
  563. // it will also fire the specified error code handler.
  564. func (ctx *Context) StopWithProblem(statusCode int, problem Problem) {
  565. ctx.StopWithStatus(statusCode)
  566. problem.Status(statusCode)
  567. ctx.Problem(problem)
  568. }
  569. // +------------------------------------------------------------+
  570. // | Current "user/request" storage |
  571. // | and share information between the handlers - Values(). |
  572. // | Save and get named path parameters - Params() |
  573. // +------------------------------------------------------------+
  574. // Params returns the current url's named parameters key-value storage.
  575. // Named path parameters are being saved here.
  576. // This storage, as the whole context, is per-request lifetime.
  577. func (ctx *Context) Params() *RequestParams {
  578. return &ctx.params
  579. }
  580. // Values returns the current "user" storage.
  581. // Named path parameters and any optional data can be saved here.
  582. // This storage, as the whole context, is per-request lifetime.
  583. //
  584. // You can use this function to Set and Get local values
  585. // that can be used to share information between handlers and middleware.
  586. func (ctx *Context) Values() *memstore.Store {
  587. return &ctx.values
  588. }
  589. // +------------------------------------------------------------+
  590. // | Path, Host, Subdomain, IP, Headers etc... |
  591. // +------------------------------------------------------------+
  592. // Method returns the request.Method, the client's http method to the server.
  593. func (ctx *Context) Method() string {
  594. return ctx.request.Method
  595. }
  596. // Path returns the full request path,
  597. // escaped if EnablePathEscape config field is true.
  598. func (ctx *Context) Path() string {
  599. return ctx.RequestPath(ctx.app.ConfigurationReadOnly().GetEnablePathEscape())
  600. }
  601. // DecodeQuery returns the uri parameter as url (string)
  602. // useful when you want to pass something to a database and be valid to retrieve it via context.Param
  603. // use it only for special cases, when the default behavior doesn't suits you.
  604. //
  605. // http://www.blooberry.com/indexdot/html/topics/urlencoding.htm
  606. // it uses just the url.QueryUnescape
  607. func DecodeQuery(path string) string {
  608. if path == "" {
  609. return ""
  610. }
  611. encodedPath, err := url.QueryUnescape(path)
  612. if err != nil {
  613. return path
  614. }
  615. return encodedPath
  616. }
  617. // DecodeURL returns the decoded uri
  618. // useful when you want to pass something to a database and be valid to retrieve it via context.Param
  619. // use it only for special cases, when the default behavior doesn't suits you.
  620. //
  621. // http://www.blooberry.com/indexdot/html/topics/urlencoding.htm
  622. // it uses just the url.Parse
  623. func DecodeURL(uri string) string {
  624. u, err := url.Parse(uri)
  625. if err != nil {
  626. return uri
  627. }
  628. return u.String()
  629. }
  630. // RequestPath returns the full request path,
  631. // based on the 'escape'.
  632. func (ctx *Context) RequestPath(escape bool) string {
  633. if escape {
  634. return ctx.request.URL.EscapedPath() // DecodeQuery(ctx.request.URL.EscapedPath())
  635. }
  636. return ctx.request.URL.Path // RawPath returns empty, requesturi can be used instead also.
  637. }
  638. // PathPrefixMap accepts a map of string and a handler.
  639. // The key of "m" is the key, which is the prefix, regular expressions are not valid.
  640. // The value of "m" is the handler that will be executed if HasPrefix(context.Path).
  641. // func (ctx *Context) PathPrefixMap(m map[string]context.Handler) bool {
  642. // path := ctx.Path()
  643. // for k, v := range m {
  644. // if strings.HasPrefix(path, k) {
  645. // v(ctx)
  646. // return true
  647. // }
  648. // }
  649. // return false
  650. // } no, it will not work because map is a random peek data structure.
  651. // Host returns the host:port part of the request URI, calls the `Request().Host`.
  652. // To get the subdomain part as well use the `Request().URL.Host` method instead.
  653. // To get the subdomain only use the `Subdomain` method instead.
  654. // This method makes use of the `Configuration.HostProxyHeaders` field too.
  655. func (ctx *Context) Host() string {
  656. for header, ok := range ctx.app.ConfigurationReadOnly().GetHostProxyHeaders() {
  657. if !ok {
  658. continue
  659. }
  660. if host := ctx.GetHeader(header); host != "" {
  661. return host
  662. }
  663. }
  664. return GetHost(ctx.request)
  665. }
  666. // GetHost returns the host part of the current URI.
  667. func GetHost(r *http.Request) string {
  668. if host := r.Host; host != "" {
  669. return host
  670. }
  671. // contains subdomain.
  672. return r.URL.Host
  673. }
  674. // Subdomain returns the subdomain of this request, if any.
  675. // Note that this is a fast method which does not cover all cases.
  676. func (ctx *Context) Subdomain() (subdomain string) {
  677. host := ctx.request.URL.Host // ctx.Host()
  678. if index := strings.IndexByte(host, '.'); index > 0 {
  679. subdomain = host[0:index]
  680. }
  681. // listening on mydomain.com:80
  682. // subdomain = mydomain, but it's wrong, it should return ""
  683. vhost := ctx.app.ConfigurationReadOnly().GetVHost()
  684. if strings.Contains(vhost, subdomain) { // then it's not subdomain
  685. return ""
  686. }
  687. return
  688. }
  689. // FindClosest returns a list of "n" paths close to
  690. // this request based on subdomain and request path.
  691. //
  692. // Order may change.
  693. // Example: https://github.com/kataras/iris/tree/master/_examples/routing/intelligence/manual
  694. func (ctx *Context) FindClosest(n int) []string {
  695. return ctx.app.FindClosestPaths(ctx.Subdomain(), ctx.Path(), n)
  696. }
  697. // IsWWW returns true if the current subdomain (if any) is www.
  698. func (ctx *Context) IsWWW() bool {
  699. host := ctx.Host()
  700. if index := strings.IndexByte(host, '.'); index > 0 {
  701. // if it has a subdomain and it's www then return true.
  702. if subdomain := host[0:index]; !strings.Contains(ctx.app.ConfigurationReadOnly().GetVHost(), subdomain) {
  703. return subdomain == "www"
  704. }
  705. }
  706. return false
  707. }
  708. // FullRequestURI returns the full URI,
  709. // including the scheme, the host and the relative requested path/resource.
  710. func (ctx *Context) FullRequestURI() string {
  711. return ctx.AbsoluteURI(ctx.Path())
  712. }
  713. // RemoteAddr tries to parse and return the real client's request IP.
  714. //
  715. // Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders.
  716. //
  717. // If parse based on these headers fail then it will return the Request's `RemoteAddr` field
  718. // which is filled by the server before the HTTP handler,
  719. // unless the Configuration.RemoteAddrHeadersForce was set to true
  720. // which will force this method to return the first IP from RemoteAddrHeaders
  721. // even if it's part of a private network.
  722. //
  723. // Look `Configuration.RemoteAddrHeaders`,
  724. // `Configuration.RemoteAddrHeadersForce`,
  725. // `Configuration.WithRemoteAddrHeader(...)`,
  726. // `Configuration.WithoutRemoteAddrHeader(...)` and
  727. // `Configuration.RemoteAddrPrivateSubnets` for more.
  728. func (ctx *Context) RemoteAddr() string {
  729. if remoteHeaders := ctx.app.ConfigurationReadOnly().GetRemoteAddrHeaders(); len(remoteHeaders) > 0 {
  730. privateSubnets := ctx.app.ConfigurationReadOnly().GetRemoteAddrPrivateSubnets()
  731. for _, headerName := range remoteHeaders {
  732. ipAddresses := strings.Split(ctx.GetHeader(headerName), ",")
  733. if ip, ok := netutil.GetIPAddress(ipAddresses, privateSubnets); ok {
  734. return ip
  735. }
  736. }
  737. if ctx.app.ConfigurationReadOnly().GetRemoteAddrHeadersForce() {
  738. for _, headerName := range remoteHeaders {
  739. // return the first valid IP,
  740. // even if it's a part of a private network.
  741. ipAddresses := strings.Split(ctx.GetHeader(headerName), ",")
  742. for _, addr := range ipAddresses {
  743. if ip, _, err := net.SplitHostPort(addr); err == nil {
  744. return ip
  745. }
  746. }
  747. }
  748. }
  749. }
  750. addr := strings.TrimSpace(ctx.request.RemoteAddr)
  751. if addr != "" {
  752. // if addr has port use the net.SplitHostPort otherwise(error occurs) take as it is
  753. if ip, _, err := net.SplitHostPort(addr); err == nil {
  754. return ip
  755. }
  756. }
  757. return addr
  758. }
  759. // TrimHeaderValue returns the "v[0:first space or semicolon]".
  760. func TrimHeaderValue(v string) string {
  761. for i, char := range v {
  762. if char == ' ' || char == ';' {
  763. return v[:i]
  764. }
  765. }
  766. return v
  767. }
  768. // GetHeader returns the request header's value based on its name.
  769. func (ctx *Context) GetHeader(name string) string {
  770. return ctx.request.Header.Get(name)
  771. }
  772. // GetDomain resolves and returns the server's domain.
  773. func (ctx *Context) GetDomain() string {
  774. hostport := ctx.Host()
  775. if host, _, err := net.SplitHostPort(hostport); err == nil {
  776. // has port.
  777. switch host {
  778. case "127.0.0.1", "0.0.0.0", "::1", "[::1]", "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0:1":
  779. // loopback.
  780. return "localhost"
  781. default:
  782. if domain, err := publicsuffix.EffectiveTLDPlusOne(host); err == nil {
  783. host = domain
  784. }
  785. return host
  786. }
  787. }
  788. return hostport
  789. }
  790. // IsAjax returns true if this request is an 'ajax request'( XMLHttpRequest)
  791. //
  792. // There is no a 100% way of knowing that a request was made via Ajax.
  793. // You should never trust data coming from the client, they can be easily overcome by spoofing.
  794. //
  795. // Note that "X-Requested-With" Header can be modified by any client(because of "X-"),
  796. // so don't rely on IsAjax for really serious stuff,
  797. // try to find another way of detecting the type(i.e, content type),
  798. // there are many blogs that describe these problems and provide different kind of solutions,
  799. // it's always depending on the application you're building,
  800. // this is the reason why this `IsAjax`` is simple enough for general purpose use.
  801. //
  802. // Read more at: https://developer.mozilla.org/en-US/docs/AJAX
  803. // and https://xhr.spec.whatwg.org/
  804. func (ctx *Context) IsAjax() bool {
  805. return ctx.GetHeader("X-Requested-With") == "XMLHttpRequest"
  806. }
  807. var isMobileRegex = regexp.MustCompile("(?:hpw|i|web)os|alamofire|alcatel|amoi|android|avantgo|blackberry|blazer|cell|cfnetwork|darwin|dolfin|dolphin|fennec|htc|ip(?:hone|od|ad)|ipaq|j2me|kindle|midp|minimo|mobi|motorola|nec-|netfront|nokia|opera m(ob|in)i|palm|phone|pocket|portable|psp|silk-accelerated|skyfire|sony|ucbrowser|up.browser|up.link|windows ce|xda|zte|zune")
  808. // IsMobile checks if client is using a mobile device(phone or tablet) to communicate with this server.
  809. // If the return value is true that means that the http client using a mobile
  810. // device to communicate with the server, otherwise false.
  811. //
  812. // Keep note that this checks the "User-Agent" request header.
  813. func (ctx *Context) IsMobile() bool {
  814. s := strings.ToLower(ctx.GetHeader("User-Agent"))
  815. return isMobileRegex.MatchString(s)
  816. }
  817. var isScriptRegex = regexp.MustCompile("curl|wget|collectd|python|urllib|java|jakarta|httpclient|phpcrawl|libwww|perl|go-http|okhttp|lua-resty|winhttp|awesomium")
  818. // IsScript reports whether a client is a script.
  819. func (ctx *Context) IsScript() bool {
  820. s := strings.ToLower(ctx.GetHeader("User-Agent"))
  821. return isScriptRegex.MatchString(s)
  822. }
  823. // IsSSL reports whether the client is running under HTTPS SSL.
  824. //
  825. // See `IsHTTP2` too.
  826. func (ctx *Context) IsSSL() bool {
  827. ssl := strings.EqualFold(ctx.request.URL.Scheme, "https") || ctx.request.TLS != nil
  828. if !ssl {
  829. for k, v := range ctx.app.ConfigurationReadOnly().GetSSLProxyHeaders() {
  830. if ctx.GetHeader(k) == v {
  831. ssl = true
  832. break
  833. }
  834. }
  835. }
  836. return ssl
  837. }
  838. // IsHTTP2 reports whether the protocol version for incoming request was HTTP/2.
  839. // The client code always uses either HTTP/1.1 or HTTP/2.
  840. //
  841. // See `IsSSL` too.
  842. func (ctx *Context) IsHTTP2() bool {
  843. return ctx.request.ProtoMajor == 2
  844. }
  845. // IsGRPC reports whether the request came from a gRPC client.
  846. func (ctx *Context) IsGRPC() bool {
  847. return ctx.IsHTTP2() && ctx.GetContentTypeRequested() == ContentGRPCHeaderValue
  848. }
  849. type (
  850. // Referrer contains the extracted information from the `GetReferrer`
  851. //
  852. // The structure contains struct tags for JSON, form, XML, YAML and TOML.
  853. // Look the `GetReferrer() Referrer` and `goreferrer` external package.
  854. Referrer struct {
  855. Type ReferrerType `json:"type" form:"referrer_type" xml:"Type" yaml:"Type" toml:"Type"`
  856. Label string `json:"label" form:"referrer_form" xml:"Label" yaml:"Label" toml:"Label"`
  857. URL string `json:"url" form:"referrer_url" xml:"URL" yaml:"URL" toml:"URL"`
  858. Subdomain string `json:"subdomain" form:"referrer_subdomain" xml:"Subdomain" yaml:"Subdomain" toml:"Subdomain"`
  859. Domain string `json:"domain" form:"referrer_domain" xml:"Domain" yaml:"Domain" toml:"Domain"`
  860. Tld string `json:"tld" form:"referrer_tld" xml:"Tld" yaml:"Tld" toml:"Tld"`
  861. Path string `json:"path" form:"referrer_path" xml:"Path" yaml:"Path" toml:"Path"`
  862. Query string `json:"query" form:"referrer_query" xml:"Query" yaml:"Query" toml:"GoogleType"`
  863. GoogleType ReferrerGoogleSearchType `json:"googleType" form:"referrer_google_type" xml:"GoogleType" yaml:"GoogleType" toml:"GoogleType"`
  864. }
  865. // ReferrerType is the goreferrer enum for a referrer type (indirect, direct, email, search, social).
  866. ReferrerType = goreferrer.ReferrerType
  867. // ReferrerGoogleSearchType is the goreferrer enum for a google search type (organic, adwords).
  868. ReferrerGoogleSearchType = goreferrer.GoogleSearchType
  869. )
  870. // Contains the available values of the goreferrer enums.
  871. const (
  872. ReferrerInvalid ReferrerType = iota
  873. ReferrerIndirect
  874. ReferrerDirect
  875. ReferrerEmail
  876. ReferrerSearch
  877. ReferrerSocial
  878. ReferrerNotGoogleSearch ReferrerGoogleSearchType = iota
  879. ReferrerGoogleOrganicSearch
  880. ReferrerGoogleAdwords
  881. )
  882. // unnecessary but good to know the default values upfront.
  883. var emptyReferrer = Referrer{Type: ReferrerInvalid, GoogleType: ReferrerNotGoogleSearch}
  884. // GetReferrer extracts and returns the information from the "Referer" (or "Referrer") header
  885. // and url query parameter as specified in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy.
  886. func (ctx *Context) GetReferrer() Referrer {
  887. // the underline net/http follows the https://tools.ietf.org/html/rfc7231#section-5.5.2,
  888. // so there is nothing special left to do.
  889. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
  890. refURL := ctx.GetHeader("Referer")
  891. if refURL == "" {
  892. refURL = ctx.GetHeader("Referrer")
  893. if refURL == "" {
  894. refURL = ctx.URLParam("referer")
  895. if refURL == "" {
  896. refURL = ctx.URLParam("referrer")
  897. }
  898. }
  899. }
  900. if refURL == "" {
  901. return emptyReferrer
  902. }
  903. if ref := goreferrer.DefaultRules.Parse(refURL); ref.Type > goreferrer.Invalid {
  904. return Referrer{
  905. Type: ReferrerType(ref.Type),
  906. Label: ref.Label,
  907. URL: ref.URL,
  908. Subdomain: ref.Subdomain,
  909. Domain: ref.Domain,
  910. Tld: ref.Tld,
  911. Path: ref.Path,
  912. Query: ref.Query,
  913. GoogleType: ReferrerGoogleSearchType(ref.GoogleType),
  914. }
  915. }
  916. return emptyReferrer
  917. }
  918. // SetLanguage force-sets the language for i18n, can be used inside a middleare.
  919. // It has the highest priority over the rest and if it is empty then it is ignored,
  920. // if it set to a static string of "default" or to the default language's code
  921. // then the rest of the language extractors will not be called at all and
  922. // the default language will be set instead.
  923. //
  924. // See `i18n.ExtractFunc` for a more organised way of the same feature.
  925. func (ctx *Context) SetLanguage(langCode string) {
  926. ctx.values.Set(ctx.app.ConfigurationReadOnly().GetLanguageContextKey(), langCode)
  927. }
  928. // GetLocale returns the current request's `Locale` found by i18n middleware.
  929. // See `Tr` too.
  930. func (ctx *Context) GetLocale() Locale {
  931. // Cache the Locale itself for multiple calls of `Tr` method.
  932. contextKey := ctx.app.ConfigurationReadOnly().GetLocaleContextKey()
  933. if v := ctx.values.Get(contextKey); v != nil {
  934. if locale, ok := v.(Locale); ok {
  935. return locale
  936. }
  937. }
  938. if locale := ctx.app.I18nReadOnly().GetLocale(ctx); locale != nil {
  939. ctx.values.Set(contextKey, locale)
  940. return locale
  941. }
  942. return nil
  943. }
  944. // Tr returns a i18n localized message based on format with optional arguments.
  945. // See `GetLocale` too.
  946. //
  947. // Example: https://github.com/kataras/iris/tree/master/_examples/i18n
  948. func (ctx *Context) Tr(message string, values ...interface{}) string { // other name could be: Localize.
  949. if locale := ctx.GetLocale(); locale != nil { // TODO: here... I need to change the logic, if not found then call the i18n's get locale and set the value in order to be fastest on routes that are not using (no need to reigster a middleware.)
  950. return locale.GetMessage(message, values...)
  951. }
  952. return message
  953. }
  954. // +------------------------------------------------------------+
  955. // | Response Headers helpers |
  956. // +------------------------------------------------------------+
  957. // Header adds a header to the response, if value is empty
  958. // it removes the header by its name.
  959. func (ctx *Context) Header(name string, value string) {
  960. if value == "" {
  961. ctx.writer.Header().Del(name)
  962. return
  963. }
  964. ctx.writer.Header().Add(name, value)
  965. }
  966. const contentTypeContextKey = "iris.content_type"
  967. func shouldAppendCharset(cType string) bool {
  968. if idx := strings.IndexRune(cType, '/'); idx > 1 && len(cType) > idx+1 {
  969. typ := cType[0:idx]
  970. if typ == "application" {
  971. switch cType[idx+1:] {
  972. case "json", "xml", "yaml", "problem+json", "problem+xml":
  973. return true
  974. default:
  975. return false
  976. }
  977. }
  978. }
  979. return true
  980. }
  981. func (ctx *Context) contentTypeOnce(cType string, charset string) {
  982. if charset == "" {
  983. charset = ctx.app.ConfigurationReadOnly().GetCharset()
  984. }
  985. if shouldAppendCharset(cType) {
  986. cType += "; charset=" + charset
  987. }
  988. ctx.values.Set(contentTypeContextKey, cType)
  989. ctx.writer.Header().Set(ContentTypeHeaderKey, cType)
  990. }
  991. // ContentType sets the response writer's
  992. // header "Content-Type" to the 'cType'.
  993. func (ctx *Context) ContentType(cType string) {
  994. if cType == "" {
  995. return
  996. }
  997. if _, wroteOnce := ctx.values.GetEntry(contentTypeContextKey); wroteOnce {
  998. return
  999. }
  1000. // 1. if it's path or a filename or an extension,
  1001. // then take the content type from that
  1002. if strings.Contains(cType, ".") {
  1003. ext := filepath.Ext(cType)
  1004. cType = mime.TypeByExtension(ext)
  1005. }
  1006. // if doesn't contain a charset already then append it
  1007. if !strings.Contains(cType, "charset") {
  1008. if shouldAppendCharset(cType) {
  1009. cType += "; charset=" + ctx.app.ConfigurationReadOnly().GetCharset()
  1010. }
  1011. }
  1012. ctx.writer.Header().Set(ContentTypeHeaderKey, cType)
  1013. }
  1014. // GetContentType returns the response writer's
  1015. // header value of "Content-Type".
  1016. func (ctx *Context) GetContentType() string {
  1017. return ctx.writer.Header().Get(ContentTypeHeaderKey)
  1018. }
  1019. // GetContentTypeRequested returns the request's
  1020. // trim-ed(without the charset and priority values)
  1021. // header value of "Content-Type".
  1022. func (ctx *Context) GetContentTypeRequested() string {
  1023. return TrimHeaderValue(ctx.GetHeader(ContentTypeHeaderKey))
  1024. }
  1025. // GetContentLength returns the request's
  1026. // header value of "Content-Length".
  1027. func (ctx *Context) GetContentLength() int64 {
  1028. if v := ctx.GetHeader(ContentLengthHeaderKey); v != "" {
  1029. n, _ := strconv.ParseInt(v, 10, 64)
  1030. return n
  1031. }
  1032. return 0
  1033. }
  1034. // StatusCode sets the status code header to the response.
  1035. // Look .GetStatusCode & .FireStatusCode too.
  1036. //
  1037. // Remember, the last one before .Write matters except recorder and transactions.
  1038. func (ctx *Context) StatusCode(statusCode int) {
  1039. ctx.writer.WriteHeader(statusCode)
  1040. }
  1041. // NotFound emits an error 404 to the client, using the specific custom error error handler.
  1042. // Note that you may need to call ctx.StopExecution() if you don't want the next handlers
  1043. // to be executed. Next handlers are being executed on iris because you can alt the
  1044. // error code and change it to a more specific one, i.e
  1045. // users := app.Party("/users")
  1046. // users.Done(func(ctx iris.Context){ if ctx.StatusCode() == 400 { /* custom error code for /users */ }})
  1047. func (ctx *Context) NotFound() {
  1048. ctx.StatusCode(http.StatusNotFound)
  1049. }
  1050. // GetStatusCode returns the current status code of the response.
  1051. // Look StatusCode too.
  1052. func (ctx *Context) GetStatusCode() int {
  1053. return ctx.writer.StatusCode()
  1054. }
  1055. // +------------------------------------------------------------+
  1056. // | Various Request and Post Data |
  1057. // +------------------------------------------------------------+
  1058. // URLParamExists returns true if the url parameter exists, otherwise false.
  1059. func (ctx *Context) URLParamExists(name string) bool {
  1060. if q := ctx.request.URL.Query(); q != nil {
  1061. _, exists := q[name]
  1062. return exists
  1063. }
  1064. return false
  1065. }
  1066. // URLParamDefault returns the get parameter from a request, if not found then "def" is returned.
  1067. func (ctx *Context) URLParamDefault(name string, def string) string {
  1068. if v := ctx.request.URL.Query().Get(name); v != "" {
  1069. return v
  1070. }
  1071. return def
  1072. }
  1073. // URLParam returns the get parameter from a request, if any.
  1074. func (ctx *Context) URLParam(name string) string {
  1075. return ctx.URLParamDefault(name, "")
  1076. }
  1077. // URLParamTrim returns the url query parameter with trailing white spaces removed from a request.
  1078. func (ctx *Context) URLParamTrim(name string) string {
  1079. return strings.TrimSpace(ctx.URLParam(name))
  1080. }
  1081. // URLParamEscape returns the escaped url query parameter from a request.
  1082. func (ctx *Context) URLParamEscape(name string) string {
  1083. return DecodeQuery(ctx.URLParam(name))
  1084. }
  1085. // ErrNotFound is the type error which API users can make use of
  1086. // to check if a `Context` action of a `Handler` is type of Not Found,
  1087. // e.g. URL Query Parameters.
  1088. // Example:
  1089. //
  1090. // n, err := context.URLParamInt("url_query_param_name")
  1091. // if errors.Is(err, context.ErrNotFound) {
  1092. // // [handle error...]
  1093. // }
  1094. // Another usage would be `err == context.ErrNotFound`
  1095. // HOWEVER prefer use the new `errors.Is` as API details may change in the future.
  1096. var ErrNotFound = errors.New("not found")
  1097. // URLParamInt returns the url query parameter as int value from a request,
  1098. // returns -1 and an error if parse failed or not found.
  1099. func (ctx *Context) URLParamInt(name string) (int, error) {
  1100. if v := ctx.URLParam(name); v != "" {
  1101. n, err := strconv.Atoi(v)
  1102. if err != nil {
  1103. return -1, err
  1104. }
  1105. return n, nil
  1106. }
  1107. return -1, ErrNotFound
  1108. }
  1109. // URLParamIntDefault returns the url query parameter as int value from a request,
  1110. // if not found or parse failed then "def" is returned.
  1111. func (ctx *Context) URLParamIntDefault(name string, def int) int {
  1112. v, err := ctx.URLParamInt(name)
  1113. if err != nil {
  1114. return def
  1115. }
  1116. return v
  1117. }
  1118. // URLParamInt32Default returns the url query parameter as int32 value from a request,
  1119. // if not found or parse failed then "def" is returned.
  1120. func (ctx *Context) URLParamInt32Default(name string, def int32) int32 {
  1121. if v := ctx.URLParam(name); v != "" {
  1122. n, err := strconv.ParseInt(v, 10, 32)
  1123. if err != nil {
  1124. return def
  1125. }
  1126. return int32(n)
  1127. }
  1128. return def
  1129. }
  1130. // URLParamInt64 returns the url query parameter as int64 value from a request,
  1131. // returns -1 and an error if parse failed or not found.
  1132. func (ctx *Context) URLParamInt64(name string) (int64, error) {
  1133. if v := ctx.URLParam(name); v != "" {
  1134. n, err := strconv.ParseInt(v, 10, 64)
  1135. if err != nil {
  1136. return -1, err
  1137. }
  1138. return n, nil
  1139. }
  1140. return -1, ErrNotFound
  1141. }
  1142. // URLParamInt64Default returns the url query parameter as int64 value from a request,
  1143. // if not found or parse failed then "def" is returned.
  1144. func (ctx *Context) URLParamInt64Default(name string, def int64) int64 {
  1145. v, err := ctx.URLParamInt64(name)
  1146. if err != nil {
  1147. return def
  1148. }
  1149. return v
  1150. }
  1151. // URLParamFloat64 returns the url query parameter as float64 value from a request,
  1152. // returns an error and -1 if parse failed.
  1153. func (ctx *Context) URLParamFloat64(name string) (float64, error) {
  1154. if v := ctx.URLParam(name); v != "" {
  1155. n, err := strconv.ParseFloat(v, 64)
  1156. if err != nil {
  1157. return -1, err
  1158. }
  1159. return n, nil
  1160. }
  1161. return -1, ErrNotFound
  1162. }
  1163. // URLParamFloat64Default returns the url query parameter as float64 value from a request,
  1164. // if not found or parse failed then "def" is returned.
  1165. func (ctx *Context) URLParamFloat64Default(name string, def float64) float64 {
  1166. v, err := ctx.URLParamFloat64(name)
  1167. if err != nil {
  1168. return def
  1169. }
  1170. return v
  1171. }
  1172. // URLParamBool returns the url query parameter as boolean value from a request,
  1173. // returns an error if parse failed.
  1174. func (ctx *Context) URLParamBool(name string) (bool, error) {
  1175. return strconv.ParseBool(ctx.URLParam(name))
  1176. }
  1177. // URLParams returns a map of GET query parameters separated by comma if more than one
  1178. // it returns an empty map if nothing found.
  1179. func (ctx *Context) URLParams() map[string]string {
  1180. q := ctx.request.URL.Query()
  1181. values := make(map[string]string, len(q))
  1182. for k, v := range q {
  1183. values[k] = strings.Join(v, ",")
  1184. }
  1185. return values
  1186. }
  1187. // No need anymore, net/http checks for the Form already.
  1188. // func (ctx *Context) askParseForm() error {
  1189. // if ctx.request.Form == nil {
  1190. // if err := ctx.request.ParseForm(); err != nil {
  1191. // return err
  1192. // }
  1193. // }
  1194. // return nil
  1195. // }
  1196. // FormValueDefault returns a single parsed form value by its "name",
  1197. // including both the URL field's query parameters and the POST or PUT form data.
  1198. //
  1199. // Returns the "def" if not found.
  1200. func (ctx *Context) FormValueDefault(name string, def string) string {
  1201. if form, has := ctx.form(); has {
  1202. if v := form[name]; len(v) > 0 {
  1203. return v[0]
  1204. }
  1205. }
  1206. return def
  1207. }
  1208. // FormValueDefault retruns a single parsed form value.
  1209. func FormValueDefault(r *http.Request, name string, def string, postMaxMemory int64, resetBody bool) string {
  1210. if form, has := GetForm(r, postMaxMemory, resetBody); has {
  1211. if v := form[name]; len(v) > 0 {
  1212. return v[0]
  1213. }
  1214. }
  1215. return def
  1216. }
  1217. // FormValue returns a single parsed form value by its "name",
  1218. // including both the URL field's query parameters and the POST or PUT form data.
  1219. func (ctx *Context) FormValue(name string) string {
  1220. return ctx.FormValueDefault(name, "")
  1221. }
  1222. // FormValues returns the parsed form data, including both the URL
  1223. // field's query parameters and the POST or PUT form data.
  1224. //
  1225. // The default form's memory maximum size is 32MB, it can be changed by the
  1226. // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
  1227. // NOTE: A check for nil is necessary.
  1228. func (ctx *Context) FormValues() map[string][]string {
  1229. form, _ := ctx.form()
  1230. return form
  1231. }
  1232. // Form contains the parsed form data, including both the URL
  1233. // field's query parameters and the POST or PUT form data.
  1234. func (ctx *Context) form() (form map[string][]string, found bool) {
  1235. return GetForm(ctx.request, ctx.app.ConfigurationReadOnly().GetPostMaxMemory(), ctx.app.ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal())
  1236. }
  1237. // GetForm returns the request form (url queries, post or multipart) values.
  1238. func GetForm(r *http.Request, postMaxMemory int64, resetBody bool) (form map[string][]string, found bool) {
  1239. /*
  1240. net/http/request.go#1219
  1241. for k, v := range f.Value {
  1242. r.Form[k] = append(r.Form[k], v...)
  1243. // r.PostForm should also be populated. See Issue 9305.
  1244. r.PostForm[k] = append(r.PostForm[k], v...)
  1245. }
  1246. */
  1247. if form := r.Form; len(form) > 0 {
  1248. return form, true
  1249. }
  1250. if form := r.PostForm; len(form) > 0 {
  1251. return form, true
  1252. }
  1253. if m := r.MultipartForm; m != nil {
  1254. if len(m.Value) > 0 {
  1255. return m.Value, true
  1256. }
  1257. }
  1258. var bodyCopy []byte
  1259. if resetBody {
  1260. // on POST, PUT and PATCH it will read the form values from request body otherwise from URL queries.
  1261. if m := r.Method; m == "POST" || m == "PUT" || m == "PATCH" {
  1262. bodyCopy, _ = GetBody(r, resetBody)
  1263. if len(bodyCopy) == 0 {
  1264. return nil, false
  1265. }
  1266. // r.Body = ioutil.NopCloser(io.TeeReader(r.Body, buf))
  1267. } else {
  1268. resetBody = false
  1269. }
  1270. }
  1271. // ParseMultipartForm calls `request.ParseForm` automatically
  1272. // therefore we don't need to call it here, although it doesn't hurt.
  1273. // After one call to ParseMultipartForm or ParseForm,
  1274. // subsequent calls have no effect, are idempotent.
  1275. err := r.ParseMultipartForm(postMaxMemory)
  1276. if resetBody {
  1277. r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyCopy))
  1278. }
  1279. if err != nil && err != http.ErrNotMultipart {
  1280. return nil, false
  1281. }
  1282. if form := r.Form; len(form) > 0 {
  1283. return form, true
  1284. }
  1285. if form := r.PostForm; len(form) > 0 {
  1286. return form, true
  1287. }
  1288. if m := r.MultipartForm; m != nil {
  1289. if len(m.Value) > 0 {
  1290. return m.Value, true
  1291. }
  1292. }
  1293. return nil, false
  1294. }
  1295. // PostValueDefault returns the parsed form data from POST, PATCH,
  1296. // or PUT body parameters based on a "name".
  1297. //
  1298. // If not found then "def" is returned instead.
  1299. func (ctx *Context) PostValueDefault(name string, def string) string {
  1300. ctx.form()
  1301. if v := ctx.request.PostForm[name]; len(v) > 0 {
  1302. return v[0]
  1303. }
  1304. return def
  1305. }
  1306. // PostValue returns the parsed form data from POST, PATCH,
  1307. // or PUT body parameters based on a "name"
  1308. func (ctx *Context) PostValue(name string) string {
  1309. return ctx.PostValueDefault(name, "")
  1310. }
  1311. // PostValueTrim returns the parsed form data from POST, PATCH,
  1312. // or PUT body parameters based on a "name", without trailing spaces.
  1313. func (ctx *Context) PostValueTrim(name string) string {
  1314. return strings.TrimSpace(ctx.PostValue(name))
  1315. }
  1316. // PostValueInt returns the parsed form data from POST, PATCH,
  1317. // or PUT body parameters based on a "name", as int.
  1318. //
  1319. // If not found returns -1 and a non-nil error.
  1320. func (ctx *Context) PostValueInt(name string) (int, error) {
  1321. v := ctx.PostValue(name)
  1322. if v == "" {
  1323. return -1, ErrNotFound
  1324. }
  1325. return strconv.Atoi(v)
  1326. }
  1327. // PostValueIntDefault returns the parsed form data from POST, PATCH,
  1328. // or PUT body parameters based on a "name", as int.
  1329. //
  1330. // If not found or parse errors returns the "def".
  1331. func (ctx *Context) PostValueIntDefault(name string, def int) int {
  1332. if v, err := ctx.PostValueInt(name); err == nil {
  1333. return v
  1334. }
  1335. return def
  1336. }
  1337. // PostValueInt64 returns the parsed form data from POST, PATCH,
  1338. // or PUT body parameters based on a "name", as float64.
  1339. //
  1340. // If not found returns -1 and a non-nil error.
  1341. func (ctx *Context) PostValueInt64(name string) (int64, error) {
  1342. v := ctx.PostValue(name)
  1343. if v == "" {
  1344. return -1, ErrNotFound
  1345. }
  1346. return strconv.ParseInt(v, 10, 64)
  1347. }
  1348. // PostValueInt64Default returns the parsed form data from POST, PATCH,
  1349. // or PUT body parameters based on a "name", as int64.
  1350. //
  1351. // If not found or parse errors returns the "def".
  1352. func (ctx *Context) PostValueInt64Default(name string, def int64) int64 {
  1353. if v, err := ctx.PostValueInt64(name); err == nil {
  1354. return v
  1355. }
  1356. return def
  1357. }
  1358. // PostValueFloat64 returns the parsed form data from POST, PATCH,
  1359. // or PUT body parameters based on a "name", as float64.
  1360. //
  1361. // If not found returns -1 and a non-nil error.
  1362. func (ctx *Context) PostValueFloat64(name string) (float64, error) {
  1363. v := ctx.PostValue(name)
  1364. if v == "" {
  1365. return -1, ErrNotFound
  1366. }
  1367. return strconv.ParseFloat(v, 64)
  1368. }
  1369. // PostValueFloat64Default returns the parsed form data from POST, PATCH,
  1370. // or PUT body parameters based on a "name", as float64.
  1371. //
  1372. // If not found or parse errors returns the "def".
  1373. func (ctx *Context) PostValueFloat64Default(name string, def float64) float64 {
  1374. if v, err := ctx.PostValueFloat64(name); err == nil {
  1375. return v
  1376. }
  1377. return def
  1378. }
  1379. // PostValueBool returns the parsed form data from POST, PATCH,
  1380. // or PUT body parameters based on a "name", as bool.
  1381. //
  1382. // If not found or value is false, then it returns false, otherwise true.
  1383. func (ctx *Context) PostValueBool(name string) (bool, error) {
  1384. v := ctx.PostValue(name)
  1385. if v == "" {
  1386. return false, ErrNotFound
  1387. }
  1388. return strconv.ParseBool(v)
  1389. }
  1390. // PostValues returns all the parsed form data from POST, PATCH,
  1391. // or PUT body parameters based on a "name" as a string slice.
  1392. //
  1393. // The default form's memory maximum size is 32MB, it can be changed by the
  1394. // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
  1395. func (ctx *Context) PostValues(name string) []string {
  1396. ctx.form()
  1397. return ctx.request.PostForm[name]
  1398. }
  1399. // FormFile returns the first uploaded file that received from the client.
  1400. //
  1401. //
  1402. // The default form's memory maximum size is 32MB, it can be changed by the
  1403. // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
  1404. //
  1405. // Example: https://github.com/kataras/iris/tree/master/_examples/file-server/upload-file
  1406. func (ctx *Context) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
  1407. // we don't have access to see if the request is body stream
  1408. // and then the ParseMultipartForm can be useless
  1409. // here but do it in order to apply the post limit,
  1410. // the internal request.FormFile will not do it if that's filled
  1411. // and it's not a stream body.
  1412. if err := ctx.request.ParseMultipartForm(ctx.app.ConfigurationReadOnly().GetPostMaxMemory()); err != nil {
  1413. return nil, nil, err
  1414. }
  1415. return ctx.request.FormFile(key)
  1416. }
  1417. // UploadFormFiles uploads any received file(s) from the client
  1418. // to the system physical location "destDirectory".
  1419. //
  1420. // The second optional argument "before" gives caller the chance to
  1421. // modify the *miltipart.FileHeader before saving to the disk,
  1422. // it can be used to change a file's name based on the current request,
  1423. // all FileHeader's options can be changed. You can ignore it if
  1424. // you don't need to use this capability before saving a file to the disk.
  1425. //
  1426. // Note that it doesn't check if request body streamed.
  1427. //
  1428. // Returns the copied length as int64 and
  1429. // a not nil error if at least one new file
  1430. // can't be created due to the operating system's permissions or
  1431. // http.ErrMissingFile if no file received.
  1432. //
  1433. // If you want to receive & accept files and manage them manually you can use the `context#FormFile`
  1434. // instead and create a copy function that suits your needs, the below is for generic usage.
  1435. //
  1436. // The default form's memory maximum size is 32MB, it can be changed by the
  1437. // `iris#WithPostMaxMemory` configurator at main configuration passed on `app.Run`'s second argument.
  1438. //
  1439. // See `FormFile` to a more controlled to receive a file.
  1440. //
  1441. // Example: https://github.com/kataras/iris/tree/master/_examples/file-server/upload-files
  1442. func (ctx *Context) UploadFormFiles(destDirectory string, before ...func(*Context, *multipart.FileHeader)) (n int64, err error) {
  1443. err = ctx.request.ParseMultipartForm(ctx.app.ConfigurationReadOnly().GetPostMaxMemory())
  1444. if err != nil {
  1445. return 0, err
  1446. }
  1447. if ctx.request.MultipartForm != nil {
  1448. if fhs := ctx.request.MultipartForm.File; fhs != nil {
  1449. for _, files := range fhs {
  1450. for _, file := range files {
  1451. for _, b := range before {
  1452. b(ctx, file)
  1453. }
  1454. n0, err0 := uploadTo(file, destDirectory)
  1455. if err0 != nil {
  1456. return 0, err0
  1457. }
  1458. n += n0
  1459. }
  1460. }
  1461. return n, nil
  1462. }
  1463. }
  1464. return 0, http.ErrMissingFile
  1465. }
  1466. func uploadTo(fh *multipart.FileHeader, destDirectory string) (int64, error) {
  1467. src, err := fh.Open()
  1468. if err != nil {
  1469. return 0, err
  1470. }
  1471. defer src.Close()
  1472. out, err := os.OpenFile(filepath.Join(destDirectory, fh.Filename),
  1473. os.O_WRONLY|os.O_CREATE, os.FileMode(0666))
  1474. if err != nil {
  1475. return 0, err
  1476. }
  1477. defer out.Close()
  1478. return io.Copy(out, src)
  1479. }
  1480. // AbsoluteURI parses the "s" and returns its absolute URI form.
  1481. func (ctx *Context) AbsoluteURI(s string) string {
  1482. if s == "" {
  1483. return ""
  1484. }
  1485. if s[0] == '/' {
  1486. scheme := ctx.request.URL.Scheme
  1487. if scheme == "" {
  1488. if ctx.request.TLS != nil {
  1489. scheme = "https:"
  1490. } else {
  1491. scheme = "http:"
  1492. }
  1493. }
  1494. host := ctx.Host()
  1495. return scheme + "//" + host + path.Clean(s)
  1496. }
  1497. if u, err := url.Parse(s); err == nil {
  1498. r := ctx.request
  1499. if u.Scheme == "" && u.Host == "" {
  1500. oldpath := r.URL.Path
  1501. if oldpath == "" {
  1502. oldpath = "/"
  1503. }
  1504. if s == "" || s[0] != '/' {
  1505. olddir, _ := path.Split(oldpath)
  1506. s = olddir + s
  1507. }
  1508. var query string
  1509. if i := strings.Index(s, "?"); i != -1 {
  1510. s, query = s[:i], s[i:]
  1511. }
  1512. // clean up but preserve trailing slash
  1513. trailing := strings.HasSuffix(s, "/")
  1514. s = path.Clean(s)
  1515. if trailing && !strings.HasSuffix(s, "/") {
  1516. s += "/"
  1517. }
  1518. s += query
  1519. }
  1520. }
  1521. return s
  1522. }
  1523. // Redirect sends a redirect response to the client
  1524. // to a specific url or relative path.
  1525. // accepts 2 parameters string and an optional int
  1526. // first parameter is the url to redirect
  1527. // second parameter is the http status should send,
  1528. // default is 302 (StatusFound),
  1529. // you can set it to 301 (Permant redirect)
  1530. // or 303 (StatusSeeOther) if POST method,
  1531. // or StatusTemporaryRedirect(307) if that's nessecery.
  1532. func (ctx *Context) Redirect(urlToRedirect string, statusHeader ...int) {
  1533. ctx.StopExecution()
  1534. // get the previous status code given by the end-developer.
  1535. status := ctx.GetStatusCode()
  1536. if status < 300 { // the previous is not a RCF-valid redirect status.
  1537. status = 0
  1538. }
  1539. if len(statusHeader) > 0 {
  1540. // check if status code is passed via receivers.
  1541. if s := statusHeader[0]; s > 0 {
  1542. status = s
  1543. }
  1544. }
  1545. if status == 0 {
  1546. // if status remains zero then default it.
  1547. // a 'temporary-redirect-like' which works better than for our purpose
  1548. status = http.StatusFound
  1549. }
  1550. http.Redirect(ctx.writer, ctx.request, urlToRedirect, status)
  1551. }
  1552. // +------------------------------------------------------------+
  1553. // | Body Readers |
  1554. // +------------------------------------------------------------+
  1555. // SetMaxRequestBodySize sets a limit to the request body size
  1556. // should be called before reading the request body from the client.
  1557. func (ctx *Context) SetMaxRequestBodySize(limitOverBytes int64) {
  1558. ctx.request.Body = http.MaxBytesReader(ctx.writer, ctx.request.Body, limitOverBytes)
  1559. }
  1560. // GetBody reads and returns the request body.
  1561. // The default behavior for the http request reader is to consume the data readen
  1562. // but you can change that behavior by passing the `WithoutBodyConsumptionOnUnmarshal` iris option.
  1563. //
  1564. // However, whenever you can use the `ctx.Request().Body` instead.
  1565. func (ctx *Context) GetBody() ([]byte, error) {
  1566. return GetBody(ctx.request, ctx.app.ConfigurationReadOnly().GetDisableBodyConsumptionOnUnmarshal())
  1567. }
  1568. // GetBody reads and returns the request body.
  1569. func GetBody(r *http.Request, resetBody bool) ([]byte, error) {
  1570. data, err := ioutil.ReadAll(r.Body)
  1571. if err != nil {
  1572. return nil, err
  1573. }
  1574. if resetBody {
  1575. // * remember, Request.Body has no Bytes(), we have to consume them first
  1576. // and after re-set them to the body, this is the only solution.
  1577. r.Body = ioutil.NopCloser(bytes.NewBuffer(data))
  1578. }
  1579. return data, nil
  1580. }
  1581. // Validator is the validator for request body on Context methods such as
  1582. // ReadJSON, ReadMsgPack, ReadXML, ReadYAML, ReadForm, ReadQuery, ReadBody and e.t.c.
  1583. type Validator interface {
  1584. Struct(interface{}) error
  1585. // If community asks for more than a struct validation on JSON, XML, MsgPack, Form, Query and e.t.c
  1586. // then we should add more methods here, alternative approach would be to have a
  1587. // `Validator:Validate(interface{}) error` and a map[reflect.Kind]Validator instead.
  1588. }
  1589. // UnmarshalBody reads the request's body and binds it to a value or pointer of any type
  1590. // Examples of usage: context.ReadJSON, context.ReadXML.
  1591. //
  1592. // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-custom-via-unmarshaler/main.go
  1593. func (ctx *Context) UnmarshalBody(outPtr interface{}, unmarshaler Unmarshaler) error {
  1594. if ctx.request.Body == nil {
  1595. return fmt.Errorf("unmarshal: empty body: %w", ErrNotFound)
  1596. }
  1597. rawData, err := ctx.GetBody()
  1598. if err != nil {
  1599. return err
  1600. }
  1601. // check if the v contains its own decode
  1602. // in this case the v should be a pointer also,
  1603. // but this is up to the user's custom Decode implementation*
  1604. //
  1605. // See 'BodyDecoder' for more.
  1606. if decoder, isDecoder := outPtr.(BodyDecoder); isDecoder {
  1607. return decoder.Decode(rawData)
  1608. }
  1609. // // check if v is already a pointer, if yes then pass as it's
  1610. // if reflect.TypeOf(v).Kind() == reflect.Ptr {
  1611. // return unmarshaler.Unmarshal(rawData, v)
  1612. // } <- no need for that, ReadJSON is documented enough to receive a pointer,
  1613. // we don't need to reduce the performance here by using the reflect.TypeOf method.
  1614. // f the v doesn't contains a self-body decoder use the custom unmarshaler to bind the body.
  1615. err = unmarshaler.Unmarshal(rawData, outPtr)
  1616. if err != nil {
  1617. return err
  1618. }
  1619. return ctx.app.Validate(outPtr)
  1620. }
  1621. func (ctx *Context) shouldOptimize() bool {
  1622. return ctx.app.ConfigurationReadOnly().GetEnableOptimizations()
  1623. }
  1624. // ReadJSON reads JSON from request's body and binds it to a value of any json-valid type.
  1625. //
  1626. // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-json/main.go
  1627. func (ctx *Context) ReadJSON(outPtr interface{}) error {
  1628. unmarshaler := json.Unmarshal
  1629. if ctx.shouldOptimize() {
  1630. unmarshaler = jsoniter.Unmarshal
  1631. }
  1632. return ctx.UnmarshalBody(outPtr, UnmarshalerFunc(unmarshaler))
  1633. }
  1634. // ReadXML reads XML from request's body and binds it to a value of any xml-valid type.
  1635. //
  1636. // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-xml/main.go
  1637. func (ctx *Context) ReadXML(outPtr interface{}) error {
  1638. return ctx.UnmarshalBody(outPtr, UnmarshalerFunc(xml.Unmarshal))
  1639. }
  1640. // ReadYAML reads YAML from request's body and binds it to the "outPtr" value.
  1641. //
  1642. // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-yaml/main.go
  1643. func (ctx *Context) ReadYAML(outPtr interface{}) error {
  1644. return ctx.UnmarshalBody(outPtr, UnmarshalerFunc(yaml.Unmarshal))
  1645. }
  1646. // IsErrPath can be used at `context#ReadForm` and `context#ReadQuery`.
  1647. // It reports whether the incoming error
  1648. // can be ignored when server allows unknown post values to be sent by the client.
  1649. //
  1650. // A shortcut for the `schema#IsErrPath`.
  1651. var IsErrPath = schema.IsErrPath
  1652. // ErrEmptyForm is returned by `context#ReadForm` and `context#ReadBody`
  1653. // when it should read data from a request form data but there is none.
  1654. var ErrEmptyForm = errors.New("empty form")
  1655. // ReadForm binds the request body of a form to the "formObject".
  1656. // It supports any kind of type, including custom structs.
  1657. // It will return nothing if request data are empty.
  1658. // The struct field tag is "form".
  1659. // Note that it will return nil error on empty form data if `Configuration.FireEmptyFormError`
  1660. // is false (as defaulted) in this case the caller should check the pointer to
  1661. // see if something was actually binded.
  1662. //
  1663. // If a client sent an unknown field, this method will return an error,
  1664. // in order to ignore that error use the `err != nil && !iris.IsErrPath(err)`.
  1665. //
  1666. // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-form/main.go
  1667. func (ctx *Context) ReadForm(formObject interface{}) error {
  1668. values := ctx.FormValues()
  1669. if len(values) == 0 {
  1670. if ctx.app.ConfigurationReadOnly().GetFireEmptyFormError() {
  1671. return ErrEmptyForm
  1672. }
  1673. return nil
  1674. }
  1675. err := schema.DecodeForm(values, formObject)
  1676. if err != nil {
  1677. return err
  1678. }
  1679. return ctx.app.Validate(formObject)
  1680. }
  1681. // ReadQuery binds url query to "ptr". The struct field tag is "url".
  1682. //
  1683. // Example: https://github.com/kataras/iris/blob/master/_examples/request-body/read-query/main.go
  1684. func (ctx *Context) ReadQuery(ptr interface{}) error {
  1685. values := ctx.request.URL.Query()
  1686. if len(values) == 0 {
  1687. return nil
  1688. }
  1689. err := schema.DecodeQuery(values, ptr)
  1690. if err != nil {
  1691. return err
  1692. }
  1693. return ctx.app.Validate(ptr)
  1694. }
  1695. // ReadProtobuf binds the body to the "ptr" of a proto Message and returns any error.
  1696. // Look `ReadJSONProtobuf` too.
  1697. func (ctx *Context) ReadProtobuf(ptr proto.Message) error {
  1698. rawData, err := ctx.GetBody()
  1699. if err != nil {
  1700. return err
  1701. }
  1702. return proto.Unmarshal(rawData, ptr)
  1703. }
  1704. // ProtoUnmarshalOptions is a type alias for protojson.UnmarshalOptions.
  1705. type ProtoUnmarshalOptions = protojson.UnmarshalOptions
  1706. var defaultProtobufUnmarshalOptions ProtoUnmarshalOptions
  1707. // ReadJSONProtobuf reads a JSON body request into the given "ptr" proto.Message.
  1708. // Look `ReadProtobuf` too.
  1709. func (ctx *Context) ReadJSONProtobuf(ptr proto.Message, opts ...ProtoUnmarshalOptions) error {
  1710. rawData, err := ctx.GetBody()
  1711. if err != nil {
  1712. return err
  1713. }
  1714. opt := defaultProtobufUnmarshalOptions
  1715. if len(opts) > 0 {
  1716. opt = opts[1]
  1717. }
  1718. return opt.Unmarshal(rawData, ptr)
  1719. }
  1720. // ReadMsgPack binds the request body of msgpack format to the "ptr" and returns any error.
  1721. func (ctx *Context) ReadMsgPack(ptr interface{}) error {
  1722. rawData, err := ctx.GetBody()
  1723. if err != nil {
  1724. return err
  1725. }
  1726. err = msgpack.Unmarshal(rawData, ptr)
  1727. if err != nil {
  1728. return err
  1729. }
  1730. return ctx.app.Validate(ptr)
  1731. }
  1732. // ReadBody binds the request body to the "ptr" depending on the HTTP Method and the Request's Content-Type.
  1733. // If a GET method request then it reads from a form (or URL Query), otherwise
  1734. // it tries to match (depending on the request content-type) the data format e.g.
  1735. // JSON, Protobuf, MsgPack, XML, YAML, MultipartForm and binds the result to the "ptr".
  1736. func (ctx *Context) ReadBody(ptr interface{}) error {
  1737. if ctx.Method() == http.MethodGet {
  1738. if ctx.Request().URL.RawQuery != "" {
  1739. // try read from query.
  1740. return ctx.ReadQuery(ptr)
  1741. }
  1742. // otherwise use the ReadForm,
  1743. // it's actually the same except
  1744. // ReadQuery will not fire errors on:
  1745. // 1. unknown or empty url query parameters
  1746. // 2. empty query or form (if FireEmptyFormError is enabled).
  1747. return ctx.ReadForm(ptr)
  1748. }
  1749. switch ctx.GetContentTypeRequested() {
  1750. case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:
  1751. return ctx.ReadXML(ptr)
  1752. case ContentYAMLHeaderValue:
  1753. return ctx.ReadYAML(ptr)
  1754. case ContentFormHeaderValue, ContentFormMultipartHeaderValue:
  1755. return ctx.ReadForm(ptr)
  1756. case ContentJSONHeaderValue:
  1757. return ctx.ReadJSON(ptr)
  1758. case ContentProtobufHeaderValue:
  1759. msg, ok := ptr.(proto.Message)
  1760. if !ok {
  1761. return ErrContentNotSupported
  1762. }
  1763. return ctx.ReadProtobuf(msg)
  1764. case ContentMsgPackHeaderValue, ContentMsgPack2HeaderValue:
  1765. return ctx.ReadMsgPack(ptr)
  1766. default:
  1767. if ctx.Request().URL.RawQuery != "" {
  1768. // try read from query.
  1769. return ctx.ReadQuery(ptr)
  1770. }
  1771. // otherwise default to JSON.
  1772. return ctx.ReadJSON(ptr)
  1773. }
  1774. }
  1775. // +------------------------------------------------------------+
  1776. // | Body (raw) Writers |
  1777. // +------------------------------------------------------------+
  1778. // Write writes the data to the connection as part of an HTTP reply.
  1779. //
  1780. // If WriteHeader has not yet been called, Write calls
  1781. // WriteHeader(http.StatusOK) before writing the data. If the Header
  1782. // does not contain a Content-Type line, Write adds a Content-Type set
  1783. // to the result of passing the initial 512 bytes of written data to
  1784. // DetectContentType.
  1785. //
  1786. // Depending on the HTTP protocol version and the client, calling
  1787. // Write or WriteHeader may prevent future reads on the
  1788. // Request.Body. For HTTP/1.x requests, handlers should read any
  1789. // needed request body data before writing the response. Once the
  1790. // headers have been flushed (due to either an explicit Flusher.Flush
  1791. // call or writing enough data to trigger a flush), the request body
  1792. // may be unavailable. For HTTP/2 requests, the Go HTTP server permits
  1793. // handlers to continue to read the request body while concurrently
  1794. // writing the response. However, such behavior may not be supported
  1795. // by all HTTP/2 clients. Handlers should read before writing if
  1796. // possible to maximize compatibility.
  1797. func (ctx *Context) Write(rawBody []byte) (int, error) {
  1798. return ctx.writer.Write(rawBody)
  1799. }
  1800. // Writef formats according to a format specifier and writes to the response.
  1801. //
  1802. // Returns the number of bytes written and any write error encountered.
  1803. func (ctx *Context) Writef(format string, a ...interface{}) (n int, err error) {
  1804. return fmt.Fprintf(ctx.writer, format, a...)
  1805. }
  1806. // WriteString writes a simple string to the response.
  1807. //
  1808. // Returns the number of bytes written and any write error encountered.
  1809. func (ctx *Context) WriteString(body string) (n int, err error) {
  1810. return io.WriteString(ctx.writer, body)
  1811. }
  1812. const (
  1813. // ContentTypeHeaderKey is the header key of "Content-Type".
  1814. ContentTypeHeaderKey = "Content-Type"
  1815. // LastModifiedHeaderKey is the header key of "Last-Modified".
  1816. LastModifiedHeaderKey = "Last-Modified"
  1817. // IfModifiedSinceHeaderKey is the header key of "If-Modified-Since".
  1818. IfModifiedSinceHeaderKey = "If-Modified-Since"
  1819. // CacheControlHeaderKey is the header key of "Cache-Control".
  1820. CacheControlHeaderKey = "Cache-Control"
  1821. // ETagHeaderKey is the header key of "ETag".
  1822. ETagHeaderKey = "ETag"
  1823. // ContentDispositionHeaderKey is the header key of "Content-Disposition".
  1824. ContentDispositionHeaderKey = "Content-Disposition"
  1825. // ContentLengthHeaderKey is the header key of "Content-Length"
  1826. ContentLengthHeaderKey = "Content-Length"
  1827. // ContentEncodingHeaderKey is the header key of "Content-Encoding".
  1828. ContentEncodingHeaderKey = "Content-Encoding"
  1829. // GzipHeaderValue is the header value of "gzip".
  1830. GzipHeaderValue = "gzip"
  1831. // AcceptEncodingHeaderKey is the header key of "Accept-Encoding".
  1832. AcceptEncodingHeaderKey = "Accept-Encoding"
  1833. // VaryHeaderKey is the header key of "Vary".
  1834. VaryHeaderKey = "Vary"
  1835. )
  1836. var unixEpochTime = time.Unix(0, 0)
  1837. // IsZeroTime reports whether t is obviously unspecified (either zero or Unix()=0).
  1838. func IsZeroTime(t time.Time) bool {
  1839. return t.IsZero() || t.Equal(unixEpochTime)
  1840. }
  1841. // ParseTime parses a time header (such as the Date: header),
  1842. // trying each forth formats (or three if Application's configuration's TimeFormat is defaulted)
  1843. // that are allowed by HTTP/1.1:
  1844. // Application's configuration's TimeFormat or/and http.TimeFormat,
  1845. // time.RFC850, and time.ANSIC.
  1846. //
  1847. // Look `context#FormatTime` for the opossite operation (Time to string).
  1848. var ParseTime = func(ctx *Context, text string) (t time.Time, err error) {
  1849. t, err = time.Parse(ctx.Application().ConfigurationReadOnly().GetTimeFormat(), text)
  1850. if err != nil {
  1851. return http.ParseTime(text)
  1852. }
  1853. return
  1854. }
  1855. // FormatTime returns a textual representation of the time value formatted
  1856. // according to the Application's configuration's TimeFormat field
  1857. // which defines the format.
  1858. //
  1859. // Look `context#ParseTime` for the opossite operation (string to Time).
  1860. var FormatTime = func(ctx *Context, t time.Time) string {
  1861. return t.Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat())
  1862. }
  1863. // SetLastModified sets the "Last-Modified" based on the "modtime" input.
  1864. // If "modtime" is zero then it does nothing.
  1865. //
  1866. // It's mostly internally on core/router and context packages.
  1867. func (ctx *Context) SetLastModified(modtime time.Time) {
  1868. if !IsZeroTime(modtime) {
  1869. ctx.Header(LastModifiedHeaderKey, FormatTime(ctx, modtime.UTC())) // or modtime.UTC()?
  1870. }
  1871. }
  1872. // ErrPreconditionFailed may be returned from `Context` methods
  1873. // that has to perform one or more client side preconditions before the actual check, e.g. `CheckIfModifiedSince`.
  1874. // Usage:
  1875. // ok, err := context.CheckIfModifiedSince(modTime)
  1876. // if err != nil {
  1877. // if errors.Is(err, context.ErrPreconditionFailed) {
  1878. // [handle missing client conditions,such as not valid request method...]
  1879. // }else {
  1880. // [the error is probably a time parse error...]
  1881. // }
  1882. // }
  1883. var ErrPreconditionFailed = errors.New("precondition failed")
  1884. // CheckIfModifiedSince checks if the response is modified since the "modtime".
  1885. // Note that it has nothing to do with server-side caching.
  1886. // It does those checks by checking if the "If-Modified-Since" request header
  1887. // sent by client or a previous server response header
  1888. // (e.g with WriteWithExpiration or HandleDir or Favicon etc.)
  1889. // is a valid one and it's before the "modtime".
  1890. //
  1891. // A check for !modtime && err == nil is necessary to make sure that
  1892. // it's not modified since, because it may return false but without even
  1893. // had the chance to check the client-side (request) header due to some errors,
  1894. // like the HTTP Method is not "GET" or "HEAD" or if the "modtime" is zero
  1895. // or if parsing time from the header failed. See `ErrPreconditionFailed` too.
  1896. //
  1897. // It's mostly used internally, e.g. `context#WriteWithExpiration`.
  1898. func (ctx *Context) CheckIfModifiedSince(modtime time.Time) (bool, error) {
  1899. if method := ctx.Method(); method != http.MethodGet && method != http.MethodHead {
  1900. return false, fmt.Errorf("method: %w", ErrPreconditionFailed)
  1901. }
  1902. ims := ctx.GetHeader(IfModifiedSinceHeaderKey)
  1903. if ims == "" || IsZeroTime(modtime) {
  1904. return false, fmt.Errorf("zero time: %w", ErrPreconditionFailed)
  1905. }
  1906. t, err := ParseTime(ctx, ims)
  1907. if err != nil {
  1908. return false, err
  1909. }
  1910. // sub-second precision, so
  1911. // use mtime < t+1s instead of mtime <= t to check for unmodified.
  1912. if modtime.UTC().Before(t.Add(1 * time.Second)) {
  1913. return false, nil
  1914. }
  1915. return true, nil
  1916. }
  1917. // WriteNotModified sends a 304 "Not Modified" status code to the client,
  1918. // it makes sure that the content type, the content length headers
  1919. // and any "ETag" are removed before the response sent.
  1920. //
  1921. // It's mostly used internally on core/router/fs.go and context methods.
  1922. func (ctx *Context) WriteNotModified() {
  1923. // RFC 7232 section 4.1:
  1924. // a sender SHOULD NOT generate representation metadata other than the
  1925. // above listed fields unless said metadata exists for the purpose of
  1926. // guiding cache updates (e.g.," Last-Modified" might be useful if the
  1927. // response does not have an ETag field).
  1928. h := ctx.ResponseWriter().Header()
  1929. delete(h, ContentTypeHeaderKey)
  1930. delete(h, ContentLengthHeaderKey)
  1931. if h.Get(ETagHeaderKey) != "" {
  1932. delete(h, LastModifiedHeaderKey)
  1933. }
  1934. ctx.StatusCode(http.StatusNotModified)
  1935. }
  1936. // WriteWithExpiration works like `Write` but it will check if a resource is modified,
  1937. // based on the "modtime" input argument,
  1938. // otherwise sends a 304 status code in order to let the client-side render the cached content.
  1939. func (ctx *Context) WriteWithExpiration(body []byte, modtime time.Time) (int, error) {
  1940. if modified, err := ctx.CheckIfModifiedSince(modtime); !modified && err == nil {
  1941. ctx.WriteNotModified()
  1942. return 0, nil
  1943. }
  1944. ctx.SetLastModified(modtime)
  1945. return ctx.writer.Write(body)
  1946. }
  1947. // StreamWriter registers the given stream writer for populating
  1948. // response body.
  1949. //
  1950. // Access to context's and/or its' members is forbidden from writer.
  1951. //
  1952. // This function may be used in the following cases:
  1953. //
  1954. // * if response body is too big (more than iris.LimitRequestBodySize(if set)).
  1955. // * if response body is streamed from slow external sources.
  1956. // * if response body must be streamed to the client in chunks.
  1957. // (aka `http server push`).
  1958. func (ctx *Context) StreamWriter(writer func(w io.Writer) error) error {
  1959. cancelCtx := ctx.Request().Context()
  1960. notifyClosed := cancelCtx.Done()
  1961. for {
  1962. select {
  1963. // response writer forced to close, exit.
  1964. case <-notifyClosed:
  1965. return cancelCtx.Err()
  1966. default:
  1967. if err := writer(ctx.writer); err != nil {
  1968. return err
  1969. }
  1970. ctx.writer.Flush()
  1971. }
  1972. }
  1973. }
  1974. // +------------------------------------------------------------+
  1975. // | Body Writers with compression |
  1976. // +------------------------------------------------------------+
  1977. // ClientSupportsEncoding reports whether the
  1978. // client expects one of the given "encodings" compression.
  1979. //
  1980. // Note, this method just reports back the first valid encoding it sees,
  1981. // meaning that request accept-encoding offers don't matter here.
  1982. // See `CompressWriter` too.
  1983. func (ctx *Context) ClientSupportsEncoding(encodings ...string) bool {
  1984. if len(encodings) == 0 {
  1985. return false
  1986. }
  1987. if h := ctx.GetHeader(AcceptEncodingHeaderKey); h != "" {
  1988. for _, v := range strings.Split(h, ",") {
  1989. for _, encoding := range encodings {
  1990. if strings.Contains(v, encoding) {
  1991. return true
  1992. }
  1993. }
  1994. }
  1995. }
  1996. return false
  1997. }
  1998. // CompressWriter enables or disables the compress response writer.
  1999. // if the client expects a valid compression algorithm then this
  2000. // will change the response writer to a compress writer instead.
  2001. // All future write and rich write methods will respect this option.
  2002. // Usage:
  2003. // app.Use(func(ctx iris.Context){
  2004. // err := ctx.CompressWriter(true)
  2005. // ctx.Next()
  2006. // })
  2007. // The recommendation is to compress data as much as possible and therefore to use this field,
  2008. // but some types of resources, such as jpeg images, are already compressed.
  2009. // Sometimes, using additional compression doesn't reduce payload size and
  2010. // can even make the payload longer.
  2011. func (ctx *Context) CompressWriter(enable bool) error {
  2012. switch w := ctx.writer.(type) {
  2013. case *CompressResponseWriter:
  2014. if enable {
  2015. return nil
  2016. }
  2017. w.Disabled = true
  2018. case *ResponseRecorder:
  2019. if enable {
  2020. // Keep the Recorder as ctx.writer.
  2021. // Wrap the existing net/http response writer
  2022. // with the compressed writer and
  2023. // replace the recorder's response writer
  2024. // reference with that compressed one.
  2025. // Fixes an issue when Record is called before CompressWriter.
  2026. cw, err := AcquireCompressResponseWriter(w.ResponseWriter, ctx.request, -1)
  2027. if err != nil {
  2028. return err
  2029. }
  2030. w.ResponseWriter = cw
  2031. } else {
  2032. cw, ok := w.ResponseWriter.(*CompressResponseWriter)
  2033. if ok {
  2034. cw.Disabled = true
  2035. }
  2036. }
  2037. default:
  2038. if !enable {
  2039. return nil
  2040. }
  2041. cw, err := AcquireCompressResponseWriter(w, ctx.request, -1)
  2042. if err != nil {
  2043. return err
  2044. }
  2045. ctx.writer = cw
  2046. }
  2047. return nil
  2048. }
  2049. // CompressReader accepts a boolean, which, if set to true
  2050. // it wraps the request body reader with a reader which decompresses request data before read.
  2051. // If the "enable" input argument is false then the request body will reset to the default one.
  2052. //
  2053. // Useful when incoming request data are compressed.
  2054. // All future calls of `ctx.GetBody/ReadXXX/UnmarshalBody` methods will respect this option.
  2055. //
  2056. // Usage:
  2057. // app.Use(func(ctx iris.Context){
  2058. // err := ctx.CompressReader(true)
  2059. // ctx.Next()
  2060. // })
  2061. // More:
  2062. // if cr, ok := ctx.Request().Body.(*CompressReader); ok {
  2063. // cr.Src // the original request body
  2064. // cr.Encoding // the compression algorithm.
  2065. // }
  2066. //
  2067. // It returns `ErrRequestNotCompressed` if client's request data are not compressed
  2068. // (or empty)
  2069. // or `ErrNotSupportedCompression` if server missing the decompression algorithm.
  2070. func (ctx *Context) CompressReader(enable bool) error {
  2071. cr, ok := ctx.request.Body.(*CompressReader)
  2072. if enable {
  2073. if ok {
  2074. // already called.
  2075. return nil
  2076. }
  2077. encoding := ctx.GetHeader(ContentEncodingHeaderKey)
  2078. if encoding == IDENTITY {
  2079. // no transformation whatsoever, return nil error and
  2080. // don't wrap the body reader.
  2081. return nil
  2082. }
  2083. r, err := NewCompressReader(ctx.request.Body, encoding)
  2084. if err != nil {
  2085. return err
  2086. }
  2087. ctx.request.Body = r
  2088. } else {
  2089. if ok {
  2090. ctx.request.Body = cr.Src
  2091. }
  2092. }
  2093. return nil
  2094. }
  2095. // +------------------------------------------------------------+
  2096. // | Rich Body Content Writers/Renderers |
  2097. // +------------------------------------------------------------+
  2098. // ViewEngine registers a view engine for the current chain of handlers.
  2099. // It overrides any previously registered engines, including the application's root ones.
  2100. // Note that, because performance is everything,
  2101. // the "engine" MUST be already ready-to-use,
  2102. // meaning that its `Load` method should be called once before this method call.
  2103. //
  2104. // To register a view engine per-group of groups too see `Party.RegisterView` instead.
  2105. func (ctx *Context) ViewEngine(engine ViewEngine) {
  2106. ctx.values.Set(ctx.app.ConfigurationReadOnly().GetViewEngineContextKey(), engine)
  2107. }
  2108. // ViewLayout sets the "layout" option if and when .View
  2109. // is being called afterwards, in the same request.
  2110. // Useful when need to set or/and change a layout based on the previous handlers in the chain.
  2111. //
  2112. // Note that the 'layoutTmplFile' argument can be set to iris.NoLayout
  2113. // to disable the layout for a specific view render action,
  2114. // it disables the engine's configuration's layout property.
  2115. //
  2116. // Look .ViewData and .View too.
  2117. //
  2118. // Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
  2119. func (ctx *Context) ViewLayout(layoutTmplFile string) {
  2120. ctx.values.Set(ctx.app.ConfigurationReadOnly().GetViewLayoutContextKey(), layoutTmplFile)
  2121. }
  2122. // ViewData saves one or more key-value pair in order to be passed if and when .View
  2123. // is being called afterwards, in the same request.
  2124. // Useful when need to set or/and change template data from previous hanadlers in the chain.
  2125. //
  2126. // If .View's "binding" argument is not nil and it's not a type of map
  2127. // then these data are being ignored, binding has the priority, so the main route's handler can still decide.
  2128. // If binding is a map or iris.Map then these data are being added to the view data
  2129. // and passed to the template.
  2130. //
  2131. // After .View, the data are not destroyed, in order to be re-used if needed (again, in the same request as everything else),
  2132. // to clear the view data, developers can call:
  2133. // ctx.Set(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey(), nil)
  2134. //
  2135. // If 'key' is empty then the value is added as it's (struct or map) and developer is unable to add other value.
  2136. //
  2137. // Look .ViewLayout and .View too.
  2138. //
  2139. // Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
  2140. func (ctx *Context) ViewData(key string, value interface{}) {
  2141. viewDataContextKey := ctx.app.ConfigurationReadOnly().GetViewDataContextKey()
  2142. if key == "" {
  2143. ctx.values.Set(viewDataContextKey, value)
  2144. return
  2145. }
  2146. v := ctx.values.Get(viewDataContextKey)
  2147. if v == nil {
  2148. ctx.values.Set(viewDataContextKey, Map{key: value})
  2149. return
  2150. }
  2151. if data, ok := v.(map[string]interface{}); ok {
  2152. data[key] = value
  2153. } else if data, ok := v.(Map); ok {
  2154. data[key] = value
  2155. }
  2156. }
  2157. // GetViewData returns the values registered by `context#ViewData`.
  2158. // The return value is `map[string]interface{}`, this means that
  2159. // if a custom struct registered to ViewData then this function
  2160. // will try to parse it to map, if failed then the return value is nil
  2161. // A check for nil is always a good practise if different
  2162. // kind of values or no data are registered via `ViewData`.
  2163. //
  2164. // Similarly to `viewData := ctx.Values().Get("iris.view.data")` or
  2165. // `viewData := ctx.Values().Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())`.
  2166. func (ctx *Context) GetViewData() map[string]interface{} {
  2167. viewDataContextKey := ctx.app.ConfigurationReadOnly().GetViewDataContextKey()
  2168. v := ctx.values.Get(viewDataContextKey)
  2169. // if no values found, then return nil
  2170. if v == nil {
  2171. return nil
  2172. }
  2173. // if struct, convert it to map[string]interface{}
  2174. if structs.IsStruct(v) {
  2175. return structs.Map(v)
  2176. }
  2177. // if pure map[string]interface{}
  2178. if viewData, ok := v.(map[string]interface{}); ok {
  2179. return viewData
  2180. }
  2181. // if context#Map
  2182. if viewData, ok := v.(Map); ok {
  2183. return viewData
  2184. }
  2185. // if failure, then return nil
  2186. return nil
  2187. }
  2188. // View renders a template based on the registered view engine(s).
  2189. // First argument accepts the filename, relative to the view engine's Directory and Extension,
  2190. // i.e: if directory is "./templates" and want to render the "./templates/users/index.html"
  2191. // then you pass the "users/index.html" as the filename argument.
  2192. //
  2193. // The second optional argument can receive a single "view model"
  2194. // that will be binded to the view template if it's not nil,
  2195. // otherwise it will check for previous view data stored by the `ViewData`
  2196. // even if stored at any previous handler(middleware) for the same request.
  2197. //
  2198. // Look .ViewData and .ViewLayout too.
  2199. //
  2200. // Examples: https://github.com/kataras/iris/tree/master/_examples/view
  2201. func (ctx *Context) View(filename string, optionalViewModel ...interface{}) error {
  2202. ctx.ContentType(ContentHTMLHeaderValue)
  2203. cfg := ctx.app.ConfigurationReadOnly()
  2204. layout := ctx.values.GetString(cfg.GetViewLayoutContextKey())
  2205. var bindingData interface{}
  2206. if len(optionalViewModel) > 0 {
  2207. // a nil can override the existing data or model sent by `ViewData`.
  2208. bindingData = optionalViewModel[0]
  2209. } else {
  2210. bindingData = ctx.values.Get(cfg.GetViewDataContextKey())
  2211. }
  2212. if key := cfg.GetViewEngineContextKey(); key != "" {
  2213. if engineV := ctx.values.Get(key); engineV != nil {
  2214. if engine, ok := engineV.(ViewEngine); ok {
  2215. err := engine.ExecuteWriter(ctx, filename, layout, bindingData)
  2216. if err != nil {
  2217. ctx.app.Logger().Errorf("View [%v] [%T]: %v", ctx.getLogIdentifier(), engine, err)
  2218. return err
  2219. }
  2220. return nil
  2221. }
  2222. }
  2223. }
  2224. err := ctx.app.View(ctx, filename, layout, bindingData) // if failed it logs the error.
  2225. if err != nil {
  2226. ctx.StopWithStatus(http.StatusInternalServerError)
  2227. }
  2228. return err
  2229. }
  2230. // getLogIdentifier returns the ID, or the client remote IP address,
  2231. // useful for internal logging of context's method failure.
  2232. func (ctx *Context) getLogIdentifier() interface{} {
  2233. if id := ctx.GetID(); id != nil {
  2234. return id
  2235. }
  2236. return ctx.RemoteAddr()
  2237. }
  2238. const (
  2239. // ContentBinaryHeaderValue header value for binary data.
  2240. ContentBinaryHeaderValue = "application/octet-stream"
  2241. // ContentWebassemblyHeaderValue header value for web assembly files.
  2242. ContentWebassemblyHeaderValue = "application/wasm"
  2243. // ContentHTMLHeaderValue is the string of text/html response header's content type value.
  2244. ContentHTMLHeaderValue = "text/html"
  2245. // ContentJSONHeaderValue header value for JSON data.
  2246. ContentJSONHeaderValue = "application/json"
  2247. // ContentJSONProblemHeaderValue header value for JSON API problem error.
  2248. // Read more at: https://tools.ietf.org/html/rfc7807
  2249. ContentJSONProblemHeaderValue = "application/problem+json"
  2250. // ContentXMLProblemHeaderValue header value for XML API problem error.
  2251. // Read more at: https://tools.ietf.org/html/rfc7807
  2252. ContentXMLProblemHeaderValue = "application/problem+xml"
  2253. // ContentJavascriptHeaderValue header value for JSONP & Javascript data.
  2254. ContentJavascriptHeaderValue = "text/javascript"
  2255. // ContentTextHeaderValue header value for Text data.
  2256. ContentTextHeaderValue = "text/plain"
  2257. // ContentXMLHeaderValue header value for XML data.
  2258. ContentXMLHeaderValue = "text/xml"
  2259. // ContentXMLUnreadableHeaderValue obselete header value for XML.
  2260. ContentXMLUnreadableHeaderValue = "application/xml"
  2261. // ContentMarkdownHeaderValue custom key/content type, the real is the text/html.
  2262. ContentMarkdownHeaderValue = "text/markdown"
  2263. // ContentYAMLHeaderValue header value for YAML data.
  2264. ContentYAMLHeaderValue = "application/x-yaml"
  2265. // ContentProtobufHeaderValue header value for Protobuf messages data.
  2266. ContentProtobufHeaderValue = "application/x-protobuf"
  2267. // ContentMsgPackHeaderValue header value for MsgPack data.
  2268. ContentMsgPackHeaderValue = "application/msgpack"
  2269. // ContentMsgPack2HeaderValue alternative header value for MsgPack data.
  2270. ContentMsgPack2HeaderValue = "application/x-msgpack"
  2271. // ContentFormHeaderValue header value for post form data.
  2272. ContentFormHeaderValue = "application/x-www-form-urlencoded"
  2273. // ContentFormMultipartHeaderValue header value for post multipart form data.
  2274. ContentFormMultipartHeaderValue = "multipart/form-data"
  2275. // ContentGRPCHeaderValue Content-Type header value for gRPC.
  2276. ContentGRPCHeaderValue = "application/grpc"
  2277. )
  2278. // Binary writes out the raw bytes as binary data.
  2279. func (ctx *Context) Binary(data []byte) (int, error) {
  2280. ctx.ContentType(ContentBinaryHeaderValue)
  2281. return ctx.Write(data)
  2282. }
  2283. // Text writes out a string as plain text.
  2284. func (ctx *Context) Text(format string, args ...interface{}) (int, error) {
  2285. ctx.ContentType(ContentTextHeaderValue)
  2286. return ctx.Writef(format, args...)
  2287. }
  2288. // HTML writes out a string as text/html.
  2289. func (ctx *Context) HTML(format string, args ...interface{}) (int, error) {
  2290. ctx.ContentType(ContentHTMLHeaderValue)
  2291. return ctx.Writef(format, args...)
  2292. }
  2293. // ProtoMarshalOptions is a type alias for protojson.MarshalOptions.
  2294. type ProtoMarshalOptions = protojson.MarshalOptions
  2295. // JSON contains the options for the JSON (Context's) Renderer.
  2296. type JSON struct {
  2297. // http-specific
  2298. StreamingJSON bool
  2299. // content-specific
  2300. UnescapeHTML bool
  2301. Indent string
  2302. Prefix string
  2303. ASCII bool // if true writes with unicode to ASCII content.
  2304. Secure bool // if true then it prepends a "while(1);" when Go slice (to JSON Array) value.
  2305. // proto.Message specific marshal options.
  2306. Proto ProtoMarshalOptions
  2307. }
  2308. // JSONP contains the options for the JSONP (Context's) Renderer.
  2309. type JSONP struct {
  2310. // content-specific
  2311. Indent string
  2312. Callback string
  2313. }
  2314. // XML contains the options for the XML (Context's) Renderer.
  2315. type XML struct {
  2316. // content-specific
  2317. Indent string
  2318. Prefix string
  2319. }
  2320. // Markdown contains the options for the Markdown (Context's) Renderer.
  2321. type Markdown struct {
  2322. // content-specific
  2323. Sanitize bool
  2324. }
  2325. var (
  2326. newLineB = []byte("\n")
  2327. // the html codes for unescaping.
  2328. ltHex = []byte("\\u003c")
  2329. lt = []byte("<")
  2330. gtHex = []byte("\\u003e")
  2331. gt = []byte(">")
  2332. andHex = []byte("\\u0026")
  2333. and = []byte("&")
  2334. // secure JSON.
  2335. jsonArrayPrefix = []byte("[")
  2336. jsonArraySuffix = []byte("]")
  2337. secureJSONPrefix = []byte("while(1);")
  2338. )
  2339. // WriteJSON marshals the given interface object and writes the JSON response to the 'writer'.
  2340. // Ignores StatusCode and StreamingJSON options.
  2341. func WriteJSON(writer io.Writer, v interface{}, options JSON, optimize bool) (int, error) {
  2342. var (
  2343. result []byte
  2344. err error
  2345. )
  2346. if m, ok := v.(proto.Message); ok {
  2347. result, err = options.Proto.Marshal(m)
  2348. if err != nil {
  2349. return 0, err
  2350. }
  2351. return writer.Write(result)
  2352. }
  2353. if !optimize && options.Indent == "" {
  2354. options.Indent = " "
  2355. }
  2356. if indent := options.Indent; indent != "" {
  2357. marshalIndent := json.MarshalIndent
  2358. if optimize {
  2359. marshalIndent = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent
  2360. }
  2361. result, err = marshalIndent(v, "", indent)
  2362. result = append(result, newLineB...)
  2363. } else {
  2364. marshal := json.Marshal
  2365. if optimize {
  2366. marshal = jsoniter.ConfigCompatibleWithStandardLibrary.Marshal
  2367. }
  2368. result, err = marshal(v)
  2369. }
  2370. if err != nil {
  2371. return 0, err
  2372. }
  2373. if options.UnescapeHTML {
  2374. result = bytes.Replace(result, ltHex, lt, -1)
  2375. result = bytes.Replace(result, gtHex, gt, -1)
  2376. result = bytes.Replace(result, andHex, and, -1)
  2377. }
  2378. if options.Secure {
  2379. if bytes.HasPrefix(result, jsonArrayPrefix) && bytes.HasSuffix(result, jsonArraySuffix) {
  2380. result = append(secureJSONPrefix, result...)
  2381. }
  2382. }
  2383. if options.ASCII {
  2384. if len(result) > 0 {
  2385. buf := new(bytes.Buffer)
  2386. for _, s := range bytesToString(result) {
  2387. char := string(s)
  2388. if s >= 128 {
  2389. char = fmt.Sprintf("\\u%04x", int64(s))
  2390. }
  2391. buf.WriteString(char)
  2392. }
  2393. result = buf.Bytes()
  2394. }
  2395. }
  2396. if prefix := options.Prefix; prefix != "" {
  2397. result = append(stringToBytes(prefix), result...)
  2398. }
  2399. return writer.Write(result)
  2400. }
  2401. // See https://golang.org/src/strings/builder.go#L45
  2402. func bytesToString(b []byte) string {
  2403. return *(*string)(unsafe.Pointer(&b))
  2404. }
  2405. func stringToBytes(s string) []byte {
  2406. return *(*[]byte)(unsafe.Pointer(&s))
  2407. }
  2408. // DefaultJSONOptions is the optional settings that are being used
  2409. // inside `ctx.JSON`.
  2410. var DefaultJSONOptions = JSON{}
  2411. // JSON marshals the given interface object and writes the JSON response to the client.
  2412. // If the value is a compatible `proto.Message` one
  2413. // then it only uses the options.Proto settings to marshal.
  2414. func (ctx *Context) JSON(v interface{}, opts ...JSON) (n int, err error) {
  2415. options := DefaultJSONOptions
  2416. if len(opts) > 0 {
  2417. options = opts[0]
  2418. }
  2419. ctx.ContentType(ContentJSONHeaderValue)
  2420. if options.StreamingJSON {
  2421. if ctx.shouldOptimize() {
  2422. jsoniterConfig := jsoniter.Config{
  2423. EscapeHTML: !options.UnescapeHTML,
  2424. IndentionStep: 4,
  2425. }.Froze()
  2426. enc := jsoniterConfig.NewEncoder(ctx.writer)
  2427. err = enc.Encode(v)
  2428. } else {
  2429. enc := json.NewEncoder(ctx.writer)
  2430. enc.SetEscapeHTML(!options.UnescapeHTML)
  2431. enc.SetIndent(options.Prefix, options.Indent)
  2432. err = enc.Encode(v)
  2433. }
  2434. if err != nil {
  2435. ctx.app.Logger().Debugf("JSON: %v", err)
  2436. ctx.StatusCode(http.StatusInternalServerError) // it handles the fallback to normal mode here which also removes any compression headers.
  2437. return 0, err
  2438. }
  2439. return ctx.writer.Written(), err
  2440. }
  2441. n, err = WriteJSON(ctx.writer, v, options, ctx.shouldOptimize())
  2442. if err != nil {
  2443. ctx.app.Logger().Debugf("JSON: %v", err)
  2444. ctx.StatusCode(http.StatusInternalServerError)
  2445. return 0, err
  2446. }
  2447. return n, err
  2448. }
  2449. var finishCallbackB = []byte(");")
  2450. // WriteJSONP marshals the given interface object and writes the JSON response to the writer.
  2451. func WriteJSONP(writer io.Writer, v interface{}, options JSONP, optimize bool) (int, error) {
  2452. if callback := options.Callback; callback != "" {
  2453. n, err := writer.Write(stringToBytes(callback + "("))
  2454. if err != nil {
  2455. return n, err
  2456. }
  2457. defer writer.Write(finishCallbackB)
  2458. }
  2459. if !optimize && options.Indent == "" {
  2460. options.Indent = " "
  2461. }
  2462. if indent := options.Indent; indent != "" {
  2463. marshalIndent := json.MarshalIndent
  2464. if optimize {
  2465. marshalIndent = jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent
  2466. }
  2467. result, err := marshalIndent(v, "", indent)
  2468. if err != nil {
  2469. return 0, err
  2470. }
  2471. result = append(result, newLineB...)
  2472. return writer.Write(result)
  2473. }
  2474. marshal := json.Marshal
  2475. if optimize {
  2476. marshal = jsoniter.ConfigCompatibleWithStandardLibrary.Marshal
  2477. }
  2478. result, err := marshal(v)
  2479. if err != nil {
  2480. return 0, err
  2481. }
  2482. return writer.Write(result)
  2483. }
  2484. // DefaultJSONPOptions is the optional settings that are being used
  2485. // inside `ctx.JSONP`.
  2486. var DefaultJSONPOptions = JSONP{}
  2487. // JSONP marshals the given interface object and writes the JSON response to the client.
  2488. func (ctx *Context) JSONP(v interface{}, opts ...JSONP) (int, error) {
  2489. options := DefaultJSONPOptions
  2490. if len(opts) > 0 {
  2491. options = opts[0]
  2492. }
  2493. ctx.ContentType(ContentJavascriptHeaderValue)
  2494. n, err := WriteJSONP(ctx.writer, v, options, ctx.shouldOptimize())
  2495. if err != nil {
  2496. ctx.app.Logger().Debugf("JSONP: %v", err)
  2497. ctx.StatusCode(http.StatusInternalServerError)
  2498. return 0, err
  2499. }
  2500. return n, err
  2501. }
  2502. type xmlMapEntry struct {
  2503. XMLName xml.Name
  2504. Value interface{} `xml:",chardata"`
  2505. }
  2506. // XMLMap wraps a map[string]interface{} to compatible xml marshaler,
  2507. // in order to be able to render maps as XML on the `Context.XML` method.
  2508. //
  2509. // Example: `Context.XML(XMLMap("Root", map[string]interface{}{...})`.
  2510. func XMLMap(elementName string, v Map) xml.Marshaler {
  2511. return xmlMap{
  2512. entries: v,
  2513. elementName: elementName,
  2514. }
  2515. }
  2516. type xmlMap struct {
  2517. entries Map
  2518. elementName string
  2519. }
  2520. // MarshalXML marshals a map to XML.
  2521. func (m xmlMap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  2522. if len(m.entries) == 0 {
  2523. return nil
  2524. }
  2525. start.Name = xml.Name{Local: m.elementName}
  2526. err := e.EncodeToken(start)
  2527. if err != nil {
  2528. return err
  2529. }
  2530. for k, v := range m.entries {
  2531. err = e.Encode(xmlMapEntry{XMLName: xml.Name{Local: k}, Value: v})
  2532. if err != nil {
  2533. return err
  2534. }
  2535. }
  2536. return e.EncodeToken(start.End())
  2537. }
  2538. // WriteXML marshals the given interface object and writes the XML response to the writer.
  2539. func WriteXML(writer io.Writer, v interface{}, options XML, optimize bool) (int, error) {
  2540. if prefix := options.Prefix; prefix != "" {
  2541. n, err := writer.Write(stringToBytes(prefix))
  2542. if err != nil {
  2543. return n, err
  2544. }
  2545. }
  2546. if !optimize && options.Indent == "" {
  2547. options.Indent = " " // Two spaces for XML is the default indentation when not optimized.
  2548. }
  2549. if indent := options.Indent; indent != "" {
  2550. result, err := xml.MarshalIndent(v, "", indent)
  2551. if err != nil {
  2552. return 0, err
  2553. }
  2554. result = append(result, newLineB...)
  2555. return writer.Write(result)
  2556. }
  2557. result, err := xml.Marshal(v)
  2558. if err != nil {
  2559. return 0, err
  2560. }
  2561. return writer.Write(result)
  2562. }
  2563. // DefaultXMLOptions is the optional settings that are being used
  2564. // from `ctx.XML`.
  2565. var DefaultXMLOptions = XML{}
  2566. // XML marshals the given interface object and writes the XML response to the client.
  2567. // To render maps as XML see the `XMLMap` package-level function.
  2568. func (ctx *Context) XML(v interface{}, opts ...XML) (int, error) {
  2569. options := DefaultXMLOptions
  2570. if len(opts) > 0 {
  2571. options = opts[0]
  2572. }
  2573. ctx.ContentType(ContentXMLHeaderValue)
  2574. n, err := WriteXML(ctx.writer, v, options, ctx.shouldOptimize())
  2575. if err != nil {
  2576. ctx.app.Logger().Debugf("XML: %v", err)
  2577. ctx.StatusCode(http.StatusInternalServerError)
  2578. return 0, err
  2579. }
  2580. return n, err
  2581. }
  2582. // Problem writes a JSON or XML problem response.
  2583. // Order of Problem fields are not always rendered the same.
  2584. //
  2585. // Behaves exactly like `Context.JSON`
  2586. // but with default ProblemOptions.JSON indent of " " and
  2587. // a response content type of "application/problem+json" instead.
  2588. //
  2589. // Use the options.RenderXML and XML fields to change this behavior and
  2590. // send a response of content type "application/problem+xml" instead.
  2591. //
  2592. // Read more at: https://github.com/kataras/iris/wiki/Routing-error-handlers
  2593. func (ctx *Context) Problem(v interface{}, opts ...ProblemOptions) (int, error) {
  2594. options := DefaultProblemOptions
  2595. if len(opts) > 0 {
  2596. options = opts[0]
  2597. // Currently apply only if custom options passsed, otherwise,
  2598. // with the current settings, it's not required.
  2599. // This may change in the future though.
  2600. options.Apply(ctx)
  2601. }
  2602. if p, ok := v.(Problem); ok {
  2603. // if !p.Validate() {
  2604. // ctx.StatusCode(http.StatusInternalServerError)
  2605. // return ErrNotValidProblem
  2606. // }
  2607. p.updateURIsToAbs(ctx)
  2608. code, _ := p.getStatus()
  2609. if code == 0 { // get the current status code and set it to the problem.
  2610. code = ctx.GetStatusCode()
  2611. ctx.StatusCode(code)
  2612. } else {
  2613. // send the problem's status code
  2614. ctx.StatusCode(code)
  2615. }
  2616. if options.RenderXML {
  2617. ctx.contentTypeOnce(ContentXMLProblemHeaderValue, "")
  2618. // Problem is an xml Marshaler already, don't use `XMLMap`.
  2619. return ctx.XML(v, options.XML)
  2620. }
  2621. }
  2622. ctx.contentTypeOnce(ContentJSONProblemHeaderValue, "")
  2623. return ctx.JSON(v, options.JSON)
  2624. }
  2625. // WriteMarkdown parses the markdown to html and writes these contents to the writer.
  2626. func WriteMarkdown(writer io.Writer, markdownB []byte, options Markdown) (int, error) {
  2627. buf := blackfriday.Run(markdownB)
  2628. if options.Sanitize {
  2629. buf = bluemonday.UGCPolicy().SanitizeBytes(buf)
  2630. }
  2631. return writer.Write(buf)
  2632. }
  2633. // DefaultMarkdownOptions is the optional settings that are being used
  2634. // from `WriteMarkdown` and `ctx.Markdown`.
  2635. var DefaultMarkdownOptions = Markdown{}
  2636. // Markdown parses the markdown to html and renders its result to the client.
  2637. func (ctx *Context) Markdown(markdownB []byte, opts ...Markdown) (int, error) {
  2638. options := DefaultMarkdownOptions
  2639. if len(opts) > 0 {
  2640. options = opts[0]
  2641. }
  2642. ctx.ContentType(ContentHTMLHeaderValue)
  2643. n, err := WriteMarkdown(ctx.writer, markdownB, options)
  2644. if err != nil {
  2645. ctx.app.Logger().Debugf("Markdown: %v", err)
  2646. ctx.StatusCode(http.StatusInternalServerError)
  2647. return 0, err
  2648. }
  2649. return n, err
  2650. }
  2651. // YAML marshals the "v" using the yaml marshaler and renders its result to the client.
  2652. func (ctx *Context) YAML(v interface{}) (int, error) {
  2653. out, err := yaml.Marshal(v)
  2654. if err != nil {
  2655. ctx.app.Logger().Debugf("YAML: %v", err)
  2656. ctx.StatusCode(http.StatusInternalServerError)
  2657. return 0, err
  2658. }
  2659. ctx.ContentType(ContentYAMLHeaderValue)
  2660. return ctx.Write(out)
  2661. }
  2662. // Protobuf parses the "v" of proto Message and renders its result to the client.
  2663. func (ctx *Context) Protobuf(v proto.Message) (int, error) {
  2664. out, err := proto.Marshal(v)
  2665. if err != nil {
  2666. return 0, err
  2667. }
  2668. ctx.ContentType(ContentProtobufHeaderValue)
  2669. return ctx.Write(out)
  2670. }
  2671. // MsgPack parses the "v" of msgpack format and renders its result to the client.
  2672. func (ctx *Context) MsgPack(v interface{}) (int, error) {
  2673. out, err := msgpack.Marshal(v)
  2674. if err != nil {
  2675. return 0, err
  2676. }
  2677. ctx.ContentType(ContentMsgPackHeaderValue)
  2678. return ctx.Write(out)
  2679. }
  2680. // +-----------------------------------------------------------------------+
  2681. // | Content Νegotiation |
  2682. // | https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation | |
  2683. // +-----------------------------------------------------------------------+
  2684. // ErrContentNotSupported returns from the `Negotiate` method
  2685. // when server responds with 406.
  2686. var ErrContentNotSupported = errors.New("unsupported content")
  2687. // ContentSelector is the interface which structs can implement
  2688. // to manually choose a content based on the negotiated mime (content type).
  2689. // It can be passed to the `Context.Negotiate` method.
  2690. //
  2691. // See the `N` struct too.
  2692. type ContentSelector interface {
  2693. SelectContent(mime string) interface{}
  2694. }
  2695. // ContentNegotiator is the interface which structs can implement
  2696. // to override the `Context.Negotiate` default implementation and
  2697. // manually respond to the client based on a manuall call of `Context.Negotiation().Build()`
  2698. // to get the final negotiated mime and charset.
  2699. // It can be passed to the `Context.Negotiate` method.
  2700. type ContentNegotiator interface {
  2701. // mime and charset can be retrieved by:
  2702. // mime, charset := Context.Negotiation().Build()
  2703. // Pass this method to `Context.Negotiate` method
  2704. // to write custom content.
  2705. // Overriding the existing behavior of Context.Negotiate for selecting values based on
  2706. // content types, although it can accept any custom mime type with []byte.
  2707. // Content type is already set.
  2708. // Use it with caution, 99.9% you don't need this but it's here for extreme cases.
  2709. Negotiate(ctx *Context) (int, error)
  2710. }
  2711. // N is a struct which can be passed on the `Context.Negotiate` method.
  2712. // It contains fields which should be filled based on the `Context.Negotiation()`
  2713. // server side values. If no matched mime then its "Other" field will be sent,
  2714. // which should be a string or []byte.
  2715. // It completes the `ContentSelector` interface.
  2716. type N struct {
  2717. Text, HTML string
  2718. Markdown []byte
  2719. Binary []byte
  2720. JSON interface{}
  2721. Problem Problem
  2722. JSONP interface{}
  2723. XML interface{}
  2724. YAML interface{}
  2725. Protobuf interface{}
  2726. MsgPack interface{}
  2727. Other []byte // custom content types.
  2728. }
  2729. var _ ContentSelector = N{}
  2730. // SelectContent returns a content based on the matched negotiated "mime".
  2731. func (n N) SelectContent(mime string) interface{} {
  2732. switch mime {
  2733. case ContentTextHeaderValue:
  2734. return n.Text
  2735. case ContentHTMLHeaderValue:
  2736. return n.HTML
  2737. case ContentMarkdownHeaderValue:
  2738. return n.Markdown
  2739. case ContentBinaryHeaderValue:
  2740. return n.Binary
  2741. case ContentJSONHeaderValue:
  2742. return n.JSON
  2743. case ContentJSONProblemHeaderValue:
  2744. return n.Problem
  2745. case ContentJavascriptHeaderValue:
  2746. return n.JSONP
  2747. case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:
  2748. return n.XML
  2749. case ContentYAMLHeaderValue:
  2750. return n.YAML
  2751. case ContentProtobufHeaderValue:
  2752. return n.Protobuf
  2753. case ContentMsgPackHeaderValue, ContentMsgPack2HeaderValue:
  2754. return n.MsgPack
  2755. default:
  2756. return n.Other
  2757. }
  2758. }
  2759. const negotiationContextKey = "iris.negotiation_builder"
  2760. // Negotiation creates once and returns the negotiation builder
  2761. // to build server-side available prioritized content
  2762. // for specific content type(s), charset(s) and encoding algorithm(s).
  2763. //
  2764. // See `Negotiate` method too.
  2765. func (ctx *Context) Negotiation() *NegotiationBuilder {
  2766. if n := ctx.values.Get(negotiationContextKey); n != nil {
  2767. return n.(*NegotiationBuilder)
  2768. }
  2769. acceptBuilder := NegotiationAcceptBuilder{}
  2770. acceptBuilder.accept = parseHeader(ctx.GetHeader("Accept"))
  2771. acceptBuilder.charset = parseHeader(ctx.GetHeader("Accept-Charset"))
  2772. n := &NegotiationBuilder{Accept: acceptBuilder}
  2773. ctx.values.Set(negotiationContextKey, n)
  2774. return n
  2775. }
  2776. func parseHeader(headerValue string) []string {
  2777. in := strings.Split(headerValue, ",")
  2778. out := make([]string, 0, len(in))
  2779. for _, value := range in {
  2780. // remove any spaces and quality values such as ;q=0.8.
  2781. v := strings.TrimSpace(strings.Split(value, ";")[0])
  2782. if v != "" {
  2783. out = append(out, v)
  2784. }
  2785. }
  2786. return out
  2787. }
  2788. // Negotiate used for serving different representations of a resource at the same URI.
  2789. //
  2790. // The "v" can be a single `N` struct value.
  2791. // The "v" can be any value completes the `ContentSelector` interface.
  2792. // The "v" can be any value completes the `ContentNegotiator` interface.
  2793. // The "v" can be any value of struct(JSON, JSONP, XML, YAML, Protobuf, MsgPack) or
  2794. // string(TEXT, HTML) or []byte(Markdown, Binary) or []byte with any matched mime type.
  2795. //
  2796. // If the "v" is nil, the `Context.Negotitation()` builder's
  2797. // content will be used instead, otherwise "v" overrides builder's content
  2798. // (server mime types are still retrieved by its registered, supported, mime list)
  2799. //
  2800. // Set mime type priorities by `Negotiation().JSON().XML().HTML()...`.
  2801. // Set charset priorities by `Negotiation().Charset(...)`.
  2802. // Set encoding algorithm priorities by `Negotiation().Encoding(...)`.
  2803. // Modify the accepted by
  2804. // `Negotiation().Accept./Override()/.XML().JSON().Charset(...).Encoding(...)...`.
  2805. //
  2806. // It returns `ErrContentNotSupported` when not matched mime type(s).
  2807. //
  2808. // Resources:
  2809. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation
  2810. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept
  2811. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Charset
  2812. // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
  2813. //
  2814. // Supports the above without quality values.
  2815. //
  2816. // Read more at: https://github.com/kataras/iris/wiki/Content-negotiation
  2817. func (ctx *Context) Negotiate(v interface{}) (int, error) {
  2818. contentType, charset, encoding, content := ctx.Negotiation().Build()
  2819. if v == nil {
  2820. v = content
  2821. }
  2822. if contentType == "" {
  2823. // If the server cannot serve any matching set,
  2824. // it SHOULD send back a 406 (Not Acceptable) error code.
  2825. ctx.StatusCode(http.StatusNotAcceptable)
  2826. return -1, ErrContentNotSupported
  2827. }
  2828. if charset == "" {
  2829. charset = ctx.app.ConfigurationReadOnly().GetCharset()
  2830. }
  2831. if encoding != "" {
  2832. ctx.CompressWriter(true)
  2833. }
  2834. ctx.contentTypeOnce(contentType, charset)
  2835. if n, ok := v.(ContentNegotiator); ok {
  2836. return n.Negotiate(ctx)
  2837. }
  2838. if s, ok := v.(ContentSelector); ok {
  2839. v = s.SelectContent(contentType)
  2840. }
  2841. // switch v := value.(type) {
  2842. // case []byte:
  2843. // if contentType == ContentMarkdownHeaderValue {
  2844. // return ctx.Markdown(v)
  2845. // }
  2846. // return ctx.Write(v)
  2847. // case string:
  2848. // return ctx.WriteString(v)
  2849. // default:
  2850. // make it switch by content-type only, but we lose custom mime types capability that way:
  2851. // ^ solved with []byte on default case and
  2852. // ^ N.Other and
  2853. // ^ ContentSelector and ContentNegotiator interfaces.
  2854. switch contentType {
  2855. case ContentTextHeaderValue, ContentHTMLHeaderValue:
  2856. return ctx.WriteString(v.(string))
  2857. case ContentMarkdownHeaderValue:
  2858. return ctx.Markdown(v.([]byte))
  2859. case ContentJSONHeaderValue:
  2860. return ctx.JSON(v)
  2861. case ContentJSONProblemHeaderValue, ContentXMLProblemHeaderValue:
  2862. return ctx.Problem(v)
  2863. case ContentJavascriptHeaderValue:
  2864. return ctx.JSONP(v)
  2865. case ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue:
  2866. return ctx.XML(v)
  2867. case ContentYAMLHeaderValue:
  2868. return ctx.YAML(v)
  2869. case ContentProtobufHeaderValue:
  2870. msg, ok := v.(proto.Message)
  2871. if !ok {
  2872. return -1, ErrContentNotSupported
  2873. }
  2874. return ctx.Protobuf(msg)
  2875. case ContentMsgPackHeaderValue, ContentMsgPack2HeaderValue:
  2876. return ctx.MsgPack(v)
  2877. default:
  2878. // maybe "Other" or v is []byte or string but not a built-in framework mime,
  2879. // for custom content types,
  2880. // panic if not correct usage.
  2881. switch vv := v.(type) {
  2882. case []byte:
  2883. return ctx.Write(vv)
  2884. case string:
  2885. return ctx.WriteString(vv)
  2886. default:
  2887. ctx.StatusCode(http.StatusNotAcceptable)
  2888. return -1, ErrContentNotSupported
  2889. }
  2890. }
  2891. }
  2892. // NegotiationBuilder returns from the `Context.Negotitation`
  2893. // and can be used inside chain of handlers to build server-side
  2894. // mime type(s), charset(s) and encoding algorithm(s)
  2895. // that should match with the client's
  2896. // Accept, Accept-Charset and Accept-Encoding headers (by-default).
  2897. // To modify the client's accept use its "Accept" field
  2898. // which it's the `NegotitationAcceptBuilder`.
  2899. //
  2900. // See the `Negotiate` method too.
  2901. type NegotiationBuilder struct {
  2902. Accept NegotiationAcceptBuilder
  2903. mime []string // we need order.
  2904. contents map[string]interface{} // map to the "mime" and content should be rendered if that mime requested.
  2905. charset []string
  2906. encoding []string
  2907. }
  2908. // MIME registers a mime type and optionally the value that should be rendered
  2909. // through `Context.Negotiate` when this mime type is accepted by client.
  2910. //
  2911. // Returns itself for recursive calls.
  2912. func (n *NegotiationBuilder) MIME(mime string, content interface{}) *NegotiationBuilder {
  2913. mimes := parseHeader(mime) // if contains more than one sep by commas ",".
  2914. if content == nil {
  2915. n.mime = append(n.mime, mimes...)
  2916. return n
  2917. }
  2918. if n.contents == nil {
  2919. n.contents = make(map[string]interface{})
  2920. }
  2921. for _, m := range mimes {
  2922. n.mime = append(n.mime, m)
  2923. n.contents[m] = content
  2924. }
  2925. return n
  2926. }
  2927. // Text registers the "text/plain" content type and, optionally,
  2928. // a value that `Context.Negotiate` will render
  2929. // when a client accepts the "text/plain" content type.
  2930. //
  2931. // Returns itself for recursive calls.
  2932. func (n *NegotiationBuilder) Text(v ...string) *NegotiationBuilder {
  2933. var content interface{}
  2934. if len(v) > 0 {
  2935. content = v[0]
  2936. }
  2937. return n.MIME(ContentTextHeaderValue, content)
  2938. }
  2939. // HTML registers the "text/html" content type and, optionally,
  2940. // a value that `Context.Negotiate` will render
  2941. // when a client accepts the "text/html" content type.
  2942. //
  2943. // Returns itself for recursive calls.
  2944. func (n *NegotiationBuilder) HTML(v ...string) *NegotiationBuilder {
  2945. var content interface{}
  2946. if len(v) > 0 {
  2947. content = v[0]
  2948. }
  2949. return n.MIME(ContentHTMLHeaderValue, content)
  2950. }
  2951. // Markdown registers the "text/markdown" content type and, optionally,
  2952. // a value that `Context.Negotiate` will render
  2953. // when a client accepts the "text/markdown" content type.
  2954. //
  2955. // Returns itself for recursive calls.
  2956. func (n *NegotiationBuilder) Markdown(v ...[]byte) *NegotiationBuilder {
  2957. var content interface{}
  2958. if len(v) > 0 {
  2959. content = v
  2960. }
  2961. return n.MIME(ContentMarkdownHeaderValue, content)
  2962. }
  2963. // Binary registers the "application/octet-stream" content type and, optionally,
  2964. // a value that `Context.Negotiate` will render
  2965. // when a client accepts the "application/octet-stream" content type.
  2966. //
  2967. // Returns itself for recursive calls.
  2968. func (n *NegotiationBuilder) Binary(v ...[]byte) *NegotiationBuilder {
  2969. var content interface{}
  2970. if len(v) > 0 {
  2971. content = v[0]
  2972. }
  2973. return n.MIME(ContentBinaryHeaderValue, content)
  2974. }
  2975. // JSON registers the "application/json" content type and, optionally,
  2976. // a value that `Context.Negotiate` will render
  2977. // when a client accepts the "application/json" content type.
  2978. //
  2979. // Returns itself for recursive calls.
  2980. func (n *NegotiationBuilder) JSON(v ...interface{}) *NegotiationBuilder {
  2981. var content interface{}
  2982. if len(v) > 0 {
  2983. content = v[0]
  2984. }
  2985. return n.MIME(ContentJSONHeaderValue, content)
  2986. }
  2987. // Problem registers the "application/problem+json" or "application/problem+xml" content type and, optionally,
  2988. // a value that `Context.Negotiate` will render
  2989. // when a client accepts the "application/problem+json" or the "application/problem+xml" content type.
  2990. //
  2991. // Returns itself for recursive calls.
  2992. func (n *NegotiationBuilder) Problem(v ...interface{}) *NegotiationBuilder {
  2993. var content interface{}
  2994. if len(v) > 0 {
  2995. content = v[0]
  2996. }
  2997. return n.MIME(ContentJSONProblemHeaderValue+","+ContentXMLProblemHeaderValue, content)
  2998. }
  2999. // JSONP registers the "text/javascript" content type and, optionally,
  3000. // a value that `Context.Negotiate` will render
  3001. // when a client accepts the "javascript/javascript" content type.
  3002. //
  3003. // Returns itself for recursive calls.
  3004. func (n *NegotiationBuilder) JSONP(v ...interface{}) *NegotiationBuilder {
  3005. var content interface{}
  3006. if len(v) > 0 {
  3007. content = v[0]
  3008. }
  3009. return n.MIME(ContentJavascriptHeaderValue, content)
  3010. }
  3011. // XML registers the "text/xml" and "application/xml" content types and, optionally,
  3012. // a value that `Context.Negotiate` will render
  3013. // when a client accepts one of the "text/xml" or "application/xml" content types.
  3014. //
  3015. // Returns itself for recursive calls.
  3016. func (n *NegotiationBuilder) XML(v ...interface{}) *NegotiationBuilder {
  3017. var content interface{}
  3018. if len(v) > 0 {
  3019. content = v[0]
  3020. }
  3021. return n.MIME(ContentXMLHeaderValue+","+ContentXMLUnreadableHeaderValue, content)
  3022. }
  3023. // YAML registers the "application/x-yaml" content type and, optionally,
  3024. // a value that `Context.Negotiate` will render
  3025. // when a client accepts the "application/x-yaml" content type.
  3026. //
  3027. // Returns itself for recursive calls.
  3028. func (n *NegotiationBuilder) YAML(v ...interface{}) *NegotiationBuilder {
  3029. var content interface{}
  3030. if len(v) > 0 {
  3031. content = v[0]
  3032. }
  3033. return n.MIME(ContentYAMLHeaderValue, content)
  3034. }
  3035. // Protobuf registers the "application/x-protobuf" content type and, optionally,
  3036. // a value that `Context.Negotiate` will render
  3037. // when a client accepts the "application/x-protobuf" content type.
  3038. //
  3039. // Returns itself for recursive calls.
  3040. func (n *NegotiationBuilder) Protobuf(v ...interface{}) *NegotiationBuilder {
  3041. var content interface{}
  3042. if len(v) > 0 {
  3043. content = v[0]
  3044. }
  3045. return n.MIME(ContentProtobufHeaderValue, content)
  3046. }
  3047. // MsgPack registers the "application/x-msgpack" and "application/msgpack" content types and, optionally,
  3048. // a value that `Context.Negotiate` will render
  3049. // when a client accepts one of the "application/x-msgpack" or "application/msgpack" content types.
  3050. //
  3051. // Returns itself for recursive calls.
  3052. func (n *NegotiationBuilder) MsgPack(v ...interface{}) *NegotiationBuilder {
  3053. var content interface{}
  3054. if len(v) > 0 {
  3055. content = v[0]
  3056. }
  3057. return n.MIME(ContentMsgPackHeaderValue+","+ContentMsgPack2HeaderValue, content)
  3058. }
  3059. // Any registers a wildcard that can match any client's accept content type.
  3060. //
  3061. // Returns itself for recursive calls.
  3062. func (n *NegotiationBuilder) Any(v ...interface{}) *NegotiationBuilder {
  3063. var content interface{}
  3064. if len(v) > 0 {
  3065. content = v[0]
  3066. }
  3067. return n.MIME("*", content)
  3068. }
  3069. // Charset overrides the application's config's charset (which defaults to "utf-8")
  3070. // that a client should match for
  3071. // (through Accept-Charset header or custom through `NegotitationBuilder.Accept.Override().Charset(...)` call).
  3072. // Do not set it if you don't know what you're doing.
  3073. //
  3074. // Returns itself for recursive calls.
  3075. func (n *NegotiationBuilder) Charset(charset ...string) *NegotiationBuilder {
  3076. n.charset = append(n.charset, charset...)
  3077. return n
  3078. }
  3079. // Encoding registers one or more encoding algorithms by name, i.e gzip, deflate, br, snappy, s2.
  3080. // that a client should match for (through Accept-Encoding header).
  3081. //
  3082. // Returns itself for recursive calls.
  3083. func (n *NegotiationBuilder) Encoding(encoding ...string) *NegotiationBuilder {
  3084. n.encoding = append(n.encoding, encoding...)
  3085. return n
  3086. }
  3087. // EncodingGzip registers the "gzip" encoding algorithm
  3088. // that a client should match for (through Accept-Encoding header or call of Accept.Encoding(enc)).
  3089. //
  3090. // It will make resources to served by "gzip" if Accept-Encoding contains the "gzip" as well.
  3091. //
  3092. // Returns itself for recursive calls.
  3093. func (n *NegotiationBuilder) EncodingGzip() *NegotiationBuilder {
  3094. return n.Encoding(GzipHeaderValue)
  3095. }
  3096. // Build calculates the client's and server's mime type(s), charset(s) and encoding
  3097. // and returns the final content type, charset and encoding that server should render
  3098. // to the client. It does not clear the fields, use the `Clear` method if neeeded.
  3099. //
  3100. // The returned "content" can be nil if the matched "contentType" does not provide any value,
  3101. // in that case the `Context.Negotiate(v)` must be called with a non-nil value.
  3102. func (n *NegotiationBuilder) Build() (contentType, charset, encoding string, content interface{}) {
  3103. contentType = negotiationMatch(n.Accept.accept, n.mime)
  3104. charset = negotiationMatch(n.Accept.charset, n.charset)
  3105. encoding = negotiationMatch(n.Accept.encoding, n.encoding)
  3106. if n.contents != nil {
  3107. if data, ok := n.contents[contentType]; ok {
  3108. content = data
  3109. }
  3110. }
  3111. return
  3112. }
  3113. // Clear clears the prioritized mime type(s), charset(s) and any contents
  3114. // relative to those mime type(s).
  3115. // The "Accept" field is stay as it is, use its `Override` method
  3116. // to clear out the client's accepted mime type(s) and charset(s).
  3117. func (n *NegotiationBuilder) Clear() *NegotiationBuilder {
  3118. n.mime = n.mime[0:0]
  3119. n.contents = nil
  3120. n.charset = n.charset[0:0]
  3121. return n
  3122. }
  3123. // NegotiationAcceptBuilder builds the accepted mime types and charset
  3124. //
  3125. // and "Accept-Charset" headers respectfully.
  3126. // The default values are set by the client side, server can append or override those.
  3127. // The end result will be challenged with runtime preffered set of content types and charsets.
  3128. //
  3129. // See the `Negotiate` method too.
  3130. type NegotiationAcceptBuilder struct {
  3131. // initialized with "Accept" request header values.
  3132. accept []string
  3133. // initialized with "Accept-Charset" request header. and if was empty then the
  3134. // application's default (which defaults to utf-8).
  3135. charset []string
  3136. // initialized with "Accept-Encoding" request header values.
  3137. encoding []string
  3138. // To support override in request life cycle.
  3139. // We need slice when data is the same format
  3140. // for one or more mime types,
  3141. // i.e text/xml and obselete application/xml.
  3142. lastAccept []string
  3143. lastCharset []string
  3144. lastEncoding []string
  3145. }
  3146. // Override clears the default values for accept and accept charset.
  3147. // Returns itself.
  3148. func (n *NegotiationAcceptBuilder) Override() *NegotiationAcceptBuilder {
  3149. // when called first.
  3150. n.accept = n.accept[0:0]
  3151. n.charset = n.charset[0:0]
  3152. n.encoding = n.encoding[0:0]
  3153. // when called after.
  3154. if len(n.lastAccept) > 0 {
  3155. n.accept = append(n.accept, n.lastAccept...)
  3156. n.lastAccept = n.lastAccept[0:0]
  3157. }
  3158. if len(n.lastCharset) > 0 {
  3159. n.charset = append(n.charset, n.lastCharset...)
  3160. n.lastCharset = n.lastCharset[0:0]
  3161. }
  3162. if len(n.lastEncoding) > 0 {
  3163. n.encoding = append(n.encoding, n.lastEncoding...)
  3164. n.lastEncoding = n.lastEncoding[0:0]
  3165. }
  3166. return n
  3167. }
  3168. // MIME adds accepted client's mime type(s).
  3169. // Returns itself.
  3170. func (n *NegotiationAcceptBuilder) MIME(mimeType ...string) *NegotiationAcceptBuilder {
  3171. n.lastAccept = mimeType
  3172. n.accept = append(n.accept, mimeType...)
  3173. return n
  3174. }
  3175. // Text adds the "text/plain" as accepted client content type.
  3176. // Returns itself.
  3177. func (n *NegotiationAcceptBuilder) Text() *NegotiationAcceptBuilder {
  3178. return n.MIME(ContentTextHeaderValue)
  3179. }
  3180. // HTML adds the "text/html" as accepted client content type.
  3181. // Returns itself.
  3182. func (n *NegotiationAcceptBuilder) HTML() *NegotiationAcceptBuilder {
  3183. return n.MIME(ContentHTMLHeaderValue)
  3184. }
  3185. // Markdown adds the "text/markdown" as accepted client content type.
  3186. // Returns itself.
  3187. func (n *NegotiationAcceptBuilder) Markdown() *NegotiationAcceptBuilder {
  3188. return n.MIME(ContentMarkdownHeaderValue)
  3189. }
  3190. // Binary adds the "application/octet-stream" as accepted client content type.
  3191. // Returns itself.
  3192. func (n *NegotiationAcceptBuilder) Binary() *NegotiationAcceptBuilder {
  3193. return n.MIME(ContentBinaryHeaderValue)
  3194. }
  3195. // JSON adds the "application/json" as accepted client content type.
  3196. // Returns itself.
  3197. func (n *NegotiationAcceptBuilder) JSON() *NegotiationAcceptBuilder {
  3198. return n.MIME(ContentJSONHeaderValue)
  3199. }
  3200. // Problem adds the "application/problem+json" and "application/problem-xml"
  3201. // as accepted client content types.
  3202. // Returns itself.
  3203. func (n *NegotiationAcceptBuilder) Problem() *NegotiationAcceptBuilder {
  3204. return n.MIME(ContentJSONProblemHeaderValue, ContentXMLProblemHeaderValue)
  3205. }
  3206. // JSONP adds the "text/javascript" as accepted client content type.
  3207. // Returns itself.
  3208. func (n *NegotiationAcceptBuilder) JSONP() *NegotiationAcceptBuilder {
  3209. return n.MIME(ContentJavascriptHeaderValue)
  3210. }
  3211. // XML adds the "text/xml" and "application/xml" as accepted client content types.
  3212. // Returns itself.
  3213. func (n *NegotiationAcceptBuilder) XML() *NegotiationAcceptBuilder {
  3214. return n.MIME(ContentXMLHeaderValue, ContentXMLUnreadableHeaderValue)
  3215. }
  3216. // YAML adds the "application/x-yaml" as accepted client content type.
  3217. // Returns itself.
  3218. func (n *NegotiationAcceptBuilder) YAML() *NegotiationAcceptBuilder {
  3219. return n.MIME(ContentYAMLHeaderValue)
  3220. }
  3221. // Protobuf adds the "application/x-protobuf" as accepted client content type.
  3222. // Returns itself.
  3223. func (n *NegotiationAcceptBuilder) Protobuf() *NegotiationAcceptBuilder {
  3224. return n.MIME(ContentYAMLHeaderValue)
  3225. }
  3226. // MsgPack adds the "application/msgpack" and "application/x-msgpack" as accepted client content types.
  3227. // Returns itself.
  3228. func (n *NegotiationAcceptBuilder) MsgPack() *NegotiationAcceptBuilder {
  3229. return n.MIME(ContentYAMLHeaderValue)
  3230. }
  3231. // Charset adds one or more client accepted charsets.
  3232. // Returns itself.
  3233. func (n *NegotiationAcceptBuilder) Charset(charset ...string) *NegotiationAcceptBuilder {
  3234. n.lastCharset = charset
  3235. n.charset = append(n.charset, charset...)
  3236. return n
  3237. }
  3238. // Encoding adds one or more client accepted encoding algorithms.
  3239. // Returns itself.
  3240. func (n *NegotiationAcceptBuilder) Encoding(encoding ...string) *NegotiationAcceptBuilder {
  3241. n.lastEncoding = encoding
  3242. n.encoding = append(n.encoding, encoding...)
  3243. return n
  3244. }
  3245. // EncodingGzip adds the "gzip" as accepted encoding.
  3246. // Returns itself.
  3247. func (n *NegotiationAcceptBuilder) EncodingGzip() *NegotiationAcceptBuilder {
  3248. return n.Encoding(GzipHeaderValue)
  3249. }
  3250. // +------------------------------------------------------------+
  3251. // | Serve files |
  3252. // +------------------------------------------------------------+
  3253. // ServeContent replies to the request using the content in the
  3254. // provided ReadSeeker. The main benefit of ServeContent over io.Copy
  3255. // is that it handles Range requests properly, sets the MIME type, and
  3256. // handles If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since,
  3257. // and If-Range requests.
  3258. //
  3259. // If the response's Content-Type header is not set, ServeContent
  3260. // first tries to deduce the type from name's file extension.
  3261. //
  3262. // The name is otherwise unused; in particular it can be empty and is
  3263. // never sent in the response.
  3264. //
  3265. // If modtime is not the zero time or Unix epoch, ServeContent
  3266. // includes it in a Last-Modified header in the response. If the
  3267. // request includes an If-Modified-Since header, ServeContent uses
  3268. // modtime to decide whether the content needs to be sent at all.
  3269. //
  3270. // The content's Seek method must work: ServeContent uses
  3271. // a seek to the end of the content to determine its size.
  3272. //
  3273. // If the caller has set w's ETag header formatted per RFC 7232, section 2.3,
  3274. // ServeContent uses it to handle requests using If-Match, If-None-Match, or If-Range.
  3275. //
  3276. // Note that *os.File implements the io.ReadSeeker interface.
  3277. // Note that compression can be registered
  3278. // through `ctx.CompressWriter(true)` or `app.Use(iris.Compression)`.
  3279. func (ctx *Context) ServeContent(content io.ReadSeeker, filename string, modtime time.Time) {
  3280. ctx.ServeContentWithRate(content, filename, modtime, 0, 0)
  3281. }
  3282. // rateReadSeeker is a io.ReadSeeker that is rate limited by
  3283. // the given token bucket. Each token in the bucket
  3284. // represents one byte. See "golang.org/x/time/rate" package.
  3285. type rateReadSeeker struct {
  3286. io.ReadSeeker
  3287. ctx stdContext.Context
  3288. limiter *rate.Limiter
  3289. }
  3290. func (rs *rateReadSeeker) Read(buf []byte) (int, error) {
  3291. n, err := rs.ReadSeeker.Read(buf)
  3292. if n <= 0 {
  3293. return n, err
  3294. }
  3295. err = rs.limiter.WaitN(rs.ctx, n)
  3296. return n, err
  3297. }
  3298. // ServeContentWithRate same as `ServeContent` but it can throttle the speed of reading
  3299. // and though writing the "content" to the client.
  3300. func (ctx *Context) ServeContentWithRate(content io.ReadSeeker, filename string, modtime time.Time, limit float64, burst int) {
  3301. if limit > 0 {
  3302. content = &rateReadSeeker{
  3303. ReadSeeker: content,
  3304. ctx: ctx.request.Context(),
  3305. limiter: rate.NewLimiter(rate.Limit(limit), burst),
  3306. }
  3307. }
  3308. if ctx.GetContentType() == "" {
  3309. ctx.ContentType(filename)
  3310. }
  3311. http.ServeContent(ctx.writer, ctx.request, filename, modtime, content)
  3312. }
  3313. // ServeFile replies to the request with the contents of the named
  3314. // file or directory.
  3315. //
  3316. // If the provided file or directory name is a relative path, it is
  3317. // interpreted relative to the current directory and may ascend to
  3318. // parent directories. If the provided name is constructed from user
  3319. // input, it should be sanitized before calling `ServeFile`.
  3320. //
  3321. // Use it when you want to serve assets like css and javascript files.
  3322. // If client should confirm and save the file use the `SendFile` instead.
  3323. // Note that compression can be registered
  3324. // through `ctx.CompressWriter(true)` or `app.Use(iris.Compression)`.
  3325. func (ctx *Context) ServeFile(filename string) error {
  3326. return ctx.ServeFileWithRate(filename, 0, 0)
  3327. }
  3328. // ServeFileWithRate same as `ServeFile` but it can throttle the speed of reading
  3329. // and though writing the file to the client.
  3330. func (ctx *Context) ServeFileWithRate(filename string, limit float64, burst int) error {
  3331. f, err := os.Open(filename)
  3332. if err != nil {
  3333. ctx.StatusCode(http.StatusNotFound)
  3334. return err
  3335. }
  3336. defer f.Close()
  3337. st, err := f.Stat()
  3338. if err != nil {
  3339. code := http.StatusInternalServerError
  3340. if os.IsNotExist(err) {
  3341. code = http.StatusNotFound
  3342. }
  3343. if os.IsPermission(err) {
  3344. code = http.StatusForbidden
  3345. }
  3346. ctx.StatusCode(code)
  3347. return err
  3348. }
  3349. if st.IsDir() {
  3350. return ctx.ServeFile(path.Join(filename, "index.html"))
  3351. }
  3352. ctx.ServeContentWithRate(f, st.Name(), st.ModTime(), limit, burst)
  3353. return nil
  3354. }
  3355. // SendFile sends a file as an attachment, that is downloaded and saved locally from client.
  3356. // Note that compression can be registered
  3357. // through `ctx.CompressWriter(true)` or `app.Use(iris.Compression)`.
  3358. // Use `ServeFile` if a file should be served as a page asset instead.
  3359. func (ctx *Context) SendFile(src string, destName string) error {
  3360. return ctx.SendFileWithRate(src, destName, 0, 0)
  3361. }
  3362. // SendFileWithRate same as `SendFile` but it can throttle the speed of reading
  3363. // and though writing the file to the client.
  3364. func (ctx *Context) SendFileWithRate(src, destName string, limit float64, burst int) error {
  3365. if destName == "" {
  3366. destName = filepath.Base(src)
  3367. }
  3368. ctx.writer.Header().Set(ContentDispositionHeaderKey, "attachment;filename="+destName)
  3369. return ctx.ServeFileWithRate(src, limit, burst)
  3370. }
  3371. // +------------------------------------------------------------+
  3372. // | Cookies |
  3373. // +------------------------------------------------------------+
  3374. // Set of Cookie actions for `CookieOption`.
  3375. const (
  3376. OpCookieGet uint8 = iota
  3377. OpCookieSet
  3378. OpCookieDel
  3379. )
  3380. // CookieOption is the type of function that is accepted on
  3381. // context's methods like `SetCookieKV`, `RemoveCookie` and `SetCookie`
  3382. // as their (last) variadic input argument to amend the to-be-sent cookie.
  3383. //
  3384. // The "op" is the operation code, 0 is GET, 1 is SET and 2 is REMOVE.
  3385. type CookieOption func(ctx *Context, c *http.Cookie, op uint8)
  3386. // CookieIncluded reports whether the "cookie.Name" is in the list of "cookieNames".
  3387. // Notes:
  3388. // If "cookieNames" slice is empty then it returns true,
  3389. // If "cookie.Name" is empty then it returns false.
  3390. func CookieIncluded(cookie *http.Cookie, cookieNames []string) bool {
  3391. if cookie.Name == "" {
  3392. return false
  3393. }
  3394. if len(cookieNames) > 0 {
  3395. for _, name := range cookieNames {
  3396. if cookie.Name == name {
  3397. return true
  3398. }
  3399. }
  3400. return false
  3401. }
  3402. return true
  3403. }
  3404. var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-")
  3405. func sanitizeCookieName(n string) string {
  3406. return cookieNameSanitizer.Replace(n)
  3407. }
  3408. // CookieAllowReclaim accepts the Context itself.
  3409. // If set it will add the cookie to (on `CookieSet`, `CookieSetKV`, `CookieUpsert`)
  3410. // or remove the cookie from (on `CookieRemove`) the Request object too.
  3411. func CookieAllowReclaim(cookieNames ...string) CookieOption {
  3412. return func(ctx *Context, c *http.Cookie, op uint8) {
  3413. if op == OpCookieGet {
  3414. return
  3415. }
  3416. if !CookieIncluded(c, cookieNames) {
  3417. return
  3418. }
  3419. switch op {
  3420. case OpCookieSet:
  3421. // perform upsert on request cookies or is it too much and not worth the cost?
  3422. ctx.Request().AddCookie(c)
  3423. case OpCookieDel:
  3424. header := ctx.Request().Header
  3425. if cookiesLine := header.Get("Cookie"); cookiesLine != "" {
  3426. if cookies := strings.Split(cookiesLine, "; "); len(cookies) > 1 {
  3427. // more than one cookie here.
  3428. // select that one and remove it.
  3429. name := sanitizeCookieName(c.Name)
  3430. for _, nameValue := range cookies {
  3431. if strings.HasPrefix(nameValue, name) {
  3432. cookiesLine = strings.Replace(cookiesLine, "; "+nameValue, "", 1)
  3433. // current cookiesLine: myapp_session_id=5ccf4e89-8d0e-4ed6-9f4c-6746d7c5e2ee; key1=value1
  3434. // found nameValue: key1=value1
  3435. // new cookiesLine: myapp_session_id=5ccf4e89-8d0e-4ed6-9f4c-6746d7c5e2ee
  3436. header.Set("Cookie", cookiesLine)
  3437. break
  3438. }
  3439. }
  3440. return
  3441. }
  3442. }
  3443. header.Del("Cookie")
  3444. }
  3445. }
  3446. }
  3447. // CookieAllowSubdomains set to the Cookie Options
  3448. // in order to allow subdomains to have access to the cookies.
  3449. // It sets the cookie's Domain field (if was empty) and
  3450. // it also sets the cookie's SameSite to lax mode too.
  3451. func CookieAllowSubdomains(cookieNames ...string) CookieOption {
  3452. return func(ctx *Context, c *http.Cookie, _ uint8) {
  3453. if c.Domain != "" {
  3454. return // already set.
  3455. }
  3456. if !CookieIncluded(c, cookieNames) {
  3457. return
  3458. }
  3459. c.Domain = ctx.GetDomain()
  3460. c.SameSite = http.SameSiteLaxMode // allow subdomain sharing.
  3461. }
  3462. }
  3463. // CookieSameSite sets a same-site rule for cookies to set.
  3464. // SameSite allows a server to define a cookie attribute making it impossible for
  3465. // the browser to send this cookie along with cross-site requests. The main
  3466. // goal is to mitigate the risk of cross-origin information leakage, and provide
  3467. // some protection against cross-site request forgery attacks.
  3468. //
  3469. // See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details.
  3470. func CookieSameSite(sameSite http.SameSite) CookieOption {
  3471. return func(_ *Context, c *http.Cookie, op uint8) {
  3472. if op == OpCookieSet {
  3473. c.SameSite = sameSite
  3474. }
  3475. }
  3476. }
  3477. // CookieSecure sets the cookie's Secure option if the current request's
  3478. // connection is using TLS. See `CookieHTTPOnly` too.
  3479. func CookieSecure(ctx *Context, c *http.Cookie, op uint8) {
  3480. if op == OpCookieSet {
  3481. if ctx.Request().TLS != nil {
  3482. c.Secure = true
  3483. }
  3484. }
  3485. }
  3486. // CookieHTTPOnly is a `CookieOption`.
  3487. // Use it to set the cookie's HttpOnly field to false or true.
  3488. // HttpOnly field defaults to true for `RemoveCookie` and `SetCookieKV`.
  3489. // See `CookieSecure` too.
  3490. func CookieHTTPOnly(httpOnly bool) CookieOption {
  3491. return func(_ *Context, c *http.Cookie, op uint8) {
  3492. if op == OpCookieSet {
  3493. c.HttpOnly = httpOnly
  3494. }
  3495. }
  3496. }
  3497. // CookiePath is a `CookieOption`.
  3498. // Use it to change the cookie's Path field.
  3499. func CookiePath(path string) CookieOption {
  3500. return func(_ *Context, c *http.Cookie, op uint8) {
  3501. if op > OpCookieGet { // on set and remove.
  3502. c.Path = path
  3503. }
  3504. }
  3505. }
  3506. // CookieCleanPath is a `CookieOption`.
  3507. // Use it to clear the cookie's Path field, exactly the same as `CookiePath("")`.
  3508. func CookieCleanPath(_ *Context, c *http.Cookie, op uint8) {
  3509. if op > OpCookieGet {
  3510. c.Path = ""
  3511. }
  3512. }
  3513. // CookieExpires is a `CookieOption`.
  3514. // Use it to change the cookie's Expires and MaxAge fields by passing the lifetime of the cookie.
  3515. func CookieExpires(durFromNow time.Duration) CookieOption {
  3516. return func(_ *Context, c *http.Cookie, op uint8) {
  3517. if op == OpCookieSet {
  3518. c.Expires = time.Now().Add(durFromNow)
  3519. c.MaxAge = int(durFromNow.Seconds())
  3520. }
  3521. }
  3522. }
  3523. // SecureCookie should encodes and decodes
  3524. // authenticated and optionally encrypted cookie values.
  3525. // See `CookieEncoding` package-level function.
  3526. type SecureCookie interface {
  3527. // Encode should encode the cookie value.
  3528. // Should accept the cookie's name as its first argument
  3529. // and as second argument the cookie value ptr.
  3530. // Should return an encoded value or an empty one if encode operation failed.
  3531. // Should return an error if encode operation failed.
  3532. //
  3533. // Note: Errors are not printed, so you have to know what you're doing,
  3534. // and remember: if you use AES it only supports key sizes of 16, 24 or 32 bytes.
  3535. // You either need to provide exactly that amount or you derive the key from what you type in.
  3536. //
  3537. // See `Decode` too.
  3538. Encode(cookieName string, cookieValue interface{}) (string, error)
  3539. // Decode should decode the cookie value.
  3540. // Should accept the cookie's name as its first argument,
  3541. // as second argument the encoded cookie value and as third argument the decoded value ptr.
  3542. // Should return a decoded value or an empty one if decode operation failed.
  3543. // Should return an error if decode operation failed.
  3544. //
  3545. // Note: Errors are not printed, so you have to know what you're doing,
  3546. // and remember: if you use AES it only supports key sizes of 16, 24 or 32 bytes.
  3547. // You either need to provide exactly that amount or you derive the key from what you type in.
  3548. //
  3549. // See `Encode` too.
  3550. Decode(cookieName string, cookieValue string, cookieValuePtr interface{}) error
  3551. }
  3552. // CookieEncoding accepts a value which implements `Encode` and `Decode` methods.
  3553. // It calls its `Encode` on `Context.SetCookie, UpsertCookie, and SetCookieKV` methods.
  3554. // And on `Context.GetCookie` method it calls its `Decode`.
  3555. // If "cookieNames" slice is not empty then only cookies
  3556. // with that `Name` will be encoded on set and decoded on get, that way you can encrypt
  3557. // specific cookie names (like the session id) and let the rest of the cookies "insecure".
  3558. //
  3559. // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/securecookie
  3560. func CookieEncoding(encoding SecureCookie, cookieNames ...string) CookieOption {
  3561. return func(_ *Context, c *http.Cookie, op uint8) {
  3562. if op == OpCookieDel {
  3563. return
  3564. }
  3565. if !CookieIncluded(c, cookieNames) {
  3566. return
  3567. }
  3568. switch op {
  3569. case OpCookieSet:
  3570. // Should encode, it's a write to the client operation.
  3571. newVal, err := encoding.Encode(c.Name, c.Value)
  3572. if err != nil {
  3573. c.Value = ""
  3574. } else {
  3575. c.Value = newVal
  3576. }
  3577. return
  3578. case OpCookieGet:
  3579. // Should decode, it's a read from the client operation.
  3580. if err := encoding.Decode(c.Name, c.Value, &c.Value); err != nil {
  3581. c.Value = ""
  3582. }
  3583. }
  3584. }
  3585. }
  3586. const cookieOptionsContextKey = "iris.cookie.options"
  3587. // AddCookieOptions adds cookie options for `SetCookie`,
  3588. // `SetCookieKV, UpsertCookie` and `RemoveCookie` methods
  3589. // for the current request. It can be called from a middleware before
  3590. // cookies sent or received from the next Handler in the chain.
  3591. //
  3592. // Available builtin Cookie options are:
  3593. // * CookieAllowReclaim
  3594. // * CookieAllowSubdomains
  3595. // * CookieSecure
  3596. // * CookieHTTPOnly
  3597. // * CookieSameSite
  3598. // * CookiePath
  3599. // * CookieCleanPath
  3600. // * CookieExpires
  3601. // * CookieEncoding
  3602. //
  3603. // Example at: https://github.com/kataras/iris/tree/master/_examples/cookies/securecookie
  3604. func (ctx *Context) AddCookieOptions(options ...CookieOption) {
  3605. if len(options) == 0 {
  3606. return
  3607. }
  3608. if v := ctx.values.Get(cookieOptionsContextKey); v != nil {
  3609. if opts, ok := v.([]CookieOption); ok {
  3610. options = append(opts, options...)
  3611. }
  3612. }
  3613. ctx.values.Set(cookieOptionsContextKey, options)
  3614. }
  3615. func (ctx *Context) applyCookieOptions(c *http.Cookie, op uint8, override []CookieOption) {
  3616. if v := ctx.values.Get(cookieOptionsContextKey); v != nil {
  3617. if options, ok := v.([]CookieOption); ok {
  3618. for _, opt := range options {
  3619. opt(ctx, c, op)
  3620. }
  3621. }
  3622. }
  3623. // The function's ones should be called last, so they can override
  3624. // the stored ones (i.e by a prior middleware).
  3625. for _, opt := range override {
  3626. opt(ctx, c, op)
  3627. }
  3628. }
  3629. // ClearCookieOptions clears any previously registered cookie options.
  3630. // See `AddCookieOptions` too.
  3631. func (ctx *Context) ClearCookieOptions() {
  3632. ctx.values.Remove(cookieOptionsContextKey)
  3633. }
  3634. // SetCookie adds a cookie.
  3635. // Use of the "options" is not required, they can be used to amend the "cookie".
  3636. //
  3637. // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
  3638. func (ctx *Context) SetCookie(cookie *http.Cookie, options ...CookieOption) {
  3639. ctx.applyCookieOptions(cookie, OpCookieSet, options)
  3640. http.SetCookie(ctx.writer, cookie)
  3641. }
  3642. const setCookieHeaderKey = "Set-Cookie"
  3643. // UpsertCookie adds a cookie to the response like `SetCookie` does
  3644. // but it will also perform a replacement of the cookie
  3645. // if already set by a previous `SetCookie` call.
  3646. // It reports whether the cookie is new (true) or an existing one was updated (false).
  3647. func (ctx *Context) UpsertCookie(cookie *http.Cookie, options ...CookieOption) bool {
  3648. ctx.applyCookieOptions(cookie, OpCookieSet, options)
  3649. header := ctx.ResponseWriter().Header()
  3650. if cookies := header[setCookieHeaderKey]; len(cookies) > 0 {
  3651. s := cookie.Name + "=" // name=?value
  3652. for i, c := range cookies {
  3653. if strings.HasPrefix(c, s) {
  3654. // We need to update the Set-Cookie (to update the expiration or any other cookie's properties).
  3655. // Probably the cookie is set and then updated in the first session creation
  3656. // (e.g. UpdateExpiration, see https://github.com/kataras/iris/issues/1485).
  3657. cookies[i] = cookie.String()
  3658. header[setCookieHeaderKey] = cookies
  3659. return false
  3660. }
  3661. }
  3662. }
  3663. header.Add(setCookieHeaderKey, cookie.String())
  3664. return true
  3665. }
  3666. // SetCookieKVExpiration is 365 days by-default
  3667. // you can change it or simple, use the SetCookie for more control.
  3668. //
  3669. // See `CookieExpires` and `AddCookieOptions` for more.
  3670. var SetCookieKVExpiration = time.Duration(8760) * time.Hour
  3671. // SetCookieKV adds a cookie, requires the name(string) and the value(string).
  3672. //
  3673. // By default it expires after 365 days and it is added to the root URL path,
  3674. // use the `CookieExpires` and `CookiePath` to modify them.
  3675. // Alternatively: ctx.SetCookie(&http.Cookie{...}) or ctx.AddCookieOptions(...)
  3676. //
  3677. // If you want to set custom the path:
  3678. // ctx.SetCookieKV(name, value, iris.CookiePath("/custom/path/cookie/will/be/stored"))
  3679. //
  3680. // If you want to be visible only to current request path:
  3681. // (note that client should be responsible for that if server sent an empty cookie's path, all browsers are compatible)
  3682. // ctx.SetCookieKV(name, value, iris.CookieCleanPath/iris.CookiePath(""))
  3683. // More:
  3684. // iris.CookieExpires(time.Duration)
  3685. // iris.CookieHTTPOnly(false)
  3686. //
  3687. // Examples: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
  3688. func (ctx *Context) SetCookieKV(name, value string, options ...CookieOption) {
  3689. c := &http.Cookie{}
  3690. c.Path = "/"
  3691. c.Name = name
  3692. c.Value = url.QueryEscape(value)
  3693. c.HttpOnly = true
  3694. // MaxAge=0 means no 'Max-Age' attribute specified.
  3695. // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
  3696. // MaxAge>0 means Max-Age attribute present and given in seconds
  3697. c.Expires = time.Now().Add(SetCookieKVExpiration)
  3698. c.MaxAge = int(time.Until(c.Expires).Seconds())
  3699. ctx.SetCookie(c, options...)
  3700. }
  3701. // GetCookie returns cookie's value by its name
  3702. // returns empty string if nothing was found.
  3703. //
  3704. // If you want more than the value then:
  3705. // cookie, err := ctx.Request().Cookie("name")
  3706. //
  3707. // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
  3708. func (ctx *Context) GetCookie(name string, options ...CookieOption) string {
  3709. c, err := ctx.request.Cookie(name)
  3710. if err != nil {
  3711. return ""
  3712. }
  3713. ctx.applyCookieOptions(c, OpCookieGet, options)
  3714. value, _ := url.QueryUnescape(c.Value)
  3715. return value
  3716. }
  3717. var (
  3718. // CookieExpireDelete may be set on Cookie.Expire for expiring the given cookie.
  3719. CookieExpireDelete = time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
  3720. // CookieExpireUnlimited indicates that does expires after 24 years.
  3721. CookieExpireUnlimited = time.Now().AddDate(24, 10, 10)
  3722. )
  3723. // RemoveCookie deletes a cookie by its name and path = "/".
  3724. // Tip: change the cookie's path to the current one by: RemoveCookie("name", iris.CookieCleanPath)
  3725. //
  3726. // Example: https://github.com/kataras/iris/tree/master/_examples/cookies/basic
  3727. func (ctx *Context) RemoveCookie(name string, options ...CookieOption) {
  3728. c := &http.Cookie{}
  3729. c.Name = name
  3730. c.Value = ""
  3731. c.Path = "/" // if user wants to change it, use of the CookieOption `CookiePath` is required if not `ctx.SetCookie`.
  3732. c.HttpOnly = true
  3733. // RFC says 1 second, but let's do it 1 to make sure is working
  3734. c.Expires = CookieExpireDelete
  3735. c.MaxAge = -1
  3736. ctx.applyCookieOptions(c, OpCookieDel, options)
  3737. http.SetCookie(ctx.writer, c)
  3738. }
  3739. // VisitAllCookies takes a visitor function which is called
  3740. // on each (request's) cookies' name and value.
  3741. func (ctx *Context) VisitAllCookies(visitor func(name string, value string)) {
  3742. for _, cookie := range ctx.request.Cookies() {
  3743. visitor(cookie.Name, cookie.Value)
  3744. }
  3745. }
  3746. var maxAgeExp = regexp.MustCompile(`maxage=(\d+)`)
  3747. // MaxAge returns the "cache-control" request header's value
  3748. // seconds as int64
  3749. // if header not found or parse failed then it returns -1.
  3750. func (ctx *Context) MaxAge() int64 {
  3751. header := ctx.GetHeader(CacheControlHeaderKey)
  3752. if header == "" {
  3753. return -1
  3754. }
  3755. m := maxAgeExp.FindStringSubmatch(header)
  3756. if len(m) == 2 {
  3757. if v, err := strconv.Atoi(m[1]); err == nil {
  3758. return int64(v)
  3759. }
  3760. }
  3761. return -1
  3762. }
  3763. // +------------------------------------------------------------+
  3764. // | Advanced: Response Recorder and Transactions |
  3765. // +------------------------------------------------------------+
  3766. // Record transforms the context's basic and direct responseWriter to a *ResponseRecorder
  3767. // which can be used to reset the body, reset headers, get the body,
  3768. // get & set the status code at any time and more.
  3769. func (ctx *Context) Record() {
  3770. switch w := ctx.writer.(type) {
  3771. case *ResponseRecorder:
  3772. default:
  3773. recorder := AcquireResponseRecorder()
  3774. recorder.BeginRecord(w)
  3775. ctx.ResetResponseWriter(recorder)
  3776. }
  3777. }
  3778. // Recorder returns the context's ResponseRecorder
  3779. // if not recording then it starts recording and returns the new context's ResponseRecorder
  3780. func (ctx *Context) Recorder() *ResponseRecorder {
  3781. ctx.Record()
  3782. return ctx.writer.(*ResponseRecorder)
  3783. }
  3784. // IsRecording returns the response recorder and a true value
  3785. // when the response writer is recording the status code, body, headers and so on,
  3786. // else returns nil and false.
  3787. func (ctx *Context) IsRecording() (*ResponseRecorder, bool) {
  3788. // NOTE:
  3789. // two return values in order to minimize the if statement:
  3790. // if (Recording) then writer = Recorder()
  3791. // instead we do: recorder,ok = Recording()
  3792. rr, ok := ctx.writer.(*ResponseRecorder)
  3793. return rr, ok
  3794. }
  3795. // ErrPanicRecovery may be returned from `Context` actions of a `Handler`
  3796. // which recovers from a manual panic.
  3797. // var ErrPanicRecovery = errors.New("recovery from panic")
  3798. // ErrTransactionInterrupt can be used to manually force-complete a Context's transaction
  3799. // and log(warn) the wrapped error's message.
  3800. // Usage: `... return fmt.Errorf("my custom error message: %w", context.ErrTransactionInterrupt)`.
  3801. var ErrTransactionInterrupt = errors.New("transaction interrupted")
  3802. // BeginTransaction starts a scoped transaction.
  3803. //
  3804. // Can't say a lot here because it will take more than 200 lines to write about.
  3805. // You can search third-party articles or books on how Business Transaction works (it's quite simple, especially here).
  3806. //
  3807. // Note that this is unique and new
  3808. // (=I haver never seen any other examples or code in Golang on this subject, so far, as with the most of iris features...)
  3809. // it's not covers all paths,
  3810. // such as databases, this should be managed by the libraries you use to make your database connection,
  3811. // this transaction scope is only for context's response.
  3812. // Transactions have their own middleware ecosystem also.
  3813. //
  3814. // See https://github.com/kataras/iris/tree/master/_examples/ for more
  3815. func (ctx *Context) BeginTransaction(pipe func(t *Transaction)) {
  3816. // do NOT begin a transaction when the previous transaction has been failed
  3817. // and it was requested scoped or SkipTransactions called manually.
  3818. if ctx.TransactionsSkipped() {
  3819. return
  3820. }
  3821. // start recording in order to be able to control the full response writer
  3822. ctx.Record()
  3823. t := newTransaction(ctx) // it calls this *context, so the overriding with a new pool's New of context.Context wil not work here.
  3824. defer func() {
  3825. if err := recover(); err != nil {
  3826. ctx.app.Logger().Warn(fmt.Errorf("recovery from panic: %w", ErrTransactionInterrupt))
  3827. // complete (again or not , doesn't matters) the scope without loud
  3828. t.Complete(nil)
  3829. // we continue as normal, no need to return here*
  3830. }
  3831. // write the temp contents to the original writer
  3832. t.Context().ResponseWriter().CopyTo(ctx.writer)
  3833. // give back to the transaction the original writer (SetBeforeFlush works this way and only this way)
  3834. // this is tricky but nessecery if we want ctx.FireStatusCode to work inside transactions
  3835. t.Context().ResetResponseWriter(ctx.writer)
  3836. }()
  3837. // run the worker with its context clone inside.
  3838. pipe(t)
  3839. }
  3840. // skipTransactionsContextKey set this to any value to stop executing next transactions
  3841. // it's a context-key in order to be used from anywhere, set it by calling the SkipTransactions()
  3842. const skipTransactionsContextKey = "iris.transactions.skip"
  3843. // SkipTransactions if called then skip the rest of the transactions
  3844. // or all of them if called before the first transaction
  3845. func (ctx *Context) SkipTransactions() {
  3846. ctx.values.Set(skipTransactionsContextKey, 1)
  3847. }
  3848. // TransactionsSkipped returns true if the transactions skipped or canceled at all.
  3849. func (ctx *Context) TransactionsSkipped() bool {
  3850. if n, err := ctx.values.GetInt(skipTransactionsContextKey); err == nil && n == 1 {
  3851. return true
  3852. }
  3853. return false
  3854. }
  3855. // Exec calls the framewrok's ServeHTTPC
  3856. // based on this context but with a changed method and path
  3857. // like it was requested by the user, but it is not.
  3858. //
  3859. // Offline means that the route is registered to the iris and have all features that a normal route has
  3860. // BUT it isn't available by browsing, its handlers executed only when other handler's context call them
  3861. // it can validate paths, has sessions, path parameters and all.
  3862. //
  3863. // You can find the Route by app.GetRoute("theRouteName")
  3864. // you can set a route name as: myRoute := app.Get("/mypath", handler)("theRouteName")
  3865. // that will set a name to the route and returns its RouteInfo instance for further usage.
  3866. //
  3867. // It doesn't changes the global state, if a route was "offline" it remains offline.
  3868. //
  3869. // app.None(...) and app.GetRoutes().Offline(route)/.Online(route, method)
  3870. //
  3871. // Example: https://github.com/kataras/iris/tree/master/_examples/routing/route-state
  3872. //
  3873. // User can get the response by simple using rec := ctx.Recorder(); rec.Body()/rec.StatusCode()/rec.Header().
  3874. //
  3875. // context's Values and the Session are kept in order to be able to communicate via the result route.
  3876. //
  3877. // It's for extreme use cases, 99% of the times will never be useful for you.
  3878. func (ctx *Context) Exec(method string, path string) {
  3879. if path == "" {
  3880. return
  3881. }
  3882. if method == "" {
  3883. method = "GET"
  3884. }
  3885. // backup the handlers
  3886. backupHandlers := ctx.handlers[0:]
  3887. backupPos := ctx.currentHandlerIndex
  3888. req := ctx.request
  3889. // backup the request path information
  3890. backupPath := req.URL.Path
  3891. backupMethod := req.Method
  3892. // don't backupValues := ctx.values.ReadOnly()
  3893. // set the request to be align with the 'againstRequestPath'
  3894. req.RequestURI = path
  3895. req.URL.Path = path
  3896. req.Method = method
  3897. // [values stays]
  3898. // reset handlers
  3899. ctx.handlers = ctx.handlers[0:0]
  3900. ctx.currentHandlerIndex = 0
  3901. // execute the route from the (internal) context router
  3902. // this way we keep the sessions and the values
  3903. ctx.app.ServeHTTPC(ctx)
  3904. // set the request back to its previous state
  3905. req.RequestURI = backupPath
  3906. req.URL.Path = backupPath
  3907. req.Method = backupMethod
  3908. // set back the old handlers and the last known index
  3909. ctx.handlers = backupHandlers
  3910. ctx.currentHandlerIndex = backupPos
  3911. }
  3912. // RouteExists reports whether a particular route exists
  3913. // It will search from the current subdomain of context's host, if not inside the root domain.
  3914. func (ctx *Context) RouteExists(method, path string) bool {
  3915. return ctx.app.RouteExists(ctx, method, path)
  3916. }
  3917. const (
  3918. reflectValueContextKey = "iris.context.reflect_value"
  3919. // ControllerContextKey returns the context key from which
  3920. // the `Context.Controller` method returns the store's value.
  3921. ControllerContextKey = "iris.controller.reflect_value"
  3922. )
  3923. // ReflectValue caches and returns a []reflect.Value{reflect.ValueOf(ctx)}.
  3924. // It's just a helper to maintain variable inside the context itself.
  3925. func (ctx *Context) ReflectValue() []reflect.Value {
  3926. if v := ctx.values.Get(reflectValueContextKey); v != nil {
  3927. return v.([]reflect.Value)
  3928. }
  3929. v := []reflect.Value{reflect.ValueOf(ctx)}
  3930. ctx.values.Set(reflectValueContextKey, v)
  3931. return v
  3932. }
  3933. var emptyValue reflect.Value
  3934. // Controller returns a reflect Value of the custom Controller from which this handler executed.
  3935. // It will return a Kind() == reflect.Invalid if the handler was not executed from within a controller.
  3936. func (ctx *Context) Controller() reflect.Value {
  3937. if v := ctx.values.Get(ControllerContextKey); v != nil {
  3938. return v.(reflect.Value)
  3939. }
  3940. return emptyValue
  3941. }
  3942. // DependenciesContextKey is the context key for the context's value
  3943. // to keep the serve-time static dependencies raw values.
  3944. const DependenciesContextKey = "iris.dependencies"
  3945. // DependenciesMap is the type which context serve-time
  3946. // struct dependencies are stored with.
  3947. type DependenciesMap map[reflect.Type]reflect.Value
  3948. // RegisterDependency registers a struct or slice
  3949. // or pointer to struct dependency at request-time
  3950. // for the next handler in the chain. One value per type.
  3951. // Note that it's highly recommended to register
  3952. // your dependencies before server ran
  3953. // through Party.ConfigureContainer or mvc.Application.Register
  3954. // in sake of minimum performance cost.
  3955. //
  3956. // See `UnregisterDependency` too.
  3957. func (ctx *Context) RegisterDependency(v interface{}) {
  3958. if v == nil {
  3959. return
  3960. }
  3961. val, ok := v.(reflect.Value)
  3962. if !ok {
  3963. val = reflect.ValueOf(v)
  3964. }
  3965. cv := ctx.values.Get(DependenciesContextKey)
  3966. if cv != nil {
  3967. m, ok := cv.(DependenciesMap)
  3968. if !ok {
  3969. return
  3970. }
  3971. m[val.Type()] = val
  3972. return
  3973. }
  3974. ctx.values.Set(DependenciesContextKey, DependenciesMap{
  3975. val.Type(): val,
  3976. })
  3977. }
  3978. // UnregisterDependency removes a dependency based on its type.
  3979. // Reports whether a dependency with that type was found and removed successfully.
  3980. func (ctx *Context) UnregisterDependency(typ reflect.Type) bool {
  3981. cv := ctx.values.Get(DependenciesContextKey)
  3982. if cv != nil {
  3983. m, ok := cv.(DependenciesMap)
  3984. if ok {
  3985. delete(m, typ)
  3986. return true
  3987. }
  3988. }
  3989. return false
  3990. }
  3991. // Application returns the iris app instance which belongs to this context.
  3992. // Worth to notice that this function returns an interface
  3993. // of the Application, which contains methods that are safe
  3994. // to be executed at serve-time. The full app's fields
  3995. // and methods are not available here for the developer's safety.
  3996. func (ctx *Context) Application() Application {
  3997. return ctx.app
  3998. }
  3999. const errorContextKey = "iris.context.error"
  4000. // SetErr is just a helper that sets an error value
  4001. // as a context value, it does nothing more.
  4002. // Also, by-default this error's value is written to the client
  4003. // on failures when no registered error handler is available (see `Party.On(Any)ErrorCode`).
  4004. // See `GetErr` to retrieve it back.
  4005. //
  4006. // To remove an error simply pass nil.
  4007. //
  4008. // Note that, if you want to stop the chain
  4009. // with an error see the `StopWithError/StopWithPlainError` instead.
  4010. func (ctx *Context) SetErr(err error) {
  4011. if err == nil {
  4012. ctx.values.Remove(errorContextKey)
  4013. return
  4014. }
  4015. ctx.values.Set(errorContextKey, err)
  4016. }
  4017. // GetErr is a helper which retrieves
  4018. // the error value stored by `SetErr`.
  4019. func (ctx *Context) GetErr() error {
  4020. if v := ctx.values.Get(errorContextKey); v != nil {
  4021. if err, ok := v.(error); ok {
  4022. return err
  4023. }
  4024. }
  4025. return nil
  4026. }
  4027. const idContextKey = "iris.context.id"
  4028. // SetID sets an ID, any value, to the Request Context.
  4029. // If possible the "id" should implement a `String() string` method
  4030. // so it can be rendered on `Context.String` method.
  4031. //
  4032. // See `GetID` and `middleware/requestid` too.
  4033. func (ctx *Context) SetID(id interface{}) {
  4034. ctx.values.Set(idContextKey, id)
  4035. }
  4036. // GetID returns the Request Context's ID.
  4037. // It returns nil if not given by a prior `SetID` call.
  4038. // See `middleware/requestid` too.
  4039. func (ctx *Context) GetID() interface{} {
  4040. return ctx.values.Get(idContextKey)
  4041. }
  4042. // String returns the string representation of this request.
  4043. //
  4044. // It returns the Context's ID given by a `SetID`call,
  4045. // followed by the client's IP and the method:uri.
  4046. func (ctx *Context) String() string {
  4047. return fmt.Sprintf("[%s] %s ▶ %s:%s", ctx.GetID(), ctx.RemoteAddr(), ctx.Method(), ctx.Request().RequestURI)
  4048. }