2017-02-14 04:54:11 +01:00
// Package iris provides efficient and well-designed toolbox with robust set of features to
// create your own perfect high performance web application
// with unlimited portability using the Go Programming Language.
//
// For middleware, template engines, response engines, sessions, websockets, mails, subdomains,
// dynamic subdomains, routes, party of subdomains & routes and more
//
// visit https://docs.iris-go.com
package iris
2016-05-30 16:08:09 +02:00
import (
2016-06-14 07:45:40 +02:00
"fmt"
2017-02-14 04:54:11 +01:00
"io"
2016-09-07 06:36:23 +02:00
"log"
2016-09-27 15:28:38 +02:00
"net"
2017-01-02 20:20:17 +01:00
"net/http"
2016-09-07 06:36:23 +02:00
"net/url"
"os"
2016-09-27 15:28:38 +02:00
"os/signal"
2016-09-07 06:36:23 +02:00
"strconv"
"strings"
"sync"
"time"
2016-10-02 02:30:37 +02:00
2017-02-14 04:54:11 +01:00
"github.com/geekypanda/httpcache"
2016-10-02 02:30:37 +02:00
"github.com/kataras/go-errors"
"github.com/kataras/go-fs"
"github.com/kataras/go-serializer"
"github.com/kataras/go-sessions"
2016-05-30 16:08:09 +02:00
)
const (
2016-10-21 02:06:50 +02:00
// Version is the current version number of the Iris web framework
2017-02-14 04:54:11 +01:00
Version = "6.2.0"
2016-07-06 20:24:34 +02:00
banner = ` _____ _
2016-06-06 20:04:38 +02:00
| _ _ | ( _ )
| | ____ _ ___
| | | __ || | / __ |
_ | | _ | | | | \ __ \
2016-07-06 20:24:34 +02:00
| _____ | _ | | _ || ___ / ` + Version + ` `
)
2017-02-14 04:54:11 +01:00
// Default is the field which keeps an empty `Framework`
// instance with its default configuration (config can change at runtime).
//
// Use that as `iris.Default.Handle(...)`
// or create a new, ex: `app := iris.New(); app.Handle(...)`
2016-07-06 20:24:34 +02:00
var (
2017-01-25 21:19:06 +01:00
Default * Framework
2017-02-14 04:54:11 +01:00
// ResetDefault resets the `.Default`
// to an empty *Framework with the default configuration.
2017-01-14 04:17:46 +01:00
//
2017-02-14 04:54:11 +01:00
// Note: ResetDefault field itself can be setted
// to custom function too.
ResetDefault = func ( ) { Default = New ( ) }
2016-05-30 16:08:09 +02:00
)
2016-07-06 20:24:34 +02:00
func init ( ) {
2016-10-13 16:25:01 +02:00
ResetDefault ( )
2016-07-06 20:24:34 +02:00
}
2017-02-14 04:54:11 +01:00
// Framework is our God |\| Google.Search('Greek mythology Iris').
2017-01-02 20:20:17 +01:00
type Framework struct {
2017-02-14 04:54:11 +01:00
* Router
policies Policies
2017-01-02 20:20:17 +01:00
// HTTP Server runtime fields is the iris' defined main server, developer can use unlimited number of servers
// note: they're available after .Build, and .Serve/Listen/ListenTLS/ListenLETSENCRYPT/ListenUNIX
2017-02-14 04:54:11 +01:00
ln net . Listener
closedManually bool
once sync . Once
Config * Configuration
sessions sessions . Sessions
Websocket * WebsocketServer
2017-01-02 20:20:17 +01:00
}
2017-02-14 04:54:11 +01:00
var defaultGlobalLoggerOuput = log . New ( os . Stdout , "[iris] " , log . LstdFlags )
2016-05-30 16:08:09 +02:00
2017-02-14 04:54:11 +01:00
// DevLogger returns a new Logger which prints both ProdMode and DevMode messages
// to the default global logger printer.
2016-09-09 07:09:03 +02:00
//
2017-02-14 04:54:11 +01:00
// Usage: app := iris.New()
// app.Adapt(iris.DevLogger())
//
// Users can always ignore that and adapt a custom LoggerPolicy,
// which will use your custom printer instead.
func DevLogger ( ) LoggerPolicy {
return func ( mode LogMode , logMessage string ) {
defaultGlobalLoggerOuput . Println ( logMessage )
}
}
2016-09-09 07:09:03 +02:00
2017-02-14 04:54:11 +01:00
// New creates and returns a fresh Iris *Framework instance
// with the default configuration if no 'setters' parameters passed.
func New ( setters ... OptionSetter ) * Framework {
2016-09-09 07:09:03 +02:00
s := & Framework { }
2017-02-14 04:54:11 +01:00
// +------------------------------------------------------------+
// | Set the config passed from setters |
// | or use the default one |
// +------------------------------------------------------------+
2016-09-09 07:09:03 +02:00
s . Set ( setters ... )
2016-09-07 06:36:23 +02:00
2016-09-09 07:09:03 +02:00
{
2017-02-14 04:54:11 +01:00
// +------------------------------------------------------------+
// | Module Name: Logger |
// | On Init: If user didn't adapt a custom loggger then attach |
// | a new logger using log.Logger as printer with |
// | some default options |
// +------------------------------------------------------------+
// The logger policy is never nil and it doesn't defaults to an empty func,
// instead it defaults to a logger with os.Stdout as the print target which prints
// ONLY prodction level messages.
// While in ProdMode Iris logs only panics and fatal errors.
// You can override the default log policy with app.Adapt(iris.DevLogger())
// or app.Adapt(iris.LoggerPolicy(customLogger))
// to log both ProdMode and DevMode messages.
//
// Note:
// The decision to not log everything and use middleware for http requests instead of built'n
// is because I'm using Iris on production so I don't want many logs to my screens
// while server is running.
s . Adapt ( LoggerPolicy ( func ( mode LogMode , logMessage string ) {
if mode == ProdMode {
defaultGlobalLoggerOuput . Println ( logMessage )
}
} ) )
}
// +------------------------------------------------------------+
// | |
// | Please take a look at the policy.go file first. |
// | The EventPolicy contains all the necessary information |
// | user should know about the framework's flow. |
// | |
// +------------------------------------------------------------+
// +------------------------------------------------------------+
// | On Boot: Set the VHost and VScheme config fields |
// | based on the net.Listener which (or not) |
// | setted on Serve and Listen funcs. |
// | |
// | It's the only pre-defined Boot event because of |
// | any user's custom 'Build' events should know |
// | the Host of the server. |
// +------------------------------------------------------------+
s . Adapt ( EventPolicy { Boot : func ( s * Framework ) {
// set the host and scheme
if s . Config . VHost == "" { // if not setted by Listen functions
if s . ln != nil { // but user called .Serve
// then take the listener's addr
s . Config . VHost = s . ln . Addr ( ) . String ( )
} else {
// if no .Serve or .Listen called, then the user should set the VHost manually,
// however set it to a default value here for any case
s . Config . VHost = DefaultServerAddr
}
}
// if user didn't specified a scheme then get it from the VHost, which is already setted at before statements
if s . Config . VScheme == "" {
s . Config . VScheme = ParseScheme ( s . Config . VHost )
}
} } )
2016-09-09 07:09:03 +02:00
{
2017-02-14 04:54:11 +01:00
// +------------------------------------------------------------+
// | Module Name: Renderer |
// | On Init: set templates and serializers |
// | and adapt the RenderPolicy for both |
// | templates and content-type specific renderer (serializer) |
// | On Build: build the serializers and templates |
// | based on the user's calls |
// +------------------------------------------------------------+
{
// +------------------------------------------------------------+
// | Module Name: Rich Content-Type Renderer |
// | On Init: Attach a new empty content-type serializers |
// | On Build: register the default serializers + the user's |
// +------------------------------------------------------------+
// prepare the serializers,
// serializer content-types(json,jsonp,xml,markdown) the defaults are setted:
serializers := serializer . Serializers { }
serializer . RegisterDefaults ( serializers )
//
// notes for me: Why not at the build state? in order to be overridable and not only them,
// these are easy to be overriden by external adaptors too, no matter the order,
// this is why the RenderPolicy last registration executing first and the first last.
//
// Adapt the RenderPolicy on the Build in order to be the last
// render policy, so the users can adapt their own before the default(= to override json,xml,jsonp renderer).
//
// Notes: the Renderer of the view system is managed by the
// adaptors because they are optional.
// If templates are binded to the RenderPolicy then
// If a key contains a dot('.') then is a template file
// otherwise try to find a serializer, if contains error then we return false and the error
// in order the renderer to continue to search for any other custom registerer RenderPolicy
// if no error then check if it has written anything, if yes write the content
// to the writer(which is the context.ResponseWriter or the gzip version of it)
// if no error but nothing written then we return false and the error
s . Adapt ( RenderPolicy ( func ( out io . Writer , name string , bind interface { } , options ... map [ string ] interface { } ) ( error , bool ) {
b , err := serializers . Serialize ( name , bind , options ... )
if err != nil {
return err , false // errors should be wrapped
}
if len ( b ) > 0 {
_ , err = out . Write ( b )
return err , true
}
// continue to the next if any or notice there is no available renderer for that name
return nil , false
} ) )
}
{
// +------------------------------------------------------------+
// | Module Name: Template engine's funcs |
// | On Init: Use the template mux builder to |
// | adapt the reverse routing tmpl funcs |
// | for any template engine that will be registered |
// +------------------------------------------------------------+
s . Adapt ( TemplateFuncsPolicy {
"url" : s . URL ,
"urlpath" : s . policies . RouterReversionPolicy . URLPath ,
} )
// the entire template registration logic lives inside the ./adaptors/template now.
}
2016-09-09 07:09:03 +02:00
}
{
2017-02-14 04:54:11 +01:00
// +------------------------------------------------------------+
// | Module Name: Sessions |
// | On Init: Attach a session manager with empty config |
// | On Build: Set the configuration if allowed |
// +------------------------------------------------------------+
2016-09-13 06:54:44 +02:00
2017-02-07 01:37:35 +01:00
// set the sessions in order to UseSessionDB to work
s . sessions = sessions . New ( )
2017-02-14 04:54:11 +01:00
// On Build:
s . Adapt ( EventPolicy { Build : func ( * Framework ) {
// re-set the configuration field to update users configuration
s . sessions . Set ( s . Config . Sessions )
} } )
2016-09-09 07:09:03 +02:00
}
{
2017-02-14 04:54:11 +01:00
// +------------------------------------------------------------+
// | Module Name: Websocket |
// | On Init: Attach a new websocket server. |
// | It starts on first callback registration |
// +------------------------------------------------------------+
// in order to be able to call $instance.Websocket.OnConnection.
// The whole server's configuration will be
// initialized on the first OnConnection registration (no runtime)
s . Websocket = NewWebsocketServer ( s )
}
{
// +------------------------------------------------------------+
// | Module Name: Router |
// | On Init: Attach a new router, pass a new repository, |
// | an empty error handlers list, the context pool binded |
// | to the Framework and the root path "/" |
// | On Build: Use the policies to build the router's handler |
// | based on its route repository |
// +------------------------------------------------------------+
s . Router = & Router {
repository : new ( routeRepository ) ,
Errors : & ErrorHandlers {
handlers : make ( map [ int ] Handler , 0 ) ,
} ,
Context : & contextPool {
sync . Pool { New : func ( ) interface { } { return & Context { framework : s } } } ,
} ,
relativePath : "/" ,
}
s . Adapt ( EventPolicy { Build : func ( * Framework ) {
// first check if it's not setted already by any Boot event.
if s . Router . handler == nil {
// and most importantly, check if the user has provided a router
// adaptor, if not then it should panic here, iris can't run without a router attached to it
// and default router not any more, user should select one from ./adaptors or
// any other third-party adaptor may done by community.
// I was coding the new iris version for more than 20 days(~200+ hours of code)
// and I hope that once per application the addition of +1 line users have to put,
// is not a big deal.
if s . policies . RouterBuilderPolicy == nil {
// this is important panic and app can't continue as we said.
s . handlePanic ( errRouterIsMissing . Format ( s . Config . VHost ) )
// don't trace anything else,
// the detailed errRouterIsMissing message will tell the user what to do to fix that.
os . Exit ( 0 )
}
// buid the router using user's selection build policy
s . Router . build ( s . policies . RouterBuilderPolicy )
}
} } )
2016-10-02 02:30:37 +02:00
2016-07-06 20:24:34 +02:00
}
2016-05-30 16:08:09 +02:00
2017-02-14 04:54:11 +01:00
{
// +------------------------------------------------------------+
// | Module Name: System |
// | On Build: Check for updates on Build |
// +------------------------------------------------------------+
// On Build: local repository updates
s . Adapt ( EventPolicy { Build : func ( * Framework ) {
if s . Config . CheckForUpdatesSync {
s . CheckForUpdates ( false )
} else if s . Config . CheckForUpdates {
go s . CheckForUpdates ( false )
}
} } )
}
2016-06-14 19:29:01 +02:00
2017-02-14 04:54:11 +01:00
return s
2016-09-27 15:28:38 +02:00
}
2016-08-17 11:57:54 +02:00
2017-02-14 04:54:11 +01:00
// Set sets an option, configuration field to its Config
2016-09-27 15:28:38 +02:00
func ( s * Framework ) Set ( setters ... OptionSetter ) {
if s . Config == nil {
defaultConfiguration := DefaultConfiguration ( )
s . Config = & defaultConfiguration
2016-08-17 11:57:54 +02:00
}
2016-09-16 09:25:56 +02:00
2016-09-27 15:28:38 +02:00
for _ , setter := range setters {
setter . Set ( s . Config )
2016-09-16 20:16:48 +02:00
}
2016-07-06 20:24:34 +02:00
}
2017-02-14 04:54:11 +01:00
// Log logs to the defined logger policy.
//
// The default outputs to the os.Stdout when EnvMode is 'ProductionEnv'
func ( s * Framework ) Log ( mode LogMode , log string ) {
s . policies . LoggerPolicy ( mode , log )
2016-09-18 06:21:35 +02:00
}
2017-02-14 04:54:11 +01:00
// Must checks if the error is not nil, if it isn't
// panics on registered iris' logger or
// to a recovery event handler, otherwise does nothing.
2016-09-27 15:28:38 +02:00
func ( s * Framework ) Must ( err error ) {
if err != nil {
2017-02-14 04:54:11 +01:00
s . handlePanic ( err )
2016-09-27 15:28:38 +02:00
}
2016-07-06 20:24:34 +02:00
}
2017-02-14 04:54:11 +01:00
func ( s * Framework ) handlePanic ( err error ) {
if recoveryHandler := s . policies . EventPolicy . Recover ; recoveryHandler != nil {
recoveryHandler ( s , err )
return
}
// if not a registered recovery event handler found
// then call the logger's Panic.
s . Log ( ProdMode , err . Error ( ) )
2016-09-18 05:55:44 +02:00
}
2017-02-14 04:54:11 +01:00
// Boot runs only once, automatically
// when 'Serve/Listen/ListenTLS/ListenUNIX/ListenLETSENCRYPT' called.
// It's exported because you may want to build the router
// and its components but not run the server.
//
// See ./httptest/httptest.go to understand its usage.
func ( s * Framework ) Boot ( ) ( firstTime bool ) {
2016-09-27 15:28:38 +02:00
s . once . Do ( func ( ) {
2017-02-14 04:54:11 +01:00
// here execute the boot events, before build events, if exists, here is
// where the user can make an event module to adapt custom routers and other things
// fire the before build event
s . policies . EventPolicy . Fire ( s . policies . EventPolicy . Boot , s )
2016-07-06 20:24:34 +02:00
2017-02-14 04:54:11 +01:00
// here execute the build events if exists
// right before the Listen, all methods have been setted
// usually is used to adapt third-party servers or proxies or load balancer(s)
s . policies . EventPolicy . Fire ( s . policies . EventPolicy . Build , s )
2016-10-02 06:28:36 +02:00
2017-02-14 04:54:11 +01:00
firstTime = true
2016-09-27 15:28:38 +02:00
} )
2017-02-14 04:54:11 +01:00
return
2016-09-16 20:16:48 +02:00
}
2016-09-27 15:28:38 +02:00
// Serve serves incoming connections from the given listener.
//
// Serve blocks until the given listener returns permanent error.
func ( s * Framework ) Serve ( ln net . Listener ) error {
2017-02-14 04:54:11 +01:00
if s . isRunning ( ) {
return errors . New ( "Server is already started and listening" )
2016-09-27 15:28:38 +02:00
}
// maybe a 'race' here but user should not call .Serve more than one time especially in more than one go routines...
s . ln = ln
2017-02-14 04:54:11 +01:00
s . Boot ( )
2016-09-16 20:16:48 +02:00
2017-02-14 04:54:11 +01:00
// post any panics to the user defined logger.
2016-09-27 15:28:38 +02:00
defer func ( ) {
2017-02-14 04:54:11 +01:00
if rerr := recover ( ) ; rerr != nil {
if x , ok := rerr . ( * net . OpError ) ; ok && x . Op == "accept" && s . closedManually {
///TODO:
// here we don't report it back because the user called .Close manually.
// NOTES:
//
// I know that the best option to actual Close a server is
// by using a custom net.Listener and do it via channels on its Accept.
// BUT I am not doing this right now because as I'm learning the new go v1.8 will have a shutdown
// options by-default and we will use that instead.
// println("iris.go:355:recover but closed manually so we don't run the handler")
return
}
if err , ok := rerr . ( error ) ; ok {
s . handlePanic ( err )
}
2016-09-16 20:16:48 +02:00
}
2016-09-27 15:28:38 +02:00
} ( )
2017-01-11 15:23:38 +01:00
2017-02-14 04:54:11 +01:00
srv := & http . Server {
ReadTimeout : s . Config . ReadTimeout ,
WriteTimeout : s . Config . WriteTimeout ,
MaxHeaderBytes : s . Config . MaxHeaderBytes ,
TLSNextProto : s . Config . TLSNextProto ,
ConnState : s . Config . ConnState ,
Addr : s . Config . VHost ,
ErrorLog : s . policies . LoggerPolicy . ToLogger ( log . LstdFlags ) ,
Handler : s . Router ,
}
2017-01-11 15:23:38 +01:00
2017-02-14 04:54:11 +01:00
if s . Config . TLSNextProto != nil {
srv . TLSNextProto = s . Config . TLSNextProto
}
if s . Config . ConnState != nil {
srv . ConnState = s . Config . ConnState
2017-01-11 15:23:38 +01:00
}
2017-02-14 04:54:11 +01:00
// print the banner and wait for system channel interrupt
go s . postServe ( )
2017-01-11 15:23:38 +01:00
// finally return the error or block here, remember,
// until go1.8 these are our best options.
2017-02-14 04:54:11 +01:00
return srv . Serve ( s . ln )
2017-01-11 15:23:38 +01:00
}
func ( s * Framework ) postServe ( ) {
2016-09-27 15:28:38 +02:00
if ! s . Config . DisableBanner {
bannerMessage := fmt . Sprintf ( "%s: Running at %s" , time . Now ( ) . Format ( s . Config . TimeFormat ) , s . Config . VHost )
// we don't print it via Logger because:
// 1. The banner is only 'useful' when the developer logs to terminal and no file
// 2. Prefix & LstdFlags options of the default s.Logger
2016-09-16 20:16:48 +02:00
2016-09-27 15:28:38 +02:00
fmt . Printf ( "%s\n\n%s\n" , banner , bannerMessage )
2016-09-16 20:16:48 +02:00
}
2016-10-04 00:18:17 +02:00
ch := make ( chan os . Signal , 1 )
signal . Notify ( ch , os . Interrupt )
2016-09-27 15:28:38 +02:00
<- ch
2017-01-11 17:01:29 +01:00
2017-02-14 04:54:11 +01:00
// fire any custom interrupted events and at the end close and exit
// if the custom event blocks then it decides what to do next.
s . policies . Fire ( s . policies . Interrupted , s )
// .Close doesn't really closes but it releases the ip:port, wait for go1.8 and see comments on IsRunning
s . Close ( )
os . Exit ( 1 )
2016-06-14 07:45:40 +02:00
}
2016-05-30 16:08:09 +02:00
2016-06-14 07:45:40 +02:00
// Listen starts the standalone http server
// which listens to the addr parameter which as the form of
// host:port
//
2017-02-14 04:54:11 +01:00
// If you need to manually monitor any error please use `.Serve` instead.
2016-06-14 07:45:40 +02:00
func ( s * Framework ) Listen ( addr string ) {
2016-09-27 15:28:38 +02:00
addr = ParseHost ( addr )
if s . Config . VHost == "" {
s . Config . VHost = addr
// this will be set as the front-end listening addr
}
2017-01-11 23:57:07 +01:00
// only here, other Listen functions should throw an error if port is missing.
// User should know how to fix them on ListenUNIX/ListenTLS/ListenLETSENCRYPT/Serve,
// they are used by more 'advanced' devs, mostly.
if portIdx := strings . IndexByte ( addr , ':' ) ; portIdx < 0 {
// missing port part, add it
addr = addr + ":80"
}
2016-09-27 15:28:38 +02:00
2017-01-04 14:16:53 +01:00
ln , err := TCPKeepAlive ( addr )
2016-09-27 15:28:38 +02:00
if err != nil {
2017-02-14 04:54:11 +01:00
s . handlePanic ( err )
2016-09-27 15:28:38 +02:00
}
s . Must ( s . Serve ( ln ) )
2016-06-14 07:45:40 +02:00
}
2016-05-30 16:08:09 +02:00
2016-06-14 07:45:40 +02:00
// ListenTLS Starts a https server with certificates,
// if you use this method the requests of the form of 'http://' will fail
// only https:// connections are allowed
// which listens to the addr parameter which as the form of
// host:port
//
//
2017-02-14 04:54:11 +01:00
// If you need to manually monitor any error please use `.Serve` instead.
2016-06-14 07:45:40 +02:00
func ( s * Framework ) ListenTLS ( addr string , certFile , keyFile string ) {
2016-09-27 15:28:38 +02:00
addr = ParseHost ( addr )
if s . Config . VHost == "" {
s . Config . VHost = addr
// this will be set as the front-end listening addr
}
ln , err := TLS ( addr , certFile , keyFile )
if err != nil {
2017-02-14 04:54:11 +01:00
s . handlePanic ( err )
2016-07-07 02:36:48 +02:00
}
2016-09-27 15:28:38 +02:00
s . Must ( s . Serve ( ln ) )
2016-06-14 07:45:40 +02:00
}
2016-06-06 12:25:09 +02:00
2016-09-27 15:28:38 +02:00
// ListenLETSENCRYPT starts a server listening at the specific nat address
// using key & certification taken from the letsencrypt.org 's servers
// it's also starts a second 'http' server to redirect all 'http://$ADDR_HOSTNAME:80' to the' https://$ADDR'
2016-10-28 20:21:57 +02:00
// it creates a cache file to store the certifications, for performance reasons, this file by-default is "./letsencrypt.cache"
// if you skip the second parameter then the cache file is "./letsencrypt.cache"
2016-10-28 20:55:00 +02:00
// if you want to disable cache then simple pass as second argument an empty empty string ""
2016-10-28 20:21:57 +02:00
//
2016-11-13 10:12:59 +01:00
// example: https://github.com/iris-contrib/examples/blob/master/letsencrypt/main.go
2016-10-28 20:21:57 +02:00
//
// supports localhost domains for testing,
// NOTE: if you are ready for production then use `$app.Serve(iris.LETSENCRYPTPROD("mydomain.com"))` instead
func ( s * Framework ) ListenLETSENCRYPT ( addr string , cacheFileOptional ... string ) {
2016-09-27 15:28:38 +02:00
addr = ParseHost ( addr )
if s . Config . VHost == "" {
s . Config . VHost = addr
// this will be set as the front-end listening addr
}
2016-10-28 20:21:57 +02:00
ln , err := LETSENCRYPT ( addr , cacheFileOptional ... )
2016-09-27 15:28:38 +02:00
if err != nil {
2017-02-14 04:54:11 +01:00
s . handlePanic ( err )
2016-07-29 00:33:20 +02:00
}
2017-01-04 14:16:53 +01:00
// starts a second server which listening on HOST:80 to redirect all requests to the HTTPS://HOST:PORT
Proxy ( ParseHostname ( addr ) + ":80" , "https://" + addr )
2016-09-27 15:28:38 +02:00
s . Must ( s . Serve ( ln ) )
2016-07-29 00:33:20 +02:00
}
2016-06-14 07:45:40 +02:00
// ListenUNIX starts the process of listening to the new requests using a 'socket file', this works only on unix
2016-07-07 02:36:48 +02:00
//
2016-06-30 04:58:04 +02:00
//
2017-02-14 04:54:11 +01:00
// If you need to manually monitor any error please use `.Serve` instead.
2016-07-07 02:36:48 +02:00
func ( s * Framework ) ListenUNIX ( addr string , mode os . FileMode ) {
2016-09-27 15:28:38 +02:00
// *on unix listen we don't parse the host, because sometimes it causes problems to the user
if s . Config . VHost == "" {
s . Config . VHost = addr
// this will be set as the front-end listening addr
}
ln , err := UNIX ( addr , mode )
if err != nil {
2017-02-14 04:54:11 +01:00
s . handlePanic ( err )
2016-09-27 15:28:38 +02:00
}
2016-07-05 14:29:32 +02:00
2016-09-27 15:28:38 +02:00
s . Must ( s . Serve ( ln ) )
2016-07-05 14:29:32 +02:00
}
2016-09-27 15:28:38 +02:00
// IsRunning returns true if server is running
2017-02-14 04:54:11 +01:00
func ( s * Framework ) isRunning ( ) bool {
///TODO: this will change on gov1.8,
// Reseve or Restart and Close will be re-added again when 1.8 final release.
2016-09-27 15:28:38 +02:00
return s != nil && s . ln != nil && s . ln . Addr ( ) != nil && s . ln . Addr ( ) . String ( ) != ""
}
2016-07-06 20:24:34 +02:00
2017-02-14 04:54:11 +01:00
// Close is not working propetly but it releases the host:port.
2016-07-07 02:36:48 +02:00
func ( s * Framework ) Close ( ) error {
2017-02-14 04:54:11 +01:00
if s . isRunning ( ) {
///TODO:
// This code below doesn't works without custom net listener which will work in a stop channel which will cost us performance.
// This will work on go v1.8 BUT FOR NOW make unexported reserve/reboot/restart in order to be non confusual for the user.
// Close need to be exported because whitebox tests are using this method to release the port.
2016-09-27 15:28:38 +02:00
return s . ln . Close ( )
}
return nil
}
2017-02-14 04:54:11 +01:00
// restart re-starts the server using the last .Serve's listener
// func (s *Framework) restart() error {
// ///TODO: See .close() notes
// return s.Serve(s.ln)
// }
2016-09-27 15:28:38 +02:00
2017-02-14 04:54:11 +01:00
func ( s * Framework ) ServeHTTP ( w http . ResponseWriter , r * http . Request ) {
s . Router . ServeHTTP ( w , r )
2016-09-27 15:28:38 +02:00
}
// global once because is not necessary to check for updates on more than one iris station*
var updateOnce sync . Once
const (
githubOwner = "kataras"
githubRepo = "iris"
)
// CheckForUpdates will try to search for newer version of Iris based on the https://github.com/kataras/iris/releases
// If a newer version found then the app will ask the he dev/user if want to update the 'x' version
// if 'y' is pressed then the updater will try to install the latest version
// the updater, will notify the dev/user that the update is finished and should restart the App manually.
// Note: exported func CheckForUpdates exists because of the reason that an update can be executed while Iris is running
func ( s * Framework ) CheckForUpdates ( force bool ) {
updated := false
checker := func ( ) {
2017-02-14 04:54:11 +01:00
fs . DefaultUpdaterAlreadyInstalledMessage = "Updater: Running with the latest version(%s)\n"
2016-09-27 15:28:38 +02:00
updater , err := fs . GetUpdater ( githubOwner , githubRepo , Version )
if err != nil {
2017-02-14 04:54:11 +01:00
// ignore writer's error
s . Log ( DevMode , "update failed: " + err . Error ( ) )
2016-12-31 13:40:19 +01:00
return
2016-09-27 15:28:38 +02:00
}
2017-02-14 04:54:11 +01:00
updated = updater . Run ( fs . Stdout ( s . policies . LoggerPolicy ) , fs . Stderr ( s . policies . LoggerPolicy ) , fs . Silent ( false ) )
2016-09-27 15:28:38 +02:00
}
if force {
checker ( )
} else {
updateOnce . Do ( checker )
}
if updated { // if updated, then do not run the web server
2017-02-14 04:54:11 +01:00
s . Log ( DevMode , "exiting now..." )
2016-09-27 15:28:38 +02:00
os . Exit ( 1 )
}
2016-06-16 19:08:59 +02:00
}
2017-02-14 04:54:11 +01:00
// Adapt adapds a policy to the Framework.
// It accepts single or more objects that implements the iris.Policy.
// Iris provides some of them but you can build your own based on one or more of these:
// - iris.EventPolicy
// - iris.RouterReversionPolicy
// - iris.RouterBuilderPolicy
// - iris.RouterWrapperPolicy
// - iris.TemplateRenderPolicy
// - iris.TemplateFuncsPolicy
:rainbow: sessions were re-written, update to 4.0.0-alpha.2, read HISTORY.md
**Sessions were re-written **
- Developers can use more than one 'session database', at the same time,
to store the sessions
- Easy to develop a custom session database (only two functions are
required (Load & Update)), [learn
more](https://github.com/iris-contrib/sessiondb/blob/master/redis/database.go)
- Session databases are located
[here](https://github.com/iris-contrib/sessiondb), contributions are
welcome
- The only frontend deleted 'thing' is the: **config.Sessions.Provider**
- No need to register a database, the sessions works out-of-the-box
- No frontend/API changes except the
`context.Session().Set/Delete/Clear`, they doesn't return errors
anymore, btw they (errors) were always nil :)
- Examples (master branch) were updated.
```sh
$ go get github.com/iris-contrib/sessiondb/$DATABASE
```
```go
db := $DATABASE.New(configurationHere{})
iris.UseSessionDB(db)
```
> Note: Book is not updated yet, examples are up-to-date as always.
2016-07-15 19:50:36 +02:00
//
2017-02-14 04:54:11 +01:00
// With a Policy you can change the behavior of almost each of the existing Iris' features.
// See policy.go for more.
func ( s * Framework ) Adapt ( policies ... Policy ) {
for i := range policies {
policies [ i ] . Adapt ( & s . policies )
}
:rainbow: sessions were re-written, update to 4.0.0-alpha.2, read HISTORY.md
**Sessions were re-written **
- Developers can use more than one 'session database', at the same time,
to store the sessions
- Easy to develop a custom session database (only two functions are
required (Load & Update)), [learn
more](https://github.com/iris-contrib/sessiondb/blob/master/redis/database.go)
- Session databases are located
[here](https://github.com/iris-contrib/sessiondb), contributions are
welcome
- The only frontend deleted 'thing' is the: **config.Sessions.Provider**
- No need to register a database, the sessions works out-of-the-box
- No frontend/API changes except the
`context.Session().Set/Delete/Clear`, they doesn't return errors
anymore, btw they (errors) were always nil :)
- Examples (master branch) were updated.
```sh
$ go get github.com/iris-contrib/sessiondb/$DATABASE
```
```go
db := $DATABASE.New(configurationHere{})
iris.UseSessionDB(db)
```
> Note: Book is not updated yet, examples are up-to-date as always.
2016-07-15 19:50:36 +02:00
}
2016-07-13 05:28:09 +02:00
:rainbow: sessions were re-written, update to 4.0.0-alpha.2, read HISTORY.md
**Sessions were re-written **
- Developers can use more than one 'session database', at the same time,
to store the sessions
- Easy to develop a custom session database (only two functions are
required (Load & Update)), [learn
more](https://github.com/iris-contrib/sessiondb/blob/master/redis/database.go)
- Session databases are located
[here](https://github.com/iris-contrib/sessiondb), contributions are
welcome
- The only frontend deleted 'thing' is the: **config.Sessions.Provider**
- No need to register a database, the sessions works out-of-the-box
- No frontend/API changes except the
`context.Session().Set/Delete/Clear`, they doesn't return errors
anymore, btw they (errors) were always nil :)
- Examples (master branch) were updated.
```sh
$ go get github.com/iris-contrib/sessiondb/$DATABASE
```
```go
db := $DATABASE.New(configurationHere{})
iris.UseSessionDB(db)
```
> Note: Book is not updated yet, examples are up-to-date as always.
2016-07-15 19:50:36 +02:00
// UseSessionDB registers a session database, you can register more than one
// accepts a session database which implements a Load(sid string) map[string]interface{} and an Update(sid string, newValues map[string]interface{})
// the only reason that a session database will be useful for you is when you want to keep the session's values/data after the app restart
// a session database doesn't have write access to the session, it doesn't accept the context, so forget 'cookie database' for sessions, I will never allow that, for your protection.
//
// Note: Don't worry if no session database is registered, your context.Session will continue to work.
2016-09-04 21:02:31 +02:00
func ( s * Framework ) UseSessionDB ( db sessions . Database ) {
s . sessions . UseDatabase ( db )
:rainbow: sessions were re-written, update to 4.0.0-alpha.2, read HISTORY.md
**Sessions were re-written **
- Developers can use more than one 'session database', at the same time,
to store the sessions
- Easy to develop a custom session database (only two functions are
required (Load & Update)), [learn
more](https://github.com/iris-contrib/sessiondb/blob/master/redis/database.go)
- Session databases are located
[here](https://github.com/iris-contrib/sessiondb), contributions are
welcome
- The only frontend deleted 'thing' is the: **config.Sessions.Provider**
- No need to register a database, the sessions works out-of-the-box
- No frontend/API changes except the
`context.Session().Set/Delete/Clear`, they doesn't return errors
anymore, btw they (errors) were always nil :)
- Examples (master branch) were updated.
```sh
$ go get github.com/iris-contrib/sessiondb/$DATABASE
```
```go
db := $DATABASE.New(configurationHere{})
iris.UseSessionDB(db)
```
> Note: Book is not updated yet, examples are up-to-date as always.
2016-07-15 19:50:36 +02:00
}
2016-07-13 05:28:09 +02:00
2017-01-08 05:11:50 +01:00
// DestroySessionByID removes the session entry
// from the server-side memory (and database if registered).
// Client's session cookie will still exist but it will be reseted on the next request.
//
// It's safe to use it even if you are not sure if a session with that id exists.
func ( s * Framework ) DestroySessionByID ( sid string ) {
s . sessions . DestroyByID ( sid )
}
// DestroyAllSessions removes all sessions
// from the server-side memory (and database if registered).
// Client's session cookie will still exist but it will be reseted on the next request.
func ( s * Framework ) DestroyAllSessions ( ) {
s . sessions . DestroyAll ( )
}
2017-02-14 04:54:11 +01:00
// cachedMuxEntry is just a wrapper for the Cache functionality
// it seems useless but I prefer to keep the cached handler on its own memory stack,
// reason: no clojures hell in the Cache function
type cachedMuxEntry struct {
cachedHandler http . Handler
Update to 4.0.0-alpha.3 - Response Engines, 'inject' the context.JSON/JSONP/Text/Data/Markdown/Render, Read HISTORY.md
## 4.0.0-alpha.2 -> 4.0.0-alpha.3
**New**
A **Response Engine** gives you the freedom to create/change the
render/response writer for
- `context.JSON`
- `context.JSONP`
- `context.XML`
- `context.Text`
- `context.Markdown`
- `context.Data`
- `context.Render("my_custom_type",mystructOrData{},
iris.RenderOptions{"gzip":false,"charset":"UTF-8"})`
- `context.MarkdownString`
- `iris.ResponseString(...)`
**Fix**
- https://github.com/kataras/iris/issues/294
**Small changes**
- `iris.Config.Charset`, before alpha.3 was `iris.Config.Rest.Charset` &
`iris.Config.Render.Template.Charset`, but you can override it at
runtime by passinth a map `iris.RenderOptions` on the `context.Render`
call .
- `iris.Config.IsDevelopment` , before alpha.1 was
`iris.Config.Render.Template.IsDevelopment`
**Websockets changes**
No need to import the `github.com/kataras/iris/websocket` to use the
`Connection` iteral, the websocket moved inside `kataras/iris` , now all
exported variables' names have the prefix of `Websocket`, so the old
`websocket.Connection` is now `iris.WebsocketConnection`.
Generally, no other changes on the 'frontend API', for response engines
examples and how you can register your own to add more features on
existing response engines or replace them, look
[here](https://github.com/iris-contrib/response).
**BAD SIDE**: E-Book is still pointing on the v3 release, but will be
updated soon.
2016-07-18 16:40:42 +02:00
}
2017-02-14 04:54:11 +01:00
func newCachedMuxEntry ( s * Framework , bodyHandler HandlerFunc , expiration time . Duration ) * cachedMuxEntry {
httpHandler := ToNativeHandler ( s , bodyHandler )
2016-09-29 16:05:22 +02:00
2017-02-14 04:54:11 +01:00
cachedHandler := httpcache . Cache ( httpHandler , expiration )
return & cachedMuxEntry {
cachedHandler : cachedHandler ,
}
2016-09-29 16:05:22 +02:00
}
2017-02-14 04:54:11 +01:00
func ( c * cachedMuxEntry ) Serve ( ctx * Context ) {
c . cachedHandler . ServeHTTP ( ctx . ResponseWriter , ctx . Request )
2017-01-10 13:21:49 +01:00
}
2017-02-14 04:54:11 +01:00
// Cache is just a wrapper for a route's handler which you want to enable body caching
// Usage: iris.Default.Get("/", iris.Cache(func(ctx *iris.Context){
// ctx.WriteString("Hello, world!") // or a template or anything else
// }, time.Duration(10*time.Second))) // duration of expiration
// if <=time.Second then it tries to find it though request header's "cache-control" maxage value
2017-01-10 13:21:49 +01:00
//
2017-02-14 04:54:11 +01:00
// Note that it depends on a station instance's cache service.
// Do not try to call it from default' station if you use the form of app := iris.New(),
// use the app.Cache instead of iris.Cache
func ( s * Framework ) Cache ( bodyHandler HandlerFunc , expiration time . Duration ) HandlerFunc {
ce := newCachedMuxEntry ( s , bodyHandler , expiration )
return ce . Serve
2016-07-13 05:28:09 +02:00
}
2017-02-14 04:54:11 +01:00
// Path used to check arguments with the route's named parameters and return the correct url
// if parse failed returns empty string
func ( s * Framework ) Path ( routeName string , args ... interface { } ) string {
r := s . Router . Routes ( ) . Lookup ( routeName )
if r == nil {
return ""
}
2016-07-13 05:28:09 +02:00
2017-02-14 04:54:11 +01:00
// why receive interface{}
// but need string?
// because the key:value are string for a route path
// but in the template functions all works fine with ...string
// except when the developer wants to pass that string from a binding
// via Render, then the template will fail to render
// because of expecting string; but got []string
2016-06-17 06:18:09 +02:00
2017-02-14 04:54:11 +01:00
var argsString [ ] string
if len ( args ) > 0 {
argsString = make ( [ ] string , len ( args ) )
}
2016-06-17 06:18:09 +02:00
2017-02-14 04:54:11 +01:00
for i , v := range args {
if s , ok := v . ( string ) ; ok {
argsString [ i ] = s
} else if num , ok := v . ( int ) ; ok {
argsString [ i ] = strconv . Itoa ( num )
} else if b , ok := v . ( bool ) ; ok {
argsString [ i ] = strconv . FormatBool ( b )
} else if arr , ok := v . ( [ ] string ) ; ok {
if len ( arr ) > 0 {
argsString [ i ] = arr [ 0 ]
argsString = append ( argsString , arr [ 1 : ] ... )
}
2017-01-04 20:29:58 +01:00
}
2016-06-17 06:18:09 +02:00
}
2017-01-04 20:29:58 +01:00
2017-02-14 04:54:11 +01:00
return s . policies . RouterReversionPolicy . URLPath ( r , argsString ... )
2016-06-14 07:45:40 +02:00
}
2017-01-02 20:20:17 +01:00
// DecodeQuery returns the uri parameter as url (string)
2016-07-21 19:33:00 +02:00
// useful when you want to pass something to a database and be valid to retrieve it via context.Param
// use it only for special cases, when the default behavior doesn't suits you.
//
// http://www.blooberry.com/indexdot/html/topics/urlencoding.htm
// it uses just the url.QueryUnescape
2017-01-02 20:20:17 +01:00
func DecodeQuery ( path string ) string {
if path == "" {
2016-07-21 19:33:00 +02:00
return ""
}
2017-01-02 20:20:17 +01:00
encodedPath , err := url . QueryUnescape ( path )
if err != nil {
return path
}
2016-07-21 19:33:00 +02:00
return encodedPath
}
2017-01-02 20:20:17 +01:00
// DecodeURL returns the decoded uri
2016-07-21 19:33:00 +02:00
// useful when you want to pass something to a database and be valid to retrieve it via context.Param
// use it only for special cases, when the default behavior doesn't suits you.
//
// http://www.blooberry.com/indexdot/html/topics/urlencoding.htm
2017-01-02 20:20:17 +01:00
// it uses just the url.Parse
func DecodeURL ( uri string ) string {
u , err := url . Parse ( uri )
if err != nil {
2017-01-10 07:54:39 +01:00
return uri
2016-07-21 19:33:00 +02:00
}
2017-01-02 20:20:17 +01:00
return u . String ( )
2016-07-21 19:33:00 +02:00
}
2016-06-14 07:45:40 +02:00
// URL returns the subdomain+ host + Path(...optional named parameters if route is dynamic)
// returns an empty string if parse is failed
func ( s * Framework ) URL ( routeName string , args ... interface { } ) ( url string ) {
2017-02-14 04:54:11 +01:00
r := s . Router . Routes ( ) . Lookup ( routeName )
2016-06-14 07:45:40 +02:00
if r == nil {
return
}
2016-09-27 15:28:38 +02:00
scheme := s . Config . VScheme // if s.Config.VScheme was setted, that will be used instead of the real, in order to make easy to run behind nginx
host := s . Config . VHost // if s.Config.VHost was setted, that will be used instead of the real, in order to make easy to run behind nginx
2016-05-30 16:08:09 +02:00
2016-06-14 07:45:40 +02:00
// if it's dynamic subdomain then the first argument is the subdomain part
2017-02-14 04:54:11 +01:00
// for this part we are responsible not the custom routers
if r . Subdomain ( ) == DynamicSubdomainIndicator {
if len ( args ) == 0 { // it's a wildcard subdomain but not arguments
2016-06-14 07:45:40 +02:00
return
}
2016-05-30 16:08:09 +02:00
2017-02-14 04:54:11 +01:00
if subdomain , ok := args [ 0 ] . ( string ) ; ok {
2016-06-14 07:45:40 +02:00
host = subdomain + "." + host
} else {
// it is not array because we join them before. if not pass a string then this is not a subdomain part, return empty uri
return
2016-05-30 16:08:09 +02:00
}
2017-02-14 04:54:11 +01:00
args = args [ 1 : ] // remove the subdomain part for the arguments,
2016-05-30 16:08:09 +02:00
}
2017-02-14 04:54:11 +01:00
if parsedPath := s . Path ( routeName , args ... ) ; parsedPath != "" {
2016-06-14 07:45:40 +02:00
url = scheme + host + parsedPath
2016-06-06 20:04:38 +02:00
}
2016-06-14 07:45:40 +02:00
return
}
2016-05-30 16:08:09 +02:00
2017-02-14 04:54:11 +01:00
var errTemplateRendererIsMissing = errors . New (
`
manually call of Render for a template : ' % s ' without specified RenderPolicy !
Please . Adapt one of the available view engines inside ' kataras / iris / adaptors / view ' .
By - default Iris supports five template engines :
- standard html | view . HTML ( ... )
- django | view . Django ( ... )
- handlebars | view . Handlebars ( ... )
- pug ( jade ) | view . Pug ( ... )
- amber | view . Amber ( ... )
Edit your main . go source file to adapt one of these and restart your app .
i . e : lines ( <- -- ) were missing .
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
import (
"github.com/kataras/iris"
"github.com/kataras/iris/adaptors/httprouter" // or gorillamux
"github.com/kataras/iris/adaptors/view" // <--- this line
)
func main ( ) {
app := iris . New ( )
app . Adapt ( httprouter . New ( ) ) // or gorillamux.New()
// right below the iris.New()
app . Adapt ( view . HTML ( "./templates" , ".html" ) ) // <--- and this line were missing.
app . Listen ( "%s" )
}
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
` )
// RenderOptions is a helper type for the optional runtime options can be passed by user when Render called.
// I.e the "layout" or "gzip" option
// same as iris.Map but more specific name
type RenderOptions map [ string ] interface { }
// Render renders using the specific template or any other rich content renderer to the 'w'.
//
// Example of usage:
// - send an e-mail using a template engine that you already
// adapted via: app.Adapt(view.HTML("./templates", ".html")) or app.Adapt(iris.RenderPolicy(mycustomRenderer)).
//
// It can also render json,xml,jsonp and markdown by-default before or after .Build too.
func ( s * Framework ) Render ( w io . Writer , name string , bind interface { } , options ... map [ string ] interface { } ) error {
err , ok := s . policies . RenderPolicy ( w , name , bind , options ... )
if ! ok {
// ok is false ONLY WHEN there is no registered render policy
// that is responsible for that 'name` (if contains dot '.' it's for templates).
// We don't use default template engines on the new version,
// so we should notice the user here, we could make it to panic but because that is on runtime
// we don't want to panic for that, let's give a message if the user adapted a logger for dev.
// And return that error in the case the user wasn't in dev mode, she/he can catch this error.
// Also on the README we will add the .Adapt(iris.DevLogger()) to mention that
// logging for any runtime info(except http server's panics and unexpected serious errors) is not enabled by-default.
if strings . Contains ( name , "." ) {
err = errTemplateRendererIsMissing . Format ( name , s . Config . VHost )
s . Log ( DevMode , err . Error ( ) )
return err
}
}
return err
2016-08-14 04:44:36 +02:00
}