|
|
|
|
@@ -1,7 +1,3 @@
|
|
|
|
|
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package context
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
@@ -32,7 +28,6 @@ import (
|
|
|
|
|
|
|
|
|
|
"github.com/kataras/iris/core/errors"
|
|
|
|
|
"github.com/kataras/iris/core/memstore"
|
|
|
|
|
"github.com/kataras/iris/sessions"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type (
|
|
|
|
|
@@ -142,7 +137,7 @@ func (r RequestParams) Len() int {
|
|
|
|
|
// Context is the midle-man server's "object" for the clients.
|
|
|
|
|
//
|
|
|
|
|
// A New context is being acquired from a sync.Pool on each connection.
|
|
|
|
|
// The Context is the most important thing on the Iris' http flow.
|
|
|
|
|
// The Context is the most important thing on the iris's http flow.
|
|
|
|
|
//
|
|
|
|
|
// Developers send responses to the client's request through a Context.
|
|
|
|
|
// Developers get request information from the client's request a Context.
|
|
|
|
|
@@ -155,7 +150,7 @@ type Context interface {
|
|
|
|
|
// BeginRequest is executing once for each request
|
|
|
|
|
// it should prepare the (new or acquired from pool) context's fields for the new request.
|
|
|
|
|
//
|
|
|
|
|
// To follow the Iris' flow, developer should:
|
|
|
|
|
// To follow the iris' flow, developer should:
|
|
|
|
|
// 1. reset handlers to nil
|
|
|
|
|
// 2. reset values to empty
|
|
|
|
|
// 3. reset sessions to nil
|
|
|
|
|
@@ -165,7 +160,7 @@ type Context interface {
|
|
|
|
|
BeginRequest(http.ResponseWriter, *http.Request)
|
|
|
|
|
// EndRequest is executing once after a response to the request was sent and this context is useless or released.
|
|
|
|
|
//
|
|
|
|
|
// To follow the Iris' flow, developer should:
|
|
|
|
|
// To follow the iris' flow, developer should:
|
|
|
|
|
// 1. flush the response writer's result
|
|
|
|
|
// 2. release the response writer
|
|
|
|
|
// and any other optional steps, depends on dev's application type.
|
|
|
|
|
@@ -249,7 +244,7 @@ type Context interface {
|
|
|
|
|
// Translate is the i18n (localization) middleware's function,
|
|
|
|
|
// it calls the Get("translate") to return the translated value.
|
|
|
|
|
//
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/i18n
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n
|
|
|
|
|
Translate(format string, args ...interface{}) string
|
|
|
|
|
|
|
|
|
|
// +------------------------------------------------------------+
|
|
|
|
|
@@ -270,7 +265,16 @@ type Context interface {
|
|
|
|
|
// Subdomain returns the subdomain of this request, if any.
|
|
|
|
|
// Note that this is a fast method which does not cover all cases.
|
|
|
|
|
Subdomain() (subdomain string)
|
|
|
|
|
// RemoteAddr tries to return the real client's request IP.
|
|
|
|
|
// RemoteAddr tries to parse and return the real client's request IP.
|
|
|
|
|
//
|
|
|
|
|
// Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders.
|
|
|
|
|
//
|
|
|
|
|
// If parse based on these headers fail then it will return the Request's `RemoteAddr` field
|
|
|
|
|
// which is filled by the server before the HTTP handler.
|
|
|
|
|
//
|
|
|
|
|
// Look `Configuration.RemoteAddrHeaders`,
|
|
|
|
|
// `Configuration.WithRemoteAddrHeader(...)`,
|
|
|
|
|
// `Configuration.WithoutRemoteAddrHeader(...)` for more.
|
|
|
|
|
RemoteAddr() string
|
|
|
|
|
// GetHeader returns the request header's value based on its name.
|
|
|
|
|
GetHeader(name string) string
|
|
|
|
|
@@ -344,7 +348,7 @@ type Context interface {
|
|
|
|
|
|
|
|
|
|
// NotFound emits an error 404 to the client, using the specific custom error error handler.
|
|
|
|
|
// Note that you may need to call ctx.StopExecution() if you don't want the next handlers
|
|
|
|
|
// to be executed. Next handlers are being executed on Iris because you can alt the
|
|
|
|
|
// to be executed. Next handlers are being executed on iris because you can alt the
|
|
|
|
|
// error code and change it to a more specific one, i.e
|
|
|
|
|
// users := app.Party("/users")
|
|
|
|
|
// users.Done(func(ctx context.Context){ if ctx.StatusCode() == 400 { /* custom error code for /users */ }})
|
|
|
|
|
@@ -401,9 +405,9 @@ type Context interface {
|
|
|
|
|
//
|
|
|
|
|
// Returns the number of bytes written and any write error encountered.
|
|
|
|
|
WriteString(body string) (int, error)
|
|
|
|
|
// WriteWithExpiration like SetBody but it sends with an expiration datetime
|
|
|
|
|
// which is managed by the client-side (all major web browsers supports this)
|
|
|
|
|
WriteWithExpiration(bodyContent []byte, cType string, modtime time.Time) error
|
|
|
|
|
// WriteWithExpiration like Write but it sends with an expiration datetime
|
|
|
|
|
// which is refreshed every package-level `StaticCacheDuration` field.
|
|
|
|
|
WriteWithExpiration(body []byte, modtime time.Time) (int, error)
|
|
|
|
|
// StreamWriter registers the given stream writer for populating
|
|
|
|
|
// response body.
|
|
|
|
|
//
|
|
|
|
|
@@ -459,7 +463,7 @@ type Context interface {
|
|
|
|
|
//
|
|
|
|
|
// Look .ViewData and .View too.
|
|
|
|
|
//
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/context-view-data/
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
|
|
|
|
|
ViewLayout(layoutTmplFile string)
|
|
|
|
|
|
|
|
|
|
// ViewData saves one or more key-value pair in order to be passed if and when .View
|
|
|
|
|
@@ -479,7 +483,7 @@ type Context interface {
|
|
|
|
|
//
|
|
|
|
|
// Look .ViewLayout and .View too.
|
|
|
|
|
//
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/context-view-data/
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
|
|
|
|
|
ViewData(key string, value interface{})
|
|
|
|
|
|
|
|
|
|
// View renders templates based on the adapted view engines.
|
|
|
|
|
@@ -489,7 +493,7 @@ type Context interface {
|
|
|
|
|
//
|
|
|
|
|
// Look: .ViewData and .ViewLayout too.
|
|
|
|
|
//
|
|
|
|
|
// Examples: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/
|
|
|
|
|
// Examples: https://github.com/kataras/iris/tree/master/_examples/view/
|
|
|
|
|
View(filename string) error
|
|
|
|
|
|
|
|
|
|
// Binary writes out the raw bytes as binary data.
|
|
|
|
|
@@ -533,7 +537,7 @@ type Context interface {
|
|
|
|
|
SendFile(filename string, destinationName string) error
|
|
|
|
|
|
|
|
|
|
// +------------------------------------------------------------+
|
|
|
|
|
// | Cookies, Session and Flashes |
|
|
|
|
|
// | Cookies |
|
|
|
|
|
// +------------------------------------------------------------+
|
|
|
|
|
|
|
|
|
|
// SetCookie adds a cookie
|
|
|
|
|
@@ -552,11 +556,6 @@ type Context interface {
|
|
|
|
|
// on each (request's) cookies' name and value.
|
|
|
|
|
VisitAllCookies(visitor func(name string, value string))
|
|
|
|
|
|
|
|
|
|
// Session returns the current user's Session.
|
|
|
|
|
Session() sessions.Session
|
|
|
|
|
// SessionDestroy destroys the whole session and removes the session id cookie.
|
|
|
|
|
SessionDestroy()
|
|
|
|
|
|
|
|
|
|
// MaxAge returns the "cache-control" request header's value
|
|
|
|
|
// seconds as int64
|
|
|
|
|
// if header not found or parse failed then it returns -1.
|
|
|
|
|
@@ -589,7 +588,7 @@ type Context interface {
|
|
|
|
|
// this transaction scope is only for context's response.
|
|
|
|
|
// Transactions have their own middleware ecosystem also, look iris.go:UseTransaction.
|
|
|
|
|
//
|
|
|
|
|
// See https://github.com/kataras/iris/tree/master/_examples/advanced/transactions for more
|
|
|
|
|
// See https://github.com/kataras/iris/tree/master/_examples/ for more
|
|
|
|
|
BeginTransaction(pipe func(t *Transaction))
|
|
|
|
|
// SkipTransactions if called then skip the rest of the transactions
|
|
|
|
|
// or all of them if called before the first transaction
|
|
|
|
|
@@ -613,7 +612,7 @@ type Context interface {
|
|
|
|
|
//
|
|
|
|
|
// app.None(...) and app.Routes().Offline(route)/.Online(route, method)
|
|
|
|
|
//
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/route-state
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/routing/route-state
|
|
|
|
|
//
|
|
|
|
|
// User can get the response by simple using rec := ctx.Recorder(); rec.Body()/rec.StatusCode()/rec.Header().
|
|
|
|
|
//
|
|
|
|
|
@@ -622,10 +621,10 @@ type Context interface {
|
|
|
|
|
// It's for extreme use cases, 99% of the times will never be useful for you.
|
|
|
|
|
Exec(method string, path string)
|
|
|
|
|
|
|
|
|
|
// Application returns the Iris framework instance which belongs to this context.
|
|
|
|
|
// Application returns the iris app instance which belongs to this context.
|
|
|
|
|
// Worth to notice that this function returns an interface
|
|
|
|
|
// of the Application, which contains methods that are safe
|
|
|
|
|
// to be executed at serve-time. The full framework's fields
|
|
|
|
|
// to be executed at serve-time. The full app's fields
|
|
|
|
|
// and methods are not available here for the developer's safety.
|
|
|
|
|
Application() Application
|
|
|
|
|
|
|
|
|
|
@@ -735,8 +734,8 @@ func Next(ctx Context) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LimitRequestBodySize is a middleware which sets a request body size limit for all next handlers
|
|
|
|
|
// should be registered before all other handlers
|
|
|
|
|
// LimitRequestBodySize is a middleware which sets a request body size limit
|
|
|
|
|
// for all next handlers in the chain.
|
|
|
|
|
var LimitRequestBodySize = func(maxRequestBodySizeBytes int64) Handler {
|
|
|
|
|
return func(ctx Context) {
|
|
|
|
|
ctx.SetMaxRequestBodySize(maxRequestBodySizeBytes)
|
|
|
|
|
@@ -760,12 +759,10 @@ type context struct {
|
|
|
|
|
params RequestParams // url named parameters
|
|
|
|
|
values memstore.Store // generic storage, middleware communication
|
|
|
|
|
|
|
|
|
|
// the underline application framework
|
|
|
|
|
framework Application
|
|
|
|
|
// the underline application app
|
|
|
|
|
app Application
|
|
|
|
|
// the route's handlers
|
|
|
|
|
handlers Handlers
|
|
|
|
|
// the session, can be nil if never acquired
|
|
|
|
|
session sessions.Session
|
|
|
|
|
// the current position of the handler's chain
|
|
|
|
|
currentHandlerIndex int
|
|
|
|
|
}
|
|
|
|
|
@@ -775,14 +772,14 @@ type context struct {
|
|
|
|
|
// to a custom one.
|
|
|
|
|
//
|
|
|
|
|
// This context is received by the context pool.
|
|
|
|
|
func NewContext(framework Application) Context {
|
|
|
|
|
return &context{framework: framework}
|
|
|
|
|
func NewContext(app Application) Context {
|
|
|
|
|
return &context{app: app}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BeginRequest is executing once for each request
|
|
|
|
|
// it should prepare the (new or acquired from pool) context's fields for the new request.
|
|
|
|
|
//
|
|
|
|
|
// To follow the Iris' flow, developer should:
|
|
|
|
|
// To follow the iris' flow, developer should:
|
|
|
|
|
// 1. reset handlers to nil
|
|
|
|
|
// 2. reset store to empty
|
|
|
|
|
// 3. reset sessions to nil
|
|
|
|
|
@@ -791,7 +788,6 @@ func NewContext(framework Application) Context {
|
|
|
|
|
// and any other optional steps, depends on dev's application type.
|
|
|
|
|
func (ctx *context) BeginRequest(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
ctx.handlers = nil // will be filled by router.Serve/HTTP
|
|
|
|
|
ctx.session = nil // >> >> by sessions.Session()
|
|
|
|
|
ctx.values = ctx.values[0:0] // >> >> by context.Values().Set
|
|
|
|
|
ctx.params.store = ctx.params.store[0:0]
|
|
|
|
|
ctx.request = r
|
|
|
|
|
@@ -802,7 +798,7 @@ func (ctx *context) BeginRequest(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
|
|
|
|
// EndRequest is executing once after a response to the request was sent and this context is useless or released.
|
|
|
|
|
//
|
|
|
|
|
// To follow the Iris' flow, developer should:
|
|
|
|
|
// To follow the iris' flow, developer should:
|
|
|
|
|
// 1. flush the response writer's result
|
|
|
|
|
// 2. release the response writer
|
|
|
|
|
// and any other optional steps, depends on dev's application type.
|
|
|
|
|
@@ -961,7 +957,7 @@ func (ctx *context) Values() *memstore.Store {
|
|
|
|
|
// Translate is the i18n (localization) middleware's function,
|
|
|
|
|
// it calls the Get("translate") to return the translated value.
|
|
|
|
|
//
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/i18n
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/miscellaneous/i18n
|
|
|
|
|
func (ctx *context) Translate(format string, args ...interface{}) string {
|
|
|
|
|
if cb, ok := ctx.values.Get(ctx.Application().ConfigurationReadOnly().GetTranslateFunctionContextKey()).(func(format string, args ...interface{}) string); ok {
|
|
|
|
|
return cb(format, args...)
|
|
|
|
|
@@ -1056,8 +1052,8 @@ func (ctx *context) Subdomain() (subdomain string) {
|
|
|
|
|
subdomain = host[0:index]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// listening on iris-go.com:80
|
|
|
|
|
// subdomain = iris-go, but it's wrong, it should return ""
|
|
|
|
|
// listening on mydomain.com:80
|
|
|
|
|
// subdomain = mydomain, but it's wrong, it should return ""
|
|
|
|
|
vhost := ctx.Application().ConfigurationReadOnly().GetVHost()
|
|
|
|
|
if strings.Contains(vhost, subdomain) { // then it's not subdomain
|
|
|
|
|
return ""
|
|
|
|
|
@@ -1066,30 +1062,46 @@ func (ctx *context) Subdomain() (subdomain string) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RemoteAddr tries to return the real client's request IP.
|
|
|
|
|
// RemoteAddr tries to parse and return the real client's request IP.
|
|
|
|
|
//
|
|
|
|
|
// Based on allowed headers names that can be modified from Configuration.RemoteAddrHeaders.
|
|
|
|
|
//
|
|
|
|
|
// If parse based on these headers fail then it will return the Request's `RemoteAddr` field
|
|
|
|
|
// which is filled by the server before the HTTP handler.
|
|
|
|
|
//
|
|
|
|
|
// Look `Configuration.RemoteAddrHeaders`,
|
|
|
|
|
// `Configuration.WithRemoteAddrHeader(...)`,
|
|
|
|
|
// `Configuration.WithoutRemoteAddrHeader(...)` for more.
|
|
|
|
|
func (ctx *context) RemoteAddr() string {
|
|
|
|
|
header := ctx.GetHeader("X-Real-Ip")
|
|
|
|
|
realIP := strings.TrimSpace(header)
|
|
|
|
|
if realIP != "" {
|
|
|
|
|
return realIP
|
|
|
|
|
}
|
|
|
|
|
realIP = ctx.GetHeader("X-Forwarded-For")
|
|
|
|
|
idx := strings.IndexByte(realIP, ',')
|
|
|
|
|
if idx >= 0 {
|
|
|
|
|
realIP = realIP[0:idx]
|
|
|
|
|
}
|
|
|
|
|
realIP = strings.TrimSpace(realIP)
|
|
|
|
|
if realIP != "" {
|
|
|
|
|
return realIP
|
|
|
|
|
|
|
|
|
|
remoteHeaders := ctx.Application().ConfigurationReadOnly().GetRemoteAddrHeaders()
|
|
|
|
|
|
|
|
|
|
for headerName, enabled := range remoteHeaders {
|
|
|
|
|
if enabled {
|
|
|
|
|
headerValue := ctx.GetHeader(headerName)
|
|
|
|
|
// exception needed for 'X-Forwarded-For' only , if enabled.
|
|
|
|
|
if headerName == "X-Forwarded-For" {
|
|
|
|
|
idx := strings.IndexByte(headerValue, ',')
|
|
|
|
|
if idx >= 0 {
|
|
|
|
|
headerValue = headerValue[0:idx]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
realIP := strings.TrimSpace(headerValue)
|
|
|
|
|
if realIP != "" {
|
|
|
|
|
return realIP
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addr := strings.TrimSpace(ctx.request.RemoteAddr)
|
|
|
|
|
if len(addr) == 0 {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
// if addr has port use the net.SplitHostPort otherwise(error occurs) take as it is
|
|
|
|
|
if ip, _, err := net.SplitHostPort(addr); err == nil {
|
|
|
|
|
return ip
|
|
|
|
|
if addr != "" {
|
|
|
|
|
// if addr has port use the net.SplitHostPort otherwise(error occurs) take as it is
|
|
|
|
|
if ip, _, err := net.SplitHostPort(addr); err == nil {
|
|
|
|
|
return ip
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return addr
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1160,7 +1172,7 @@ func (ctx *context) StatusCode(statusCode int) {
|
|
|
|
|
|
|
|
|
|
// NotFound emits an error 404 to the client, using the specific custom error error handler.
|
|
|
|
|
// Note that you may need to call ctx.StopExecution() if you don't want the next handlers
|
|
|
|
|
// to be executed. Next handlers are being executed on Iris because you can alt the
|
|
|
|
|
// to be executed. Next handlers are being executed on iris because you can alt the
|
|
|
|
|
// error code and change it to a more specific one, i.e
|
|
|
|
|
// users := app.Party("/users")
|
|
|
|
|
// users.Done(func(ctx context.Context){ if ctx.StatusCode() == 400 { /* custom error code for /users */ }})
|
|
|
|
|
@@ -1264,24 +1276,6 @@ func (ctx *context) Redirect(urlToRedirect string, statusHeader ...int) {
|
|
|
|
|
httpStatus = statusHeader[0]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// comment these because in some cases the the ctx.Request().URL.Path is already updated
|
|
|
|
|
// to the new one, so it shows a wrong warning message.
|
|
|
|
|
//
|
|
|
|
|
// // we don't know the Method of the url to redirect,
|
|
|
|
|
// // sure we can find it by reverse routing as we already implemented
|
|
|
|
|
// // but it will take too much time for a simple redirect, it doesn't worth it.
|
|
|
|
|
// // So we are checking the CURRENT Method for GET, HEAD, CONNECT and TRACE.
|
|
|
|
|
// // the
|
|
|
|
|
// // Fixes: http: //support.iris-go.com/d/21-wrong-warning-message-while-redirecting
|
|
|
|
|
// shouldCheckForCycle := urlToRedirect == ctx.Path() && ctx.Method() == http.MethodGet
|
|
|
|
|
// // from POST to GET on the same path will give a warning message but developers don't use the iris.DevLogger
|
|
|
|
|
// // for production, so I assume it's OK to let it logs it
|
|
|
|
|
// // (it can solve issues when developer redirects to the same handler over and over again)
|
|
|
|
|
// // Note: it doesn't stops the redirect, the developer gets what he/she expected.
|
|
|
|
|
// if shouldCheckForCycle {
|
|
|
|
|
// ctx.Application().Log("warning: redirect from: '%s' to: '%s',\ncurrent method: '%s'", ctx.Path(), urlToRedirect, ctx.Method())
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
http.Redirect(ctx.writer, ctx.request, urlToRedirect, httpStatus)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1356,7 +1350,7 @@ func (ctx *context) ReadForm(formObject interface{}) error {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// or dec := formam.NewDecoder(&formam.DecoderOptions{TagName: "form"})
|
|
|
|
|
// somewhere at the framework level. I did change the tagName to "form"
|
|
|
|
|
// somewhere at the app level. I did change the tagName to "form"
|
|
|
|
|
// inside its source code, so it's not needed for now.
|
|
|
|
|
return errReadBody.With(formam.Decode(values, formObject))
|
|
|
|
|
}
|
|
|
|
|
@@ -1429,19 +1423,17 @@ func (ctx *context) staticCachePassed(modtime time.Time) bool {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WriteWithExpiration like Write but it sends with an expiration datetime
|
|
|
|
|
// which is managed by the client-side (all major web browsers supports this)
|
|
|
|
|
func (ctx *context) WriteWithExpiration(bodyContent []byte, cType string, modtime time.Time) error {
|
|
|
|
|
// which is refreshed every package-level `StaticCacheDuration` field.
|
|
|
|
|
func (ctx *context) WriteWithExpiration(body []byte, modtime time.Time) (int, error) {
|
|
|
|
|
|
|
|
|
|
if ctx.staticCachePassed(modtime) {
|
|
|
|
|
return nil
|
|
|
|
|
return 0, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modtimeFormatted := modtime.UTC().Format(ctx.Application().ConfigurationReadOnly().GetTimeFormat())
|
|
|
|
|
ctx.Header(lastModifiedHeaderKey, modtimeFormatted)
|
|
|
|
|
|
|
|
|
|
ctx.writer.Header().Set(contentTypeHeaderKey, cType)
|
|
|
|
|
ctx.writer.Header().Set(lastModifiedHeaderKey, modtimeFormatted)
|
|
|
|
|
|
|
|
|
|
_, err := ctx.writer.Write(bodyContent)
|
|
|
|
|
return err
|
|
|
|
|
return ctx.writer.Write(body)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StreamWriter registers the given stream writer for populating
|
|
|
|
|
@@ -1584,7 +1576,7 @@ const (
|
|
|
|
|
//
|
|
|
|
|
// Look .ViewData and .View too.
|
|
|
|
|
//
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/context-view-data/
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
|
|
|
|
|
func (ctx *context) ViewLayout(layoutTmplFile string) {
|
|
|
|
|
ctx.values.Set(ctx.Application().ConfigurationReadOnly().GetViewLayoutContextKey(), layoutTmplFile)
|
|
|
|
|
}
|
|
|
|
|
@@ -1606,7 +1598,7 @@ func (ctx *context) ViewLayout(layoutTmplFile string) {
|
|
|
|
|
//
|
|
|
|
|
// Look .ViewLayout and .View too.
|
|
|
|
|
//
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/context-view-data/
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/view/context-view-data/
|
|
|
|
|
func (ctx *context) ViewData(key string, value interface{}) {
|
|
|
|
|
viewDataContextKey := ctx.Application().ConfigurationReadOnly().GetViewDataContextKey()
|
|
|
|
|
if key == "" {
|
|
|
|
|
@@ -1620,7 +1612,9 @@ func (ctx *context) ViewData(key string, value interface{}) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if data, ok := v.(Map); ok {
|
|
|
|
|
if data, ok := v.(map[string]interface{}); ok {
|
|
|
|
|
data[key] = value
|
|
|
|
|
} else if data, ok := v.(Map); ok {
|
|
|
|
|
data[key] = value
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -1632,15 +1626,18 @@ func (ctx *context) ViewData(key string, value interface{}) {
|
|
|
|
|
//
|
|
|
|
|
// Look: .ViewData and .ViewLayout too.
|
|
|
|
|
//
|
|
|
|
|
// Examples: https://github.com/kataras/iris/tree/master/_examples/intermediate/view/
|
|
|
|
|
// Examples: https://github.com/kataras/iris/tree/master/_examples/view/
|
|
|
|
|
func (ctx *context) View(filename string) error {
|
|
|
|
|
ctx.ContentType(contentHTMLHeaderValue)
|
|
|
|
|
layout := ctx.values.GetString(ctx.Application().ConfigurationReadOnly().GetViewLayoutContextKey())
|
|
|
|
|
bindingData := ctx.values.Get(ctx.Application().ConfigurationReadOnly().GetViewDataContextKey())
|
|
|
|
|
cfg := ctx.Application().ConfigurationReadOnly()
|
|
|
|
|
|
|
|
|
|
layout := ctx.values.GetString(cfg.GetViewLayoutContextKey())
|
|
|
|
|
bindingData := ctx.values.Get(cfg.GetViewDataContextKey())
|
|
|
|
|
|
|
|
|
|
err := ctx.Application().View(ctx.writer, filename, layout, bindingData)
|
|
|
|
|
if err != nil {
|
|
|
|
|
ctx.StatusCode(http.StatusInternalServerError)
|
|
|
|
|
ctx.StopExecution()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
@@ -1682,6 +1679,36 @@ func (ctx *context) HTML(htmlContents string) (int, error) {
|
|
|
|
|
return ctx.writer.WriteString(htmlContents)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// JSON contains the options for the JSON (Context's) Renderer.
|
|
|
|
|
type JSON struct {
|
|
|
|
|
// http-specific
|
|
|
|
|
StreamingJSON bool
|
|
|
|
|
// content-specific
|
|
|
|
|
UnescapeHTML bool
|
|
|
|
|
Indent string
|
|
|
|
|
Prefix string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// JSONP contains the options for the JSONP (Context's) Renderer.
|
|
|
|
|
type JSONP struct {
|
|
|
|
|
// content-specific
|
|
|
|
|
Indent string
|
|
|
|
|
Callback string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// XML contains the options for the XML (Context's) Renderer.
|
|
|
|
|
type XML struct {
|
|
|
|
|
// content-specific
|
|
|
|
|
Indent string
|
|
|
|
|
Prefix string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Markdown contains the options for the Markdown (Context's) Renderer.
|
|
|
|
|
type Markdown struct {
|
|
|
|
|
// content-specific
|
|
|
|
|
Sanitize bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
newLineB = []byte("\n")
|
|
|
|
|
// the html codes for unescaping
|
|
|
|
|
@@ -2022,31 +2049,6 @@ func (ctx *context) VisitAllCookies(visitor func(name string, value string)) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Session returns the current user's Session.
|
|
|
|
|
func (ctx *context) Session() sessions.Session {
|
|
|
|
|
sessmanager, err := ctx.Application().SessionManager()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ctx.session == nil {
|
|
|
|
|
ctx.session = sessmanager.Start(ctx.writer, ctx.request)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ctx.session
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SessionDestroy destroys the whole session and removes the session id cookie.
|
|
|
|
|
func (ctx *context) SessionDestroy() {
|
|
|
|
|
if sess := ctx.Session(); sess != nil {
|
|
|
|
|
sessmanager, err := ctx.Application().SessionManager()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
sessmanager.Destroy(ctx.writer, ctx.request)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var maxAgeExp = regexp.MustCompile(`maxage=(\d+)`)
|
|
|
|
|
|
|
|
|
|
// MaxAge returns the "cache-control" request header's value
|
|
|
|
|
@@ -2115,7 +2117,7 @@ var errTransactionInterrupted = errors.New("transaction interrupted, recovery fr
|
|
|
|
|
// this transaction scope is only for context's response.
|
|
|
|
|
// Transactions have their own middleware ecosystem also, look iris.go:UseTransaction.
|
|
|
|
|
//
|
|
|
|
|
// See https://github.com/kataras/iris/tree/master/_examples/advanced/transactions for more
|
|
|
|
|
// See https://github.com/kataras/iris/tree/master/_examples/ for more
|
|
|
|
|
func (ctx *context) BeginTransaction(pipe func(t *Transaction)) {
|
|
|
|
|
// do NOT begin a transaction when the previous transaction has been failed
|
|
|
|
|
// and it was requested scoped or SkipTransactions called manually.
|
|
|
|
|
@@ -2129,7 +2131,7 @@ func (ctx *context) BeginTransaction(pipe func(t *Transaction)) {
|
|
|
|
|
t := newTransaction(ctx) // it calls this *context, so the overriding with a new pool's New of context.Context wil not work here.
|
|
|
|
|
defer func() {
|
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
|
ctx.Application().Log(errTransactionInterrupted.Format(err).Error())
|
|
|
|
|
ctx.Application().Logger().Warnln(errTransactionInterrupted.Format(err).Error())
|
|
|
|
|
// complete (again or not , doesn't matters) the scope without loud
|
|
|
|
|
t.Complete(nil)
|
|
|
|
|
// we continue as normal, no need to return here*
|
|
|
|
|
@@ -2181,7 +2183,7 @@ func (ctx *context) TransactionsSkipped() bool {
|
|
|
|
|
//
|
|
|
|
|
// app.None(...) and app.Routes().Offline(route)/.Online(route, method)
|
|
|
|
|
//
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/intermediate/route-state
|
|
|
|
|
// Example: https://github.com/kataras/iris/tree/master/_examples/routing/route-state
|
|
|
|
|
//
|
|
|
|
|
// User can get the response by simple using rec := ctx.Recorder(); rec.Body()/rec.StatusCode()/rec.Header().
|
|
|
|
|
//
|
|
|
|
|
@@ -2233,13 +2235,13 @@ func (ctx *context) Exec(method string, path string) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Application returns the Iris framework instance which belongs to this context.
|
|
|
|
|
// Application returns the iris app instance which belongs to this context.
|
|
|
|
|
// Worth to notice that this function returns an interface
|
|
|
|
|
// of the Application, which contains methods that are safe
|
|
|
|
|
// to be executed at serve-time. The full framework's fields
|
|
|
|
|
// to be executed at serve-time. The full app's fields
|
|
|
|
|
// and methods are not available here for the developer's safety.
|
|
|
|
|
func (ctx *context) Application() Application {
|
|
|
|
|
return ctx.framework
|
|
|
|
|
return ctx.app
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// +--------------------------------------------------------------+
|
|
|
|
|
|