1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-23 21:07:03 +00:00

Add notes for the new lead maintainer of the open-source iris project and align with @get-ion/ion by @hiveminded

Former-commit-id: da4f38eb9034daa49446df3ee529423b98f9b331
This commit is contained in:
kataras
2017-07-10 18:32:42 +03:00
parent 2d4c2779a7
commit 9f85b74fc9
344 changed files with 4842 additions and 5174 deletions

View File

@@ -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
}
// +--------------------------------------------------------------+