1
0
mirror of https://github.com/kataras/iris.git synced 2026-01-08 04:21:57 +00:00

Embrace the weekend- Update to rc.3 | Read the HISTORY.md

This commit is contained in:
Makis Maropoulos
2016-06-24 01:34:49 +03:00
parent 4a446ac1e2
commit f83b532835
50 changed files with 142 additions and 3812 deletions

View File

@@ -1,2 +0,0 @@
# Middleware
Iris has its tiny middlewares here.

View File

@@ -1,157 +0,0 @@
package basicauth
import (
"encoding/base64"
"strconv"
"time"
"github.com/kataras/iris"
"github.com/kataras/iris/config"
)
type (
encodedUser struct {
HeaderValue string
Username string
logged bool
expires time.Time
}
encodedUsers []encodedUser
basicAuthMiddleware struct {
config config.BasicAuth
// these are filled from the config.Users map at the startup
auth encodedUsers
realmHeaderValue string
expireEnabled bool // if the config.Expires is a valid date, default disabled
}
)
//
// New takes one parameter, the config.BasicAuth returns a HandlerFunc
// use: iris.UseFunc(New(...)), iris.Get(...,New(...),...)
func New(c config.BasicAuth) iris.HandlerFunc {
return NewHandler(c).Serve
}
// NewHandler takes one parameter, the config.BasicAuth returns a Handler
// use: iris.Use(NewHandler(...)), iris.Get(...,iris.HandlerFunc(NewHandler(...)),...)
func NewHandler(c config.BasicAuth) iris.Handler {
b := &basicAuthMiddleware{config: config.DefaultBasicAuth().MergeSingle(c)}
b.init()
return b
}
// Default takes one parameter, the users returns a HandlerFunc
// use: iris.UseFunc(Default(...)), iris.Get(...,Default(...),...)
func Default(users map[string]string) iris.HandlerFunc {
return DefaultHandler(users).Serve
}
// DefaultHandler takes one parameter, the users returns a Handler
// use: iris.Use(DefaultHandler(...)), iris.Get(...,iris.HandlerFunc(Default(...)),...)
func DefaultHandler(users map[string]string) iris.Handler {
c := config.DefaultBasicAuth()
c.Users = users
return NewHandler(c)
}
//
func (b *basicAuthMiddleware) init() {
// pass the encoded users from the user's config's Users value
b.auth = make(encodedUsers, 0, len(b.config.Users))
for k, v := range b.config.Users {
fullUser := k + ":" + v
header := "Basic " + base64.StdEncoding.EncodeToString([]byte(fullUser))
b.auth = append(b.auth, encodedUser{HeaderValue: header, Username: k, logged: false, expires: config.CookieExpireNever})
}
// set the auth realm header's value
b.realmHeaderValue = "Basic realm=" + strconv.Quote(b.config.Realm)
if b.config.Expires > 0 {
b.expireEnabled = true
}
}
func (b *basicAuthMiddleware) findAuth(headerValue string) (auth *encodedUser, found bool) {
if len(headerValue) == 0 {
return
}
for _, user := range b.auth {
if user.HeaderValue == headerValue {
auth = &user
found = true
break
}
}
return
}
func (b *basicAuthMiddleware) askForCredentials(ctx *iris.Context) {
ctx.SetHeader("WWW-Authenticate", b.realmHeaderValue)
ctx.SetStatusCode(iris.StatusUnauthorized)
}
// Serve the actual middleware
func (b *basicAuthMiddleware) Serve(ctx *iris.Context) {
if auth, found := b.findAuth(ctx.RequestHeader("Authorization")); !found {
/* I spent time for nothing
if b.banEnabled && auth != nil { // this propably never work
if auth.tries == b.config.MaxTries {
auth.bannedTime = time.Now()
auth.unbanTime = time.Now().Add(b.config.BanDuration) // set the unban time
auth.tries++ // we plus them in order to check if already banned later
// client is banned send a forbidden status and don't continue
ctx.SetStatusCode(iris.StatusForbidden)
return
} else if auth.tries > b.config.MaxTries { // it's already banned, so check the ban duration with the bannedTime
if time.Now().After(auth.unbanTime) { // here we unban the client
auth.tries = 0
auth.bannedTime = config.CookieExpireNever
auth.unbanTime = config.CookieExpireNever
// continue and askCredentials as normal
} else {
// client is banned send a forbidden status and don't continue
ctx.SetStatusCode(iris.StatusForbidden)
return
}
}
}
if auth != nil {
auth.tries++
}*/
b.askForCredentials(ctx)
// don't continue to the next handler
} else {
// all ok set the context's value in order to be getable from the next handler
ctx.Set(b.config.ContextKey, auth.Username)
if b.expireEnabled {
if auth.logged == false {
auth.expires = time.Now().Add(b.config.Expires)
auth.logged = true
}
if time.Now().After(auth.expires) {
b.askForCredentials(ctx) // ask for authentication again
return
}
}
//auth.tries = 0
ctx.Next() // continue
}
}

View File

@@ -1,97 +0,0 @@
## Middleware information
This is a fork of the CORS middleware from [here](https://github.com/rs/cors/)
## Description
It does some security work for you between the requests, a brief view on what you can set:
* AllowedOrigins []string
* AllowOriginFunc func(origin string) bool
* AllowedMethods []string
* AllowedHeadersAll bool
* ExposedHeaders []string
* AllowCredentials bool
* MaxAge int
* OptionsPassthrough bool
## Options
```go
// AllowedOrigins is a list of origins a cross-domain request can be executed from.
// If the special "*" value is present in the list, all origins will be allowed.
// An origin may contain a wildcard (*) to replace 0 or more characters
// (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
// Only one wildcard can be used per origin.
// Default value is ["*"]
AllowedOrigins []string
// AllowOriginFunc is a custom function to validate the origin. It take the origin
// as argument and returns true if allowed or false otherwise. If this option is
// set, the content of AllowedOrigins is ignored.
AllowOriginFunc func(origin string) bool
// AllowedMethods is a list of methods the client is allowed to use with
// cross-domain requests. Default value is simple methods (GET and POST)
AllowedMethods []string
// AllowedHeaders is list of non simple headers the client is allowed to use with
// cross-domain requests.
// If the special "*" value is present in the list, all headers will be allowed.
// Default value is [] but "Origin" is always appended to the list.
AllowedHeaders []string
AllowedHeadersAll bool
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
// API specification
ExposedHeaders []string
// AllowCredentials indicates whether the request can include user credentials like
// cookies, HTTP authentication or client side SSL certificates.
AllowCredentials bool
// MaxAge indicates how long (in seconds) the results of a preflight request
// can be cached
MaxAge int
// OptionsPassthrough instructs preflight to let other potential next handlers to
// process the OPTIONS method. Turn this on if your application handles OPTIONS.
OptionsPassthrough bool
// Debugging flag adds additional output to debug server side CORS issues
Debug bool
```
## How to use
```go
package main
import (
"github.com/kataras/iris"
"github.com/kataras/iris/middleware/cors"
)
func main() {
//crs := cors.New(cors.Options{})
iris.Use(cors.Default()) // crs
iris.Get("/home", func(c *iris.Context) {
c.Write("Hello from /home")
})
println("Server is running at :8080")
iris.Listen(":8080")
}
```

View File

@@ -1,405 +0,0 @@
// Cors credits goes to @rs
package cors
import (
"log"
"net/http"
"os"
"strconv"
"strings"
"github.com/kataras/iris"
)
const toLower = 'a' - 'A'
type converter func(string) string
type wildcard struct {
prefix string
suffix string
}
func (w wildcard) match(s string) bool {
return len(s) >= len(w.prefix+w.suffix) && strings.HasPrefix(s, w.prefix) && strings.HasSuffix(s, w.suffix)
}
// convert converts a list of string using the passed converter function
func convert(s []string, c converter) []string {
out := []string{}
for _, i := range s {
out = append(out, c(i))
}
return out
}
// parseHeaderList tokenize + normalize a string containing a list of headers
func parseHeaderList(headerList string) []string {
l := len(headerList)
h := make([]byte, 0, l)
upper := true
// Estimate the number headers in order to allocate the right splice size
t := 0
for i := 0; i < l; i++ {
if headerList[i] == ',' {
t++
}
}
headers := make([]string, 0, t)
for i := 0; i < l; i++ {
b := headerList[i]
if b >= 'a' && b <= 'z' {
if upper {
h = append(h, b-toLower)
} else {
h = append(h, b)
}
} else if b >= 'A' && b <= 'Z' {
if !upper {
h = append(h, b+toLower)
} else {
h = append(h, b)
}
} else if b == '-' || b == '_' || (b >= '0' && b <= '9') {
h = append(h, b)
}
if b == ' ' || b == ',' || i == l-1 {
if len(h) > 0 {
// Flush the found header
headers = append(headers, string(h))
h = h[:0]
upper = true
}
} else {
upper = b == '-' || b == '_'
}
}
return headers
}
// Options is a configuration container to setup the CORS middleware.
type Options struct {
// AllowedOrigins is a list of origins a cross-domain request can be executed from.
// If the special "*" value is present in the list, all origins will be allowed.
// An origin may contain a wildcard (*) to replace 0 or more characters
// (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality.
// Only one wildcard can be used per origin.
// Default value is ["*"]
AllowedOrigins []string
// AllowOriginFunc is a custom function to validate the origin. It take the origin
// as argument and returns true if allowed or false otherwise. If this option is
// set, the content of AllowedOrigins is ignored.
AllowOriginFunc func(origin string) bool
// AllowedMethods is a list of methods the client is allowed to use with
// cross-domain requests. Default value is simple methods (GET and POST)
AllowedMethods []string
// AllowedHeaders is list of non simple headers the client is allowed to use with
// cross-domain requests.
// If the special "*" value is present in the list, all headers will be allowed.
// Default value is [] but "Origin" is always appended to the list.
AllowedHeaders []string
// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
// API specification
ExposedHeaders []string
// AllowCredentials indicates whether the request can include user credentials like
// cookies, HTTP authentication or client side SSL certificates.
AllowCredentials bool
// MaxAge indicates how long (in seconds) the results of a preflight request
// can be cached
MaxAge int
// OptionsPassthrough instructs preflight to let other potential next handlers to
// process the OPTIONS method. Turn this on if your application handles OPTIONS.
OptionsPassthrough bool
// Debugging flag adds additional output to debug server side CORS issues
Debug bool
}
// Cors http handler
type Cors struct {
// Debug logger
Log *log.Logger
// Set to true when allowed origins contains a "*"
allowedOriginsAll bool
// Normalized list of plain allowed origins
allowedOrigins []string
// List of allowed origins containing wildcards
allowedWOrigins []wildcard
// Optional origin validator function
allowOriginFunc func(origin string) bool
// Set to true when allowed headers contains a "*"
allowedHeadersAll bool
// Normalized list of allowed headers
allowedHeaders []string
// Normalized list of allowed methods
allowedMethods []string
// Normalized list of exposed headers
exposedHeaders []string
allowCredentials bool
maxAge int
optionPassthrough bool
}
// New creates a new Cors handler with the provided options.
func New(options Options) *Cors {
c := &Cors{
exposedHeaders: convert(options.ExposedHeaders, http.CanonicalHeaderKey),
allowOriginFunc: options.AllowOriginFunc,
allowCredentials: options.AllowCredentials,
maxAge: options.MaxAge,
optionPassthrough: options.OptionsPassthrough,
}
if options.Debug {
c.Log = log.New(os.Stdout, "[cors] ", log.LstdFlags)
}
// Normalize options
// Note: for origins and methods matching, the spec requires a case-sensitive matching.
// As it may error prone, we chose to ignore the spec here.
// Allowed Origins
if len(options.AllowedOrigins) == 0 {
// Default is all origins
c.allowedOriginsAll = true
} else {
c.allowedOrigins = []string{}
c.allowedWOrigins = []wildcard{}
for _, origin := range options.AllowedOrigins {
// Normalize
origin = strings.ToLower(origin)
if origin == "*" {
// If "*" is present in the list, turn the whole list into a match all
c.allowedOriginsAll = true
c.allowedOrigins = nil
c.allowedWOrigins = nil
break
} else if i := strings.IndexByte(origin, '*'); i >= 0 {
// Split the origin in two: start and end string without the *
w := wildcard{origin[0:i], origin[i+1:]}
c.allowedWOrigins = append(c.allowedWOrigins, w)
} else {
c.allowedOrigins = append(c.allowedOrigins, origin)
}
}
}
// Allowed Headers
if len(options.AllowedHeaders) == 0 {
// Use sensible defaults
c.allowedHeaders = []string{"Origin", "Accept", "Content-Type"}
} else {
// Origin is always appended as some browsers will always request for this header at preflight
c.allowedHeaders = convert(append(options.AllowedHeaders, "Origin"), http.CanonicalHeaderKey)
for _, h := range options.AllowedHeaders {
if h == "*" {
c.allowedHeadersAll = true
c.allowedHeaders = nil
break
}
}
}
// Allowed Methods
if len(options.AllowedMethods) == 0 {
// Default is spec's "simple" methods
c.allowedMethods = []string{"GET", "POST"}
} else {
c.allowedMethods = convert(options.AllowedMethods, strings.ToUpper)
}
return c
}
// Default creates a new Cors handler with default options
func Default() *Cors {
return New(Options{})
}
// DefaultCors creates a new Cors handler with default options
func DefaultCors() *Cors {
return Default()
}
// Conflicts used by the router optimizer
func (c *Cors) Conflicts() string {
return "httpmethod"
}
// Serve serves the middleware
func (c *Cors) Serve(ctx *iris.Context) {
if ctx.MethodString() == iris.MethodOptions {
c.logf("Serve: Preflight request")
c.handlePreflight(ctx)
// Preflight requests are standalone and should stop the chain as some other
// middleware may not handle OPTIONS requests correctly. One typical example
// is authentication middleware ; OPTIONS requests won't carry authentication
// headers (see #1)
if c.optionPassthrough {
ctx.Next()
}
} else {
c.logf("Serve: Actual request")
c.handleActualRequest(ctx)
ctx.Next()
}
}
// handlePreflight handles pre-flight CORS requests
func (c *Cors) handlePreflight(ctx *iris.Context) {
origin := ctx.RequestHeader("Origin")
if ctx.MethodString() != iris.MethodOptions {
c.logf(" Preflight aborted: %s!=OPTIONS", ctx.MethodString())
return
}
// Always set Vary headers
ctx.Response.Header.Add("Vary", "Origin")
ctx.Response.Header.Add("Vary", "Access-Control-Request-Method")
ctx.Response.Header.Add("Vary", "Access-Control-Request-Headers")
if origin == "" {
c.logf(" Preflight aborted: empty origin")
return
}
if !c.isOriginAllowed(origin) {
c.logf(" Preflight aborted: origin '%s' not allowed", origin)
return
}
reqMethod := ctx.RequestHeader("Access-Control-Request-Method")
if !c.isMethodAllowed(reqMethod) {
c.logf(" Preflight aborted: method '%s' not allowed", reqMethod)
return
}
reqHeaders := parseHeaderList(ctx.RequestHeader("Access-Control-Request-Headers"))
if !c.areHeadersAllowed(reqHeaders) {
c.logf(" Preflight aborted: headers '%v' not allowed", reqHeaders)
return
}
ctx.Response.Header.Set("Access-Control-Allow-Origin", origin)
// Spec says: Since the list of methods can be unbounded, simply returning the method indicated
// by Access-Control-Request-Method (if supported) can be enough
ctx.Response.Header.Set("Access-Control-Allow-Methods", strings.ToUpper(reqMethod))
if len(reqHeaders) > 0 {
// Spec says: Since the list of headers can be unbounded, simply returning supported headers
// from Access-Control-Request-Headers can be enough
ctx.Response.Header.Set("Access-Control-Allow-Headers", strings.Join(reqHeaders, ", "))
}
if c.allowCredentials {
ctx.Response.Header.Set("Access-Control-Allow-Credentials", "true")
}
if c.maxAge > 0 {
ctx.Response.Header.Set("Access-Control-Max-Age", strconv.Itoa(c.maxAge))
}
c.logf(" Preflight response headers: %v", ctx.Response.Header)
}
// handleActualRequest handles simple cross-origin requests, actual request or redirects
func (c *Cors) handleActualRequest(ctx *iris.Context) {
origin := ctx.RequestHeader("Origin")
if ctx.MethodString() == "OPTIONS" {
c.logf(" Actual request no headers added: method == %s", ctx.MethodString())
return
}
ctx.Response.Header.Add("Vary", "Origin")
if origin == "" {
c.logf(" Actual request no headers added: missing origin")
return
}
if !c.isOriginAllowed(origin) {
c.logf(" Actual request no headers added: origin '%s' not allowed", origin)
return
}
// Note that spec does define a way to specifically disallow a simple method like GET or
// POST. Access-Control-Allow-Methods is only used for pre-flight requests and the
// spec doesn't instruct to check the allowed methods for simple cross-origin requests.
// We think it's a nice feature to be able to have control on those methods though.
if !c.isMethodAllowed(ctx.MethodString()) {
c.logf(" Actual request no headers added: method '%s' not allowed", ctx.MethodString())
return
}
ctx.Response.Header.Set("Access-Control-Allow-Origin", origin)
if len(c.exposedHeaders) > 0 {
ctx.Response.Header.Set("Access-Control-Expose-Headers", strings.Join(c.exposedHeaders, ", "))
}
if c.allowCredentials {
ctx.Response.Header.Set("Access-Control-Allow-Credentials", "true")
}
c.logf(" Actual response added headers: %v", ctx.Response.Header)
}
// convenience method. checks if debugging is turned on before printing
func (c *Cors) logf(format string, a ...interface{}) {
if c.Log != nil {
c.Log.Printf(format, a...)
}
}
// isOriginAllowed checks if a given origin is allowed to perform cross-domain requests
// on the endpoint
func (c *Cors) isOriginAllowed(origin string) bool {
if c.allowOriginFunc != nil {
return c.allowOriginFunc(origin)
}
if c.allowedOriginsAll {
return true
}
origin = strings.ToLower(origin)
for _, o := range c.allowedOrigins {
if o == origin {
return true
}
}
for _, w := range c.allowedWOrigins {
if w.match(origin) {
return true
}
}
return false
}
// isMethodAllowed checks if a given method can be used as part of a cross-domain request
// on the endpoing
func (c *Cors) isMethodAllowed(method string) bool {
if len(c.allowedMethods) == 0 {
// If no method allowed, always return false, even for preflight request
return false
}
method = strings.ToUpper(method)
if method == "OPTIONS" {
// Always allow preflight requests
return true
}
for _, m := range c.allowedMethods {
if m == method {
return true
}
}
return false
}
// areHeadersAllowed checks if a given list of headers are allowed to used within
// a cross-domain request.
func (c *Cors) areHeadersAllowed(requestedHeaders []string) bool {
if c.allowedHeadersAll || len(requestedHeaders) == 0 {
return true
}
for _, header := range requestedHeaders {
header = http.CanonicalHeaderKey(header)
found := false
for _, h := range c.allowedHeaders {
if h == header {
found = true
}
}
if !found {
return false
}
}
return true
}

View File

@@ -1,66 +0,0 @@
## Middleware information
This folder contains a middleware for internationalization uses a third-party package named i81n.
More can be found here:
[https://github.com/Unknwon/i18n](https://github.com/Unknwon/i18n)
## Description
Package i18n is for app Internationalization and Localization.
## How to use
Create folder named 'locales'
```
///Files:
./locales/locale_en-US.ini
./locales/locale_el-US.ini
```
Contents on locale_en-US:
```
hi = hello, %s
```
Contents on locale_el-GR:
```
hi = Ãåéá, %s
```
```go
package main
import (
"github.com/kataras/iris"
"github.com/kataras/iris/middleware/i18n"
)
func main() {
iris.UseFunc(i18n.I18n(i18n.Options{Default: "en-US",
Languages: map[string]string{
"en-US": "./locales/locale_en-US.ini",
"el-GR": "./locales/locale_el-GR.ini",
"zh-CN": "./locales/locale_zh-CN.ini"}}))
// or iris.Use(i18n.I18nHandler(....))
// or iris.Get("/",i18n.I18n(....), func (ctx *iris.Context){})
iris.Get("/", func(ctx *iris.Context) {
hi := ctx.GetFmt("translate")("hi", "maki") // hi is the key, 'maki' is the %s, the second parameter is optional
language := ctx.Get("language") // language is the language key, example 'en-US'
ctx.Write("From the language %s translated output: %s", language, hi)
})
println("Server is running at :8080")
iris.Listen(":8080")
}
```
### [For a working example, click here](https://github.com/kataras/iris/tree/examples/middleware_internationalization_i18n)

View File

@@ -1,99 +0,0 @@
package i18n
import (
"strings"
"github.com/Unknwon/i18n"
"github.com/kataras/iris"
)
// AcceptLanguage is the Header key "Accept-Language"
const AcceptLanguage = "Accept-Language"
// Options the i18n options
type Options struct {
// Default set it if you want a default language
//
// Checked: Configuration state, not at runtime
Default string
// URLParameter is the name of the url parameter which the language can be indentified
//
// Checked: Serving state, runtime
URLParameter string
// Languages is a map[string]string which the key is the language i81n and the value is the file location
//
// Example of key is: 'en-US'
// Example of value is: './locales/en-US.ini'
Languages map[string]string
}
type i18nMiddleware struct {
options Options
}
func (i *i18nMiddleware) Serve(ctx *iris.Context) {
wasByCookie := false
// try to get by url parameter
language := ctx.URLParam(i.options.URLParameter)
if language == "" {
// then try to take the lang field from the cookie
language = ctx.GetCookie("lang")
if len(language) > 0 {
wasByCookie = true
} else {
// try to get by the request headers(?)
if langHeader := ctx.RequestHeader(AcceptLanguage); i18n.IsExist(langHeader) {
language = langHeader
}
}
}
// if it was not taken by the cookie, then set the cookie in order to have it
if !wasByCookie {
ctx.SetCookieKV("language", language)
}
if language == "" {
language = i.options.Default
}
locale := i18n.Locale{language}
ctx.Set("language", language)
ctx.Set("translate", locale.Tr)
ctx.Next()
}
// I18nHandler returns the middleware which is just an iris.handler
func I18nHandler(_options ...Options) *i18nMiddleware {
i := &i18nMiddleware{}
if len(_options) == 0 || (len(_options) > 0 && len(_options[0].Languages) == 0) {
panic("You cannot use this middleware without set the Languages option, please try again and read the docs.")
}
i.options = _options[0]
firstlanguage := ""
//load the files
for k, v := range i.options.Languages {
if !strings.HasSuffix(v, ".ini") {
v += ".ini"
}
err := i18n.SetMessage(k, v)
if err != nil && err != i18n.ErrLangAlreadyExist {
panic("Iris i18n Middleware: Failed to set locale file" + k + " Error:" + err.Error())
}
if firstlanguage == "" {
firstlanguage = k
}
}
// if not default language setted then set to the first of the i.options.Languages
if i.options.Default == "" {
i.options.Default = firstlanguage
}
i18n.SetDefaultLang(i.options.Default)
return i
}
// I18n returns the middleware as iris.HandlerFunc with the passed options
func I18n(_options ...Options) iris.HandlerFunc {
return I18nHandler(_options...).Serve
}

View File

@@ -1,9 +0,0 @@
## Middleware information
This folder contains a middleware which is a bridge between Iris station's logger and http requests.
**Logs the incoming requests**
## How to use
Read the logger section [here](https://kataras.gitbooks.io/iris/content/logger.html)

View File

@@ -1,100 +0,0 @@
package logger
import (
"strconv"
"time"
"github.com/kataras/iris"
"github.com/kataras/iris/config"
"github.com/kataras/iris/logger"
)
// Options are the options of the logger middlweare
// contains 5 bools
// Status, IP, Method, Path, EnableColors
// if set to true then these will print
type Options struct {
// Status displays status code (bool)
Status bool
// IP displays request's remote address (bool)
IP bool
// Method displays the http method (bool)
Method bool
// Path displays the request path (bool)
Path bool
// EnableColors defaults to false
EnableColors bool
}
// DefaultOptions returns an options which all properties are true
func DefaultOptions() Options {
return Options{true, true, true, true, false}
}
type loggerMiddleware struct {
*logger.Logger
options Options
}
// Serve serves the middleware
func (l *loggerMiddleware) Serve(ctx *iris.Context) {
//all except latency to string
var date, status, ip, method, path string
var latency time.Duration
var startTime, endTime time.Time
path = ctx.PathString()
method = ctx.MethodString()
startTime = time.Now()
ctx.Next()
//no time.Since in order to format it well after
endTime = time.Now()
date = endTime.Format("01/02 - 15:04:05")
latency = endTime.Sub(startTime)
if l.options.Status {
status = strconv.Itoa(ctx.Response.StatusCode())
}
if l.options.IP {
ip = ctx.RemoteAddr()
}
if !l.options.Method {
method = ""
}
if !l.options.Path {
path = ""
}
//finally print the logs
l.printf("%s %v %4v %s %s %s \n", date, status, latency, ip, method, path)
}
func (l *loggerMiddleware) printf(format string, a ...interface{}) {
if l.options.EnableColors {
l.Logger.Otherf(format, a...)
} else {
l.Logger.Printf(format, a...)
}
}
// New returns the logger middleware as HandlerFunc with the default settings if second parameter is not passed
func New(theLogger *logger.Logger, options ...Options) iris.HandlerFunc {
if theLogger == nil {
theLogger = logger.New(config.DefaultLogger())
}
l := &loggerMiddleware{Logger: theLogger}
if len(options) > 0 {
l.options = options[0]
} else {
l.options = DefaultOptions()
}
return l.Serve
}

View File

@@ -1,30 +0,0 @@
## Middleware information
This folder contains a middleware for safety recover the server from panic
## How to use
```go
package main
import (
"github.com/kataras/iris"
"github.com/kataras/iris/middleware/recovery"
"os"
)
func main() {
iris.Use(recovery.New(os.Stderr)) // optional parameter is the writer which the stack of the panic will be printed
iris.Get("/", func(ctx *iris.Context) {
ctx.Write("Hi, let's panic")
panic("errorrrrrrrrrrrrrrr")
})
println("Server is running at :8080")
iris.Listen(":8080")
}
```

View File

@@ -1,45 +0,0 @@
package recovery
import (
"io"
"os"
"time"
"github.com/kataras/iris"
)
type recovery struct {
//out optional output to log any panics
out io.Writer
}
func (r recovery) Serve(ctx *iris.Context) {
defer func() {
if err := recover(); err != nil {
r.out.Write([]byte("[" + time.Now().String() + "]Recovery from panic \n"))
//ctx.Panic just sends http status 500 by default, but you can change it by: iris.OnPanic(func( c *iris.Context){})
ctx.Panic()
}
}()
ctx.Next()
}
// Recovery restores the server on internal server errors (panics)
// receives an optional writer, the default is the os.Stderr if no out writer given
// returns the middleware as iris.Handler
// same as New(...)
func Recovery(out ...io.Writer) iris.Handler {
r := recovery{os.Stderr}
if out != nil && len(out) == 1 {
r.out = out[0]
}
return r
}
// New restores the server on internal server errors (panics)
// receives an optional writer, the default is the os.Stderr if no out writer given
// returns the middleware as iris.Handler
// same as Recovery(...)
func New(out ...io.Writer) iris.Handler {
return Recovery(out...)
}

View File

@@ -1,69 +0,0 @@
## Middleware information
This was out-of-the-box iris supported before, but after Iris V1.1.0 it's not, so I had to modify it.
This folder contains a middleware ported to Iris from a third-party middleware named secure.
More can be found here:
[https://github.com/unrolled/secure](https://github.com/unrolled/secure)
## Description
Secure is an HTTP middleware for Go that facilitates some quick security wins.
## How to use
```go
package main
import (
"github.com/kataras/iris"
"github.com/kataras/iris/middleware/secure"
)
func main() {
s := secure.New(secure.Options{
AllowedHosts: []string{"ssl.example.com"}, // AllowedHosts is a list of fully qualified domain names that are allowed. Default is empty list, which allows any and all host names.
SSLRedirect: true, // If SSLRedirect is set to true, then only allow HTTPS requests. Default is false.
SSLTemporaryRedirect: false, // If SSLTemporaryRedirect is true, the a 302 will be used while redirecting. Default is false (301).
SSLHost: "ssl.example.com", // SSLHost is the host name that is used to redirect HTTP requests to HTTPS. Default is "", which indicates to use the same host.
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"}, // SSLProxyHeaders is set of header keys with associated values that would indicate a valid HTTPS request. Useful when using Nginx: `map[string]string{"X-Forwarded-Proto": "https"}`. Default is blank map.
STSSeconds: 315360000, // STSSeconds is the max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.
STSIncludeSubdomains: true, // If STSIncludeSubdomains is set to true, the `includeSubdomains` will be appended to the Strict-Transport-Security header. Default is false.
STSPreload: true, // If STSPreload is set to true, the `preload` flag will be appended to the Strict-Transport-Security header. Default is false.
ForceSTSHeader: false, // STS header is only included when the connection is HTTPS. If you want to force it to always be added, set to true. `IsDevelopment` still overrides this. Default is false.
FrameDeny: true, // If FrameDeny is set to true, adds the X-Frame-Options header with the value of `DENY`. Default is false.
CustomFrameOptionsValue: "SAMEORIGIN", // CustomFrameOptionsValue allows the X-Frame-Options header value to be set with a custom value. This overrides the FrameDeny option.
ContentTypeNosniff: true, // If ContentTypeNosniff is true, adds the X-Content-Type-Options header with the value `nosniff`. Default is false.
BrowserXSSFilter: true, // If BrowserXssFilter is true, adds the X-XSS-Protection header with the value `1; mode=block`. Default is false.
ContentSecurityPolicy: "default-src 'self'", // ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value. Default is "".
PublicKey: `pin-sha256="base64+primary=="; pin-sha256="base64+backup=="; max-age=5184000; includeSubdomains; report-uri="https://www.example.com/hpkp-report"`, // PublicKey implements HPKP to prevent MITM attacks with forged certificates. Default is "".
IsDevelopment: true, // This will cause the AllowedHosts, SSLRedirect, and STSSeconds/STSIncludeSubdomains options to be ignored during development. When deploying to production, be sure to set this to false.
})
iris.UseFunc(func(c *iris.Context) {
err := s.Process(c)
// If there was an error, do not continue.
if err != nil {
return
}
c.Next()
})
iris.Get("/home", func(c *iris.Context) {
c.Write("Hello from /home")
})
println("Server is running at :8080")
iris.Listen(":8080")
}
```

View File

@@ -1,205 +0,0 @@
/*
This has been modified to work with Iris, credits goes to https://github.com/unrolled/secure
*/
package secure
import (
"fmt"
"strings"
"github.com/kataras/iris"
)
const (
stsHeader = "Strict-Transport-Security"
stsSubdomainString = "; includeSubdomains"
stsPreloadString = "; preload"
frameOptionsHeader = "X-Frame-Options"
frameOptionsValue = "DENY"
contentTypeHeader = "X-Content-Type-Options"
contentTypeValue = "nosniff"
xssProtectionHeader = "X-XSS-Protection"
xssProtectionValue = "1; mode=block"
cspHeader = "Content-Security-Policy"
hpkpHeader = "Public-Key-Pins"
)
func defaultBadHostHandler(ctx *iris.Context) {
ctx.Text(iris.StatusInternalServerError, "Bad Host")
}
// Options is a struct for specifying configuration options for the secure.Secure middleware.
type Options struct {
// AllowedHosts is a list of fully qualified domain names that are allowed. Default is empty list, which allows any and all host names.
AllowedHosts []string
// If SSLRedirect is set to true, then only allow https requests. Default is false.
SSLRedirect bool
// If SSLTemporaryRedirect is true, the a 302 will be used while redirecting. Default is false (301).
SSLTemporaryRedirect bool
// SSLHost is the host name that is used to redirect http requests to https. Default is "", which indicates to use the same host.
SSLHost string
// SSLProxyHeaders is set of header keys with associated values that would indicate a valid https request. Useful when using Nginx: `map[string]string{"X-Forwarded-Proto": "https"}`. Default is blank map.
SSLProxyHeaders map[string]string
// STSSeconds is the max-age of the Strict-Transport-Security header. Default is 0, which would NOT include the header.
STSSeconds int64
// If STSIncludeSubdomains is set to true, the `includeSubdomains` will be appended to the Strict-Transport-Security header. Default is false.
STSIncludeSubdomains bool
// If STSPreload is set to true, the `preload` flag will be appended to the Strict-Transport-Security header. Default is false.
STSPreload bool
// If ForceSTSHeader is set to true, the STS header will be added even when the connection is HTTP. Default is false.
ForceSTSHeader bool
// If FrameDeny is set to true, adds the X-Frame-Options header with the value of `DENY`. Default is false.
FrameDeny bool
// CustomFrameOptionsValue allows the X-Frame-Options header value to be set with a custom value. This overrides the FrameDeny option.
CustomFrameOptionsValue string
// If ContentTypeNosniff is true, adds the X-Content-Type-Options header with the value `nosniff`. Default is false.
ContentTypeNosniff bool
// BrowserXSSFilter If it's true, adds the X-XSS-Protection header with the value `1; mode=block`. Default is false.
BrowserXSSFilter bool
// ContentSecurityPolicy allows the Content-Security-Policy header value to be set with a custom value. Default is "".
ContentSecurityPolicy string
// PublicKey implements HPKP to prevent MITM attacks with forged certificates. Default is "".
PublicKey string
// When developing, the AllowedHosts, SSL, and STS options can cause some unwanted effects. Usually testing happens on http, not https, and on localhost, not your production domain... so set this to true for dev environment.
// If you would like your development environment to mimic production with complete Host blocking, SSL redirects, and STS headers, leave this as false. Default if false.
IsDevelopment bool
}
// Secure is a middleware that helps setup a few basic security features. A single secure.Options struct can be
// provided to configure which features should be enabled, and the ability to override a few of the default values.
type Secure struct {
// Customize Secure with an Options struct.
opt Options
// Handlers for when an error occurs (ie bad host).
badHostHandler iris.Handler
}
// New constructs a new Secure instance with supplied options.
func New(options ...Options) *Secure {
var o Options
if len(options) == 0 {
o = Options{}
} else {
o = options[0]
}
return &Secure{
opt: o,
badHostHandler: iris.HandlerFunc(defaultBadHostHandler),
}
}
// SetBadHostHandler sets the handler to call when secure rejects the host name.
func (s *Secure) SetBadHostHandler(handler iris.Handler) {
s.badHostHandler = handler
}
// Handler implements the iris.HandlerFunc for integration with iris.
func (s *Secure) Handler(h iris.Handler) iris.Handler {
return iris.HandlerFunc(func(ctx *iris.Context) {
// Let secure process the request. If it returns an error,
// that indicates the request should not continue.
err := s.Process(ctx)
// If there was an error, do not continue.
if err != nil {
return
}
h.Serve(ctx)
})
}
// Process runs the actual checks and returns an error if the middleware chain should stop.
func (s *Secure) Process(ctx *iris.Context) error {
// Allowed hosts check.
if len(s.opt.AllowedHosts) > 0 && !s.opt.IsDevelopment {
isGoodHost := false
for _, allowedHost := range s.opt.AllowedHosts {
if strings.EqualFold(allowedHost, string(ctx.Host())) {
isGoodHost = true
break
}
}
if !isGoodHost {
s.badHostHandler.Serve(ctx)
return fmt.Errorf("Bad host name: %s", string(ctx.Host()))
}
}
// Determine if we are on HTTPS.
isSSL := strings.EqualFold(string(ctx.Request.URI().Scheme()), "https") || ctx.IsTLS()
if !isSSL {
for k, v := range s.opt.SSLProxyHeaders {
if ctx.RequestHeader(k) == v {
isSSL = true
break
}
}
}
// SSL check.
if s.opt.SSLRedirect && !isSSL && !s.opt.IsDevelopment {
url := ctx.Request.URI()
url.SetScheme("https")
url.SetHostBytes(ctx.Host())
if len(s.opt.SSLHost) > 0 {
url.SetHost(s.opt.SSLHost)
}
status := iris.StatusMovedPermanently
if s.opt.SSLTemporaryRedirect {
status = iris.StatusTemporaryRedirect
}
ctx.Redirect(url.String(), status)
return fmt.Errorf("Redirecting to HTTPS")
}
// Strict Transport Security header. Only add header when we know it's an SSL connection.
// See https://tools.ietf.org/html/rfc6797#section-7.2 for details.
if s.opt.STSSeconds != 0 && (isSSL || s.opt.ForceSTSHeader) && !s.opt.IsDevelopment {
stsSub := ""
if s.opt.STSIncludeSubdomains {
stsSub = stsSubdomainString
}
if s.opt.STSPreload {
stsSub += stsPreloadString
}
ctx.Response.Header.Add(stsHeader, fmt.Sprintf("max-age=%d%s", s.opt.STSSeconds, stsSub))
}
// Frame Options header.
if len(s.opt.CustomFrameOptionsValue) > 0 {
ctx.Response.Header.Add(frameOptionsHeader, s.opt.CustomFrameOptionsValue)
} else if s.opt.FrameDeny {
ctx.Response.Header.Add(frameOptionsHeader, frameOptionsValue)
}
// Content Type Options header.
if s.opt.ContentTypeNosniff {
ctx.Response.Header.Add(contentTypeHeader, contentTypeValue)
}
// XSS Protection header.
if s.opt.BrowserXSSFilter {
ctx.Response.Header.Add(xssProtectionHeader, xssProtectionValue)
}
// HPKP header.
if len(s.opt.PublicKey) > 0 && isSSL && !s.opt.IsDevelopment {
ctx.Response.Header.Add(hpkpHeader, s.opt.PublicKey)
}
// Content Security Policy header.
if len(s.opt.ContentSecurityPolicy) > 0 {
ctx.Response.Header.Add(cspHeader, s.opt.ContentSecurityPolicy)
}
return nil
}