2016-05-30 16:08:09 +02:00
package iris
import (
"path"
"reflect"
"strconv"
"strings"
"os"
"time"
"github.com/kataras/iris/config"
"github.com/kataras/iris/context"
"github.com/kataras/iris/utils"
"github.com/valyala/fasthttp"
)
type (
// IParty is the interface which implements the whole Party of routes
IParty interface {
2016-06-02 03:45:03 +02:00
Handle ( string , string , ... Handler ) IRoute
HandleFunc ( string , string , ... HandlerFunc ) IRoute
2016-06-04 15:20:32 +02:00
Wildcard ( string , string , ... HandlerFunc )
2016-05-30 16:08:09 +02:00
API ( path string , controller HandlerAPI , middlewares ... HandlerFunc ) error
2016-06-02 03:45:03 +02:00
Get ( string , ... HandlerFunc ) RouteNameFunc
Post ( string , ... HandlerFunc ) RouteNameFunc
Put ( string , ... HandlerFunc ) RouteNameFunc
Delete ( string , ... HandlerFunc ) RouteNameFunc
Connect ( string , ... HandlerFunc ) RouteNameFunc
Head ( string , ... HandlerFunc ) RouteNameFunc
Options ( string , ... HandlerFunc ) RouteNameFunc
Patch ( string , ... HandlerFunc ) RouteNameFunc
Trace ( string , ... HandlerFunc ) RouteNameFunc
Any ( string , ... HandlerFunc ) [ ] IRoute
2016-05-30 16:08:09 +02:00
Use ( ... Handler )
UseFunc ( ... HandlerFunc )
StaticHandlerFunc ( systemPath string , stripSlashes int , compress bool , generateIndexPages bool , indexNames [ ] string ) HandlerFunc
Static ( string , string , int )
StaticFS ( string , string , int )
StaticWeb ( relative string , systemPath string , stripSlashes int )
StaticServe ( systemPath string , requestPath ... string )
Party ( string , ... HandlerFunc ) IParty // Each party can have a party too
IsRoot ( ) bool
}
// GardenParty is the struct which makes all the job for registering routes and middlewares
GardenParty struct {
relativePath string
station * Iris // this station is where the party is happening, this station's Garden is the same for all Parties per Station & Router instance
middleware Middleware
root bool
}
)
var _ IParty = & GardenParty { }
// IsRoot returns true if this is the root party ("/")
func ( p * GardenParty ) IsRoot ( ) bool {
return p . root
}
// Handle registers a route to the server's router
2016-06-02 03:45:03 +02:00
// if empty method is passed then registers handler(s) for all methods, same as .Any, but returns nil as result
func ( p * GardenParty ) Handle ( method string , registedPath string , handlers ... Handler ) IRoute {
2016-05-30 16:08:09 +02:00
if method == "" { // then use like it was .Any
for _ , k := range AllMethods {
p . Handle ( k , registedPath , handlers ... )
}
2016-06-02 03:45:03 +02:00
return nil
2016-05-30 16:08:09 +02:00
}
path := fixPath ( p . relativePath + registedPath ) // keep the last "/" as default ex: "/xyz/"
if ! p . station . config . DisablePathCorrection {
// if we have path correction remove it with absPath
path = fixPath ( absPath ( p . relativePath , registedPath ) ) // "/xyz"
}
middleware := JoinMiddleware ( p . middleware , handlers )
2016-06-04 22:07:19 +02:00
route := NewRoute ( method , path , middleware , p . station )
2016-05-30 16:08:09 +02:00
p . station . plugins . DoPreHandle ( route )
p . station . addRoute ( route )
p . station . plugins . DoPostHandle ( route )
2016-06-02 03:45:03 +02:00
return route
2016-05-30 16:08:09 +02:00
}
// HandleFunc registers and returns a route with a method string, path string and a handler
// registedPath is the relative url path
// handler is the iris.Handler which you can pass anything you want via iris.ToHandlerFunc(func(res,req){})... or just use func(c *iris.Context)
2016-06-02 03:45:03 +02:00
func ( p * GardenParty ) HandleFunc ( method string , registedPath string , handlersFn ... HandlerFunc ) IRoute {
return p . Handle ( method , registedPath , ConvertToHandlers ( handlersFn ) ... )
2016-05-30 16:08:09 +02:00
}
2016-06-04 15:20:32 +02:00
// Wildcard same as .Party("*.")
// registers a route for Dynamic subdomain
// receives three parameters
// the first is the http method
// the second is the request path, can be a dynamic path also like others
// the third are the handlerfuncs
//
// Note that this is just a global route, no party's route.
// example: subdomains_2
func ( p * GardenParty ) Wildcard ( method string , registedPath string , handlersFn ... HandlerFunc ) {
path := PrefixDynamicSubdomain + registedPath
p . station . router . HandleFunc ( method , path , handlersFn ... )
}
2016-05-30 16:08:09 +02:00
// API converts & registers a custom struct to the router
// receives two parameters
// first is the request path (string)
// second is the custom struct (interface{}) which can be anything that has a *iris.Context as field.
// third are the common middlewares, is optional parameter
//
2016-06-02 03:45:03 +02:00
// Note that API's routes have their default-name to the full registed path,
// no need to give a special name for it, because it's not supposed to be used inside your templates.
//
2016-05-30 16:08:09 +02:00
// Recommend to use when you retrieve data from an external database,
// and the router-performance is not the (only) thing which slows the server's overall performance.
//
// This is a slow method, if you care about router-performance use the Handle/HandleFunc/Get/Post/Put/Delete/Trace/Options... instead
//
// Usage:
// All the below methods are optional except the *iris.Context field,
// example with /users :
/ *
package main
import (
"github.com/kataras/iris"
)
type UserAPI struct {
* iris . Context
}
// GET /users
func ( u UserAPI ) Get ( ) {
u . Write ( "Get from /users" )
// u.JSON(iris.StatusOK,myDb.AllUsers())
}
// GET /:param1 which its value passed to the id argument
func ( u UserAPI ) GetBy ( id string ) { // id equals to u.Param("param1")
u . Write ( "Get from /users/%s" , id )
// u.JSON(iris.StatusOK, myDb.GetUserById(id))
}
// PUT /users
func ( u UserAPI ) Put ( ) {
name := u . FormValue ( "name" )
// myDb.InsertUser(...)
println ( string ( name ) )
println ( "Put from /users" )
}
// POST /users/:param1
func ( u UserAPI ) PostBy ( id string ) {
name := u . FormValue ( "name" ) // you can still use the whole Context's features!
// myDb.UpdateUser(...)
println ( string ( name ) )
println ( "Post from /users/" + id )
}
// DELETE /users/:param1
func ( u UserAPI ) DeleteBy ( id string ) {
// myDb.DeleteUser(id)
println ( "Delete from /" + id )
}
func main ( ) {
iris . API ( "/users" , UserAPI { } )
iris . Listen ( ":80" )
}
* /
func ( p * GardenParty ) API ( path string , controller HandlerAPI , middlewares ... HandlerFunc ) error {
// here we need to find the registed methods and convert them to handler funcs
// methods are collected by method naming: Get(),GetBy(...), Post(),PostBy(...), Put() and so on
typ := reflect . ValueOf ( controller ) . Type ( )
contextField , found := typ . FieldByName ( "Context" )
if ! found {
return ErrControllerContextNotFound . Return ( )
}
// check & register the Get(),Post(),Put(),Delete() and so on
for _ , methodName := range AllMethods {
methodCapitalName := strings . Title ( strings . ToLower ( methodName ) )
if method , found := typ . MethodByName ( methodCapitalName ) ; found {
methodFunc := method . Func
if ! methodFunc . IsValid ( ) || methodFunc . Type ( ) . NumIn ( ) > 1 { // for any case
continue
}
func ( path string , typ reflect . Type , contextField reflect . StructField , methodFunc reflect . Value , method string ) {
2016-06-03 04:11:50 +02:00
var handlersFn [ ] HandlerFunc
2016-05-30 16:08:09 +02:00
handlersFn = append ( handlersFn , middlewares ... )
handlersFn = append ( handlersFn , func ( ctx * Context ) {
newController := reflect . New ( typ ) . Elem ( )
newController . FieldByName ( "Context" ) . Set ( reflect . ValueOf ( ctx ) )
methodFunc . Call ( [ ] reflect . Value { newController } )
} )
// register route
p . HandleFunc ( method , path , handlersFn ... )
} ( path , typ , contextField , methodFunc , methodName )
}
}
// check for GetBy/PostBy(id string, something_else string) , these must be requested by the same order.
// (we could do this in the same top loop but I don't want)
// GET, DELETE -> with url named parameters (/users/:id/:secondArgumentIfExists)
// POST, PUT -> with post values (form)
// all other with URL Parameters (?something=this&else=other
//
// or no, I changed my mind, let all be named parameters and let users to decide what info they need,
// using the Context to take more values (post form,url params and so on).-
for _ , methodName := range AllMethods {
methodWithBy := strings . Title ( strings . ToLower ( methodName ) ) + "By"
if method , found := typ . MethodByName ( methodWithBy ) ; found {
methodFunc := method . Func
if ! methodFunc . IsValid ( ) || methodFunc . Type ( ) . NumIn ( ) < 2 { //it's By but it has not receive any arguments so its not api's
continue
}
methodFuncType := methodFunc . Type ( )
numInLen := methodFuncType . NumIn ( ) // how much data we should receive from the request
registedPath := path
for i := 1 ; i < numInLen ; i ++ { // from 1 because the first is the 'object'
if registedPath [ len ( registedPath ) - 1 ] == SlashByte {
registedPath += ":param" + strconv . Itoa ( i )
} else {
registedPath += "/:param" + strconv . Itoa ( i )
}
}
func ( registedPath string , typ reflect . Type , contextField reflect . StructField , methodFunc reflect . Value , paramsLen int , method string ) {
2016-06-03 04:11:50 +02:00
var handlersFn [ ] HandlerFunc
2016-05-30 16:08:09 +02:00
handlersFn = append ( handlersFn , middlewares ... )
handlersFn = append ( handlersFn , func ( ctx * Context ) {
newController := reflect . New ( typ ) . Elem ( )
newController . FieldByName ( "Context" ) . Set ( reflect . ValueOf ( ctx ) )
args := make ( [ ] reflect . Value , paramsLen + 1 , paramsLen + 1 )
args [ 0 ] = newController
for i := 0 ; i < paramsLen ; i ++ {
args [ i + 1 ] = reflect . ValueOf ( ctx . Params [ i ] . Value )
}
methodFunc . Call ( args )
} )
// register route
p . HandleFunc ( method , registedPath , handlersFn ... )
} ( registedPath , typ , contextField , methodFunc , numInLen - 1 , methodName )
}
}
return nil
}
// Get registers a route for the Get http method
2016-06-02 03:45:03 +02:00
func ( p * GardenParty ) Get ( path string , handlersFn ... HandlerFunc ) RouteNameFunc {
return p . HandleFunc ( MethodGet , path , handlersFn ... ) . Name
2016-05-30 16:08:09 +02:00
}
// Post registers a route for the Post http method
2016-06-02 03:45:03 +02:00
func ( p * GardenParty ) Post ( path string , handlersFn ... HandlerFunc ) RouteNameFunc {
return p . HandleFunc ( MethodPost , path , handlersFn ... ) . Name
2016-05-30 16:08:09 +02:00
}
// Put registers a route for the Put http method
2016-06-02 03:45:03 +02:00
func ( p * GardenParty ) Put ( path string , handlersFn ... HandlerFunc ) RouteNameFunc {
return p . HandleFunc ( MethodPut , path , handlersFn ... ) . Name
2016-05-30 16:08:09 +02:00
}
// Delete registers a route for the Delete http method
2016-06-02 03:45:03 +02:00
func ( p * GardenParty ) Delete ( path string , handlersFn ... HandlerFunc ) RouteNameFunc {
return p . HandleFunc ( MethodDelete , path , handlersFn ... ) . Name
2016-05-30 16:08:09 +02:00
}
// Connect registers a route for the Connect http method
2016-06-02 03:45:03 +02:00
func ( p * GardenParty ) Connect ( path string , handlersFn ... HandlerFunc ) RouteNameFunc {
return p . HandleFunc ( MethodConnect , path , handlersFn ... ) . Name
2016-05-30 16:08:09 +02:00
}
// Head registers a route for the Head http method
2016-06-02 03:45:03 +02:00
func ( p * GardenParty ) Head ( path string , handlersFn ... HandlerFunc ) RouteNameFunc {
return p . HandleFunc ( MethodHead , path , handlersFn ... ) . Name
2016-05-30 16:08:09 +02:00
}
// Options registers a route for the Options http method
2016-06-02 03:45:03 +02:00
func ( p * GardenParty ) Options ( path string , handlersFn ... HandlerFunc ) RouteNameFunc {
return p . HandleFunc ( MethodOptions , path , handlersFn ... ) . Name
2016-05-30 16:08:09 +02:00
}
// Patch registers a route for the Patch http method
2016-06-02 03:45:03 +02:00
func ( p * GardenParty ) Patch ( path string , handlersFn ... HandlerFunc ) RouteNameFunc {
return p . HandleFunc ( MethodPatch , path , handlersFn ... ) . Name
2016-05-30 16:08:09 +02:00
}
// Trace registers a route for the Trace http method
2016-06-02 03:45:03 +02:00
func ( p * GardenParty ) Trace ( path string , handlersFn ... HandlerFunc ) RouteNameFunc {
return p . HandleFunc ( MethodTrace , path , handlersFn ... ) . Name
2016-05-30 16:08:09 +02:00
}
// Any registers a route for ALL of the http methods (Get,Post,Put,Head,Patch,Options,Connect,Delete)
2016-06-02 03:45:03 +02:00
func ( p * GardenParty ) Any ( registedPath string , handlersFn ... HandlerFunc ) [ ] IRoute {
theRoutes := make ( [ ] IRoute , len ( AllMethods ) , len ( AllMethods ) )
for idx , k := range AllMethods {
r := p . HandleFunc ( k , registedPath , handlersFn ... )
theRoutes [ idx ] = r
2016-05-30 16:08:09 +02:00
}
2016-06-02 03:45:03 +02:00
return theRoutes
2016-05-30 16:08:09 +02:00
}
// H_ is used to convert a context.IContext handler func to iris.HandlerFunc, is used only inside iris internal package to avoid import cycles
func ( p * GardenParty ) H_ ( method string , registedPath string , fn func ( context . IContext ) ) {
p . HandleFunc ( method , registedPath , func ( ctx * Context ) {
fn ( ctx )
} )
}
// Use registers a Handler middleware
func ( p * GardenParty ) Use ( handlers ... Handler ) {
p . middleware = append ( p . middleware , handlers ... )
}
// UseFunc registers a HandlerFunc middleware
func ( p * GardenParty ) UseFunc ( handlersFn ... HandlerFunc ) {
p . Use ( ConvertToHandlers ( handlersFn ) ... )
}
// StaticHandlerFunc returns a HandlerFunc to serve static system directory
// Accepts 5 parameters
//
// first is the systemPath (string)
// Path to the root directory to serve files from.
//
// second is the stripSlashes (int) level
// * stripSlashes = 0, original path: "/foo/bar", result: "/foo/bar"
// * stripSlashes = 1, original path: "/foo/bar", result: "/bar"
// * stripSlashes = 2, original path: "/foo/bar", result: ""
//
// third is the compress (bool)
// Transparently compresses responses if set to true.
//
// The server tries minimizing CPU usage by caching compressed files.
// It adds fasthttp.FSCompressedFileSuffix suffix to the original file name and
// tries saving the resulting compressed file under the new file name.
// So it is advisable to give the server write access to Root
// and to all inner folders in order to minimze CPU usage when serving
// compressed responses.
//
// fourth is the generateIndexPages (bool)
// Index pages for directories without files matching IndexNames
// are automatically generated if set.
//
// Directory index generation may be quite slow for directories
// with many files (more than 1K), so it is discouraged enabling
// index pages' generation for such directories.
//
// fifth is the indexNames ([]string)
// List of index file names to try opening during directory access.
//
// For example:
//
// * index.html
// * index.htm
// * my-super-index.xml
//
func ( p * GardenParty ) StaticHandlerFunc ( systemPath string , stripSlashes int , compress bool , generateIndexPages bool , indexNames [ ] string ) HandlerFunc {
if indexNames == nil {
indexNames = [ ] string { }
}
fs := & fasthttp . FS {
// Path to directory to serve.
Root : systemPath ,
IndexNames : indexNames ,
// Generate index pages if client requests directory contents.
GenerateIndexPages : generateIndexPages ,
// Enable transparent compression to save network traffic.
Compress : compress ,
CacheDuration : config . StaticCacheDuration ,
CompressedFileSuffix : config . CompressedFileSuffix ,
}
if stripSlashes > 0 {
fs . PathRewrite = fasthttp . NewPathSlashesStripper ( stripSlashes )
}
// Create request handler for serving static files.
h := fs . NewRequestHandler ( )
return func ( ctx * Context ) {
h ( ctx . RequestCtx )
errCode := ctx . RequestCtx . Response . StatusCode ( )
if errHandler := ctx . station . router . GetByCode ( errCode ) ; errHandler != nil {
ctx . RequestCtx . Response . ResetBody ( )
ctx . EmitError ( errCode )
}
if ctx . pos < uint8 ( len ( ctx . middleware ) ) - 1 {
ctx . Next ( ) // for any case
}
}
}
// Static registers a route which serves a system directory
// this doesn't generates an index page which list all files
// no compression is used also, for these features look at StaticFS func
// accepts three parameters
// first parameter is the request url path (string)
// second parameter is the system directory (string)
// third parameter is the level (int) of stripSlashes
// * stripSlashes = 0, original path: "/foo/bar", result: "/foo/bar"
// * stripSlashes = 1, original path: "/foo/bar", result: "/bar"
// * stripSlashes = 2, original path: "/foo/bar", result: ""
func ( p * GardenParty ) Static ( relative string , systemPath string , stripSlashes int ) {
if relative [ len ( relative ) - 1 ] != SlashByte { // if / then /*filepath, if /something then /something/*filepath
2016-06-03 04:11:50 +02:00
relative += Slash
2016-05-30 16:08:09 +02:00
}
h := p . StaticHandlerFunc ( systemPath , stripSlashes , false , false , nil )
p . Get ( relative + "*filepath" , h )
p . Head ( relative + "*filepath" , h )
}
// StaticFS registers a route which serves a system directory
// this is the fastest method to serve static files
// generates an index page which list all files
// if you use this method it will generate compressed files also
// think this function as small fileserver with http
// accepts three parameters
// first parameter is the request url path (string)
// second parameter is the system directory (string)
// third parameter is the level (int) of stripSlashes
// * stripSlashes = 0, original path: "/foo/bar", result: "/foo/bar"
// * stripSlashes = 1, original path: "/foo/bar", result: "/bar"
// * stripSlashes = 2, original path: "/foo/bar", result: ""
func ( p * GardenParty ) StaticFS ( reqPath string , systemPath string , stripSlashes int ) {
if reqPath [ len ( reqPath ) - 1 ] != SlashByte {
reqPath += "/"
}
h := p . StaticHandlerFunc ( systemPath , stripSlashes , true , true , nil )
p . Get ( reqPath + "*filepath" , h )
p . Head ( reqPath + "*filepath" , h )
}
// StaticWeb same as Static but if index.html exists and request uri is '/' then display the index.html's contents
// accepts three parameters
// first parameter is the request url path (string)
// second parameter is the system directory (string)
// third parameter is the level (int) of stripSlashes
// * stripSlashes = 0, original path: "/foo/bar", result: "/foo/bar"
// * stripSlashes = 1, original path: "/foo/bar", result: "/bar"
// * stripSlashes = 2, original path: "/foo/bar", result: ""
// * if you don't know what to put on stripSlashes just 1
func ( p * GardenParty ) StaticWeb ( reqPath string , systemPath string , stripSlashes int ) {
if reqPath [ len ( reqPath ) - 1 ] != SlashByte { // if / then /*filepath, if /something then /something/*filepath
reqPath += "/"
}
hasIndex := utils . Exists ( systemPath + utils . PathSeparator + "index.html" )
serveHandler := p . StaticHandlerFunc ( systemPath , stripSlashes , false , ! hasIndex , nil ) // if not index.html exists then generate index.html which shows the list of files
indexHandler := func ( ctx * Context ) {
if len ( ctx . Param ( "filepath" ) ) < 2 && hasIndex {
ctx . Request . SetRequestURI ( "index.html" )
}
ctx . Next ( )
}
p . Get ( reqPath + "*filepath" , indexHandler , serveHandler )
p . Head ( reqPath + "*filepath" , indexHandler , serveHandler )
}
// StaticServe serves a directory as web resource
// it's the simpliest form of the Static* functions
// Almost same usage as StaticWeb
// accepts only one required parameter which is the systemPath ( the same path will be used to register the GET&HEAD routes)
// if second parameter is empty, otherwise the requestPath is the second parameter
// it uses gzip compression (compression on each request, no file cache)
func ( p * GardenParty ) StaticServe ( systemPath string , requestPath ... string ) {
var reqPath string
if len ( reqPath ) > 0 {
reqPath = requestPath [ 0 ]
}
reqPath = strings . Replace ( systemPath , utils . PathSeparator , Slash , - 1 ) // replaces any \ to /
reqPath = strings . Replace ( reqPath , "//" , Slash , - 1 ) // for any case, replaces // to /
reqPath = strings . Replace ( reqPath , "." , "" , - 1 ) // replace any dots (./mypath -> /mypath)
p . Get ( reqPath + "/*file" , func ( ctx * Context ) {
filepath := ctx . Param ( "file" )
path := strings . Replace ( filepath , "/" , utils . PathSeparator , - 1 )
path = absPath ( systemPath , path )
if ! utils . DirectoryExists ( path ) {
ctx . NotFound ( )
return
}
ctx . ServeFile ( path , true )
} )
}
/* here in order to the subdomains be able to change favicon also */
// Favicon serves static favicon
// accepts 2 parameters, second is optionally
// favPath (string), declare the system directory path of the __.ico
// requestPath (string), it's the route's path, by default this is the "/favicon.ico" because some browsers tries to get this by default first,
// you can declare your own path if you have more than one favicon (desktop, mobile and so on)
//
// this func will add a route for you which will static serve the /yuorpath/yourfile.ico to the /yourfile.ico (nothing special that you can't handle by yourself)
// Note that you have to call it on every favicon you have to serve automatically (dekstop, mobile and so on)
//
// returns an error if something goes bad
func ( p * GardenParty ) Favicon ( favPath string , requestPath ... string ) error {
f , err := os . Open ( favPath )
if err != nil {
return ErrDirectoryFileNotFound . Format ( favPath , err . Error ( ) )
}
defer f . Close ( )
fi , _ := f . Stat ( )
if fi . IsDir ( ) { // if it's dir the try to get the favicon.ico
fav := path . Join ( favPath , "favicon.ico" )
f , err = os . Open ( fav )
if err != nil {
//we try again with .png
return p . Favicon ( path . Join ( favPath , "favicon.png" ) )
}
favPath = fav
fi , _ = f . Stat ( )
}
modtime := fi . ModTime ( ) . UTC ( ) . Format ( TimeFormat )
contentType := utils . TypeByExtension ( favPath )
// copy the bytes here in order to cache and not read the ico on each request.
cacheFav := make ( [ ] byte , fi . Size ( ) )
if _ , err = f . Read ( cacheFav ) ; err != nil {
return ErrDirectoryFileNotFound . Format ( favPath , "Couldn't read the data bytes from ico: " + err . Error ( ) )
}
h := func ( ctx * Context ) {
if t , err := time . Parse ( TimeFormat , ctx . RequestHeader ( IfModifiedSince ) ) ; err == nil && fi . ModTime ( ) . Before ( t . Add ( config . StaticCacheDuration ) ) {
ctx . Response . Header . Del ( ContentType )
ctx . Response . Header . Del ( ContentLength )
ctx . SetStatusCode ( StatusNotModified )
return
}
ctx . Response . Header . Set ( ContentType , contentType )
ctx . Response . Header . Set ( LastModified , modtime )
ctx . SetStatusCode ( StatusOK )
ctx . Response . SetBody ( cacheFav )
}
reqPath := "/favicon" + path . Ext ( fi . Name ( ) ) //we could use the filename, but because standards is /favicon.ico/.png.
if len ( requestPath ) > 0 {
reqPath = requestPath [ 0 ]
}
p . Get ( reqPath , h )
p . Head ( reqPath , h )
return nil
}
// StaticContent serves bytes, memory cached, on the reqPath
func ( p * GardenParty ) StaticContent ( reqPath string , contentType string , content [ ] byte ) {
modtime := time . Now ( )
modtimeStr := modtime . UTC ( ) . Format ( TimeFormat )
h := func ( ctx * Context ) {
if t , err := time . Parse ( TimeFormat , ctx . RequestHeader ( IfModifiedSince ) ) ; err == nil && modtime . Before ( t . Add ( config . StaticCacheDuration ) ) {
ctx . Response . Header . Del ( ContentType )
ctx . Response . Header . Del ( ContentLength )
ctx . SetStatusCode ( StatusNotModified )
return
}
ctx . Response . Header . Set ( ContentType , contentType )
ctx . Response . Header . Set ( LastModified , modtimeStr )
ctx . SetStatusCode ( StatusOK )
ctx . Response . SetBody ( content )
}
p . Get ( reqPath , h )
p . Head ( reqPath , h )
}
/* */
// Party is just a group joiner of routes which have the same prefix and share same middleware(s) also.
// Party can also be named as 'Join' or 'Node' or 'Group' , Party chosen because it has more fun
func ( p * GardenParty ) Party ( path string , handlersFn ... HandlerFunc ) IParty {
middleware := ConvertToHandlers ( handlersFn )
if path [ 0 ] != SlashByte && strings . Contains ( path , "." ) {
2016-06-04 15:20:32 +02:00
//it's a domain so no handlers share (even the global ) or path, nothing.
if path [ 0 ] == MatchEverythingByte { // it's a dynamic subdomain
path = PrefixDynamicSubdomain
}
2016-05-30 16:08:09 +02:00
} else {
// set path to parent+child
path = absPath ( p . relativePath , path )
// append the parent's +child's handlers
middleware = JoinMiddleware ( p . middleware , middleware )
}
return & GardenParty { relativePath : path , station : p . station , middleware : middleware }
}
func absPath ( rootPath string , relativePath string ) ( absPath string ) {
if relativePath == "" {
absPath = rootPath
} else {
absPath = path . Join ( rootPath , relativePath )
}
return
}
// fixPath fix the double slashes, (because of root,I just do that before the .Handle no need for anything else special)
func fixPath ( str string ) string {
strafter := strings . Replace ( str , "//" , Slash , - 1 )
2016-06-06 16:24:14 +02:00
if strafter [ 0 ] == SlashByte && strings . Count ( strafter , "." ) >= 2 {
2016-05-30 16:08:09 +02:00
//it's domain, remove the first slash
strafter = strafter [ 1 : ]
}
return strafter
}