mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
Read https://github.com/kataras/iris/tree/master/HISTORY.md
This commit is contained in:
parent
2da67206c8
commit
d837381b16
153
HISTORY.md
153
HISTORY.md
|
@ -1,5 +1,158 @@
|
|||
# History
|
||||
|
||||
## 3.0.0-beta.3, 3.0.0-beta.4 -> 3.0.0-rc.1
|
||||
|
||||
This version took me many days because the whole framework's underline code is rewritten after many many many 'yoga'. Iris is not so small anymore, so I (tried) to organized it a little better. Note that, today, you can just go to [iris.go](https://github.com/kataras/iris/tree/master/iris.go) and [context.go](https://github.com/kataras/iris/tree/master/context/context.go) and look what functions you can use. You had some 'bugs' to subdomains, mail service, basic authentication and logger, these are fixed also, see below...
|
||||
|
||||
All [examples](https://github.com/iris-contrib/examples) are updated, and I tested them one by one.
|
||||
|
||||
|
||||
Many underline changes but the public API didn't changed much, of course this is relative to the way you use this framework, because that:
|
||||
|
||||
- Configuration changes: **0**
|
||||
|
||||
- iris.Iris pointer -> **iris.Framework** pointer
|
||||
|
||||
- iris.DefaultIris -> **iris.Default**
|
||||
- iris.Config() -> **iris.Config** is field now
|
||||
- iris.Websocket() -> **iris.Websocket** is field now
|
||||
- iris.Logger() -> **iris.Logger** is field now
|
||||
- iris.Plugins() -> **iris.Plugins** is field now
|
||||
- iris.Server() -> **iris.HTTPServer** is field now
|
||||
- iris.Rest() -> **REMOVED**
|
||||
|
||||
- iris.Mail() -> **REMOVED**
|
||||
- iris.Mail().Send() -> **iris.SendMail()**
|
||||
- iris.Templates() -> **REMOVED**
|
||||
- iris.Templates().RenderString() -> **iris.TemplateString()**
|
||||
|
||||
- iris.StaticHandlerFunc -> **iris.StaticHandler**
|
||||
- iris.URIOf() -> **iris.URL()**
|
||||
- iris.PathOf() -> **iris.Path()**
|
||||
|
||||
- context.RenderString() returned string,error -> **context.TemplateString() returns only string, which is empty on any parse error**
|
||||
- context.WriteHTML() -> **context.HTML()**
|
||||
- context.HTML() -> **context.RenderWithStatus()**
|
||||
|
||||
Entirely new
|
||||
|
||||
- -> **iris.ListenUNIX(addr string, socket os.Mode)**
|
||||
- -> **context.MustRender, same as Render but send response 500 and logs the error on parse error**
|
||||
- -> **context.Log(format string, a...interface{})**
|
||||
- -> **context.PostFormMulti(name string) []string**
|
||||
- -> **iris.Lookups() []Route**
|
||||
- -> **iris.Lookup(routeName string) Route**
|
||||
- -> **iris.Plugins.On(eventName string, ...func())** and fire all by **iris.Plugins.Call(eventName)**
|
||||
|
||||
- iris.Wildcard() **REMOVED**, subdomains and dynamic(wildcard) subdomains can only be registered with **iris.Party("mysubdomain.") && iris.Party("*.")**
|
||||
|
||||
|
||||
Semantic change for static subdomains
|
||||
|
||||
**1**
|
||||
|
||||
**BEFORE** :
|
||||
```go
|
||||
apiSubdomain := iris.Party("api.mydomain.com")
|
||||
{
|
||||
//....
|
||||
}
|
||||
iris.Listen("mydomain.com:80")
|
||||
```
|
||||
|
||||
|
||||
**NOW** just subdomain part, no need to duplicate ourselves:
|
||||
```go
|
||||
apiSubdomain := iris.Party("api.")
|
||||
{
|
||||
//....
|
||||
}
|
||||
iris.Listen("mydomain.com:80")
|
||||
```
|
||||
**2**
|
||||
|
||||
Before you couldn't set dynamic subdomains and normal subdomains at the same iris station, now you can.
|
||||
**NOW, this is possible**
|
||||
|
||||
```go
|
||||
/* admin.mydomain.com, and for other subdomains the Party(*.) */
|
||||
|
||||
admin := iris.Party("admin.")
|
||||
{
|
||||
// admin.mydomain.com
|
||||
admin.Get("/", func(c *iris.Context) {
|
||||
c.Write("INDEX FROM admin.mydomain.com")
|
||||
})
|
||||
// admin.mydomain.com/hey
|
||||
admin.Get("/hey", func(c *iris.Context) {
|
||||
c.Write("HEY FROM admin.mydomain.com/hey")
|
||||
})
|
||||
// admin.mydomain.com/hey2
|
||||
admin.Get("/hey2", func(c *iris.Context) {
|
||||
c.Write("HEY SECOND FROM admin.mydomain.com/hey")
|
||||
})
|
||||
}
|
||||
|
||||
// other.mydomain.com, otadsadsadsa.mydomain.com and so on....
|
||||
dynamicSubdomains := iris.Party("*.")
|
||||
{
|
||||
dynamicSubdomains.Get("/", dynamicSubdomainHandler)
|
||||
|
||||
dynamicSubdomains.Get("/something", dynamicSubdomainHandler)
|
||||
|
||||
dynamicSubdomains.Get("/something/:param1", dynamicSubdomainHandlerWithParam)
|
||||
}
|
||||
```
|
||||
|
||||
Minor change for listen
|
||||
|
||||
|
||||
**BEFORE you could just listen to a port**
|
||||
```go
|
||||
iris.Listen("8080")
|
||||
```
|
||||
**NOW you have set a HOSTNAME:PORT**
|
||||
```go
|
||||
iris.Listen(":8080")
|
||||
```
|
||||
|
||||
Relative issues/features: https://github.com/kataras/iris/issues/166 , https://github.com/kataras/iris/issues/176, https://github.com/kataras/iris/issues/183, https://github.com/kataras/iris/issues/184
|
||||
|
||||
|
||||
**Plugins**
|
||||
|
||||
PreHandle and PostHandle are removed, no need to use them anymore you can take routes by **iris.Lookups()**, but add support for custom event listeners by **iris.Plugins.On("event",func(){})** and fire all callbacks by **iris.Plugins.Call("event")** .
|
||||
|
||||
**FOR TESTERS**
|
||||
|
||||
**BEFORE** :
|
||||
```go
|
||||
api := iris.New()
|
||||
//...
|
||||
|
||||
api.PreListen(config.Server{ListeningAddr: ""})
|
||||
|
||||
e := httpexpect.WithConfig(httpexpect.Config{
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
Client: fasthttpexpect.NewBinder(api.ServeRequest),
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
**NOW**:
|
||||
|
||||
```go
|
||||
api := iris.New()
|
||||
//...
|
||||
|
||||
e := httpexpect.WithConfig(httpexpect.Config{
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
Client: fasthttpexpect.NewBinder(api.NoListen().Handler),
|
||||
})
|
||||
|
||||
|
||||
```
|
||||
|
||||
## 3.0.0-beta.2 -> 3.0.0-beta.3
|
||||
|
||||
- Complete the Jade Template Engine support, {{ render }} and {{ url }} done also.
|
||||
|
|
13
README.md
13
README.md
|
@ -6,7 +6,7 @@
|
|||
[Travis]: http://travis-ci.org/kataras/iris
|
||||
[License Widget]: https://img.shields.io/badge/license-Apache%20License%202.0-E91E63.svg?style=flat-square
|
||||
[License]: https://github.com/kataras/iris/blob/master/LICENSE
|
||||
[Release Widget]: https://img.shields.io/badge/release-v3.0.0--beta.4-blue.svg?style=flat-square
|
||||
[Release Widget]: https://img.shields.io/badge/release-v3.0.0--rc.1-blue.svg?style=flat-square
|
||||
[Release]: https://github.com/kataras/iris/releases
|
||||
[Gitter Widget]: https://img.shields.io/badge/chat-on%20gitter-00BCD4.svg?style=flat-square
|
||||
[Gitter]: https://gitter.im/kataras/iris
|
||||
|
@ -18,12 +18,7 @@
|
|||
[Language]: http://golang.org
|
||||
[Platform Widget]: https://img.shields.io/badge/platform-Any--OS-gray.svg?style=flat-square
|
||||
|
||||
The fastest backend web framework for Go, provides robust set of features.
|
||||
|
||||
```sh
|
||||
$ which -a motivation-counter
|
||||
Stars | https://github.com/kataras/iris/stargazers
|
||||
```
|
||||
the fastest backend web framework for Go, provides robust set of features.
|
||||
|
||||
[![Benchmark Wizzard Processing Time Horizontal Graph](https://raw.githubusercontent.com/iris-contrib/website/cf71811e6acb2f9bf1e715e25660392bf090b923/assets/benchmark_horizontal_transparent.png)](#benchmarks)
|
||||
|
||||
|
@ -83,7 +78,7 @@ Docs & Community
|
|||
|
||||
- Take a look at the [examples](https://github.com/iris-contrib/examples)
|
||||
|
||||
|
||||
- If for some reason your old code doesn't runs, view the [HISTORY](https://github.com//kataras/iris/tree/master/HISTORY.md)
|
||||
|
||||
|
||||
If you'd like to discuss this package, or ask questions about it, feel free to
|
||||
|
@ -123,7 +118,7 @@ Iris suggests you to use [this](https://github.com/gavv/httpexpect) new suite t
|
|||
Versioning
|
||||
------------
|
||||
|
||||
Current: **v3.0.0-beta.4**
|
||||
Current: **v3.0.0-rc.1**
|
||||
> Iris is an active project
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
package bindings
|
||||
|
||||
import "github.com/kataras/iris/errors"
|
||||
|
||||
var (
|
||||
// ErrNoForm returns an error with message: 'Request has no any valid form'
|
||||
ErrNoForm = errors.New("Request has no any valid form")
|
||||
// ErrWriteJSON returns an error with message: 'Before JSON be written to the body, JSON Encoder returned an error. Trace: +specific error'
|
||||
ErrWriteJSON = errors.New("Before JSON be written to the body, JSON Encoder returned an error. Trace: %s")
|
||||
// ErrRenderMarshalled returns an error with message: 'Before +type Rendering, MarshalIndent retured an error. Trace: +specific error'
|
||||
ErrRenderMarshalled = errors.New("Before +type Rendering, MarshalIndent returned an error. Trace: %s")
|
||||
// ErrReadBody returns an error with message: 'While trying to read +type from the request body. Trace +specific error'
|
||||
ErrReadBody = errors.New("While trying to read %s from the request body. Trace %s")
|
||||
)
|
420
bindings/form.go
420
bindings/form.go
|
@ -1,420 +0,0 @@
|
|||
// Package bindings source code from https://github.com/monoculum/formame.
|
||||
package bindings
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
const tagName = "form"
|
||||
|
||||
// A pathMap holds the values of a map with its key and values correspondent
|
||||
type pathMap struct {
|
||||
m reflect.Value
|
||||
|
||||
key string
|
||||
value reflect.Value
|
||||
|
||||
path string
|
||||
}
|
||||
|
||||
// a pathMaps holds the values for each key
|
||||
type pathMaps []*pathMap
|
||||
|
||||
// find find and get the value by the given key
|
||||
func (ma pathMaps) find(id reflect.Value, key string) *pathMap {
|
||||
for _, v := range ma {
|
||||
if v.m == id && v.key == key {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A decoder holds the values from form, the 'reflect' value of main struct
|
||||
// and the 'reflect' value of current path
|
||||
type decoder struct {
|
||||
main reflect.Value
|
||||
|
||||
curr reflect.Value
|
||||
value string
|
||||
values []string
|
||||
|
||||
path string
|
||||
field string
|
||||
index int
|
||||
|
||||
maps pathMaps
|
||||
}
|
||||
|
||||
// Decode decodes the url.Values into a element that must be a pointer to a type provided by argument
|
||||
func Decode(vs url.Values, dst interface{}) error {
|
||||
main := reflect.ValueOf(dst)
|
||||
if main.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf(tagName+": the value passed for decode is not a pointer but a %v", main.Kind())
|
||||
}
|
||||
|
||||
dec := &decoder{main: main.Elem()}
|
||||
|
||||
// iterate over the form's values and decode it
|
||||
for k, v := range vs {
|
||||
dec.path = k
|
||||
dec.field = k
|
||||
dec.values = v
|
||||
dec.value = v[0]
|
||||
if dec.value != "" {
|
||||
if err := dec.begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// set values of each maps
|
||||
for _, v := range dec.maps {
|
||||
key := v.m.Type().Key()
|
||||
switch key.Kind() {
|
||||
case reflect.String:
|
||||
// the key is a string
|
||||
v.m.SetMapIndex(reflect.ValueOf(v.key), v.value)
|
||||
default:
|
||||
// must to implement the TextUnmarshaler interface for to can to decode the map's key
|
||||
var val reflect.Value
|
||||
|
||||
if key.Kind() == reflect.Ptr {
|
||||
val = reflect.New(key.Elem())
|
||||
} else {
|
||||
val = reflect.New(key).Elem()
|
||||
}
|
||||
|
||||
dec.value = v.key
|
||||
if ok, err := dec.unmarshalText(val); !ok {
|
||||
return fmt.Errorf(tagName+": the key with %s type (%v) in the path %v should implements the TextUnmarshaler interface for to can decode it", key, v.m.Type(), v.path)
|
||||
} else if err != nil {
|
||||
return fmt.Errorf(tagName+": an error has occured in the UnmarshalText method for type %s: %s", key, err)
|
||||
}
|
||||
|
||||
v.m.SetMapIndex(val, v.value)
|
||||
}
|
||||
}
|
||||
|
||||
dec.maps = make(pathMaps, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// begin prepare the current path to walk through it
|
||||
func (dec *decoder) begin() (err error) {
|
||||
dec.curr = dec.main
|
||||
fields := strings.Split(dec.field, ".")
|
||||
for i, field := range fields {
|
||||
b := strings.IndexAny(field, "[")
|
||||
if b != -1 {
|
||||
// is a array
|
||||
e := strings.IndexAny(field, "]")
|
||||
if e == -1 {
|
||||
return errors.New(tagName + ": bad syntax array")
|
||||
}
|
||||
dec.field = field[:b]
|
||||
if dec.index, err = strconv.Atoi(field[b+1 : e]); err != nil {
|
||||
return errors.New(tagName + ": the index of array is not a number")
|
||||
}
|
||||
if len(fields) == i+1 {
|
||||
return dec.end()
|
||||
}
|
||||
if err = dec.walk(); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// not is a array
|
||||
dec.field = field
|
||||
dec.index = -1
|
||||
if len(fields) == i+1 {
|
||||
return dec.end()
|
||||
}
|
||||
if err = dec.walk(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// walk traverses the current path until to the last field
|
||||
func (dec *decoder) walk() error {
|
||||
// check if is a struct or map
|
||||
switch dec.curr.Kind() {
|
||||
case reflect.Struct:
|
||||
if err := dec.findStructField(); err != nil {
|
||||
return err
|
||||
}
|
||||
case reflect.Map:
|
||||
dec.currentMap()
|
||||
}
|
||||
// check if the struct or map is a interface
|
||||
if dec.curr.Kind() == reflect.Interface {
|
||||
dec.curr = dec.curr.Elem()
|
||||
}
|
||||
// check if the struct or map is a pointer
|
||||
if dec.curr.Kind() == reflect.Ptr {
|
||||
if dec.curr.IsNil() {
|
||||
dec.curr.Set(reflect.New(dec.curr.Type().Elem()))
|
||||
}
|
||||
dec.curr = dec.curr.Elem()
|
||||
}
|
||||
// finally, check if there are access to slice/array or not...
|
||||
if dec.index != -1 {
|
||||
switch dec.curr.Kind() {
|
||||
case reflect.Slice, reflect.Array:
|
||||
if dec.curr.Len() <= dec.index {
|
||||
dec.expandSlice(dec.index + 1)
|
||||
}
|
||||
dec.curr = dec.curr.Index(dec.index)
|
||||
default:
|
||||
return fmt.Errorf(tagName+": the field \"%v\" in path \"%v\" has a index for array but it is not", dec.field, dec.path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// end finds the last field for decode its value correspondent
|
||||
func (dec *decoder) end() error {
|
||||
if dec.curr.Kind() == reflect.Struct {
|
||||
if err := dec.findStructField(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if dec.value == "" {
|
||||
return nil
|
||||
}
|
||||
return dec.decode()
|
||||
}
|
||||
|
||||
// decode sets the value in the last field found by end function
|
||||
func (dec *decoder) decode() error {
|
||||
if ok, err := dec.unmarshalText(dec.curr); ok || err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch dec.curr.Kind() {
|
||||
case reflect.Map:
|
||||
dec.currentMap()
|
||||
return dec.decode()
|
||||
case reflect.Slice, reflect.Array:
|
||||
if dec.index == -1 {
|
||||
// not has index, so to decode all values in the slice/array
|
||||
dec.expandSlice(len(dec.values))
|
||||
tmp := dec.curr
|
||||
for i, v := range dec.values {
|
||||
dec.curr = tmp.Index(i)
|
||||
dec.value = v
|
||||
if err := dec.decode(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// has index, so to decode value by index indicated
|
||||
if dec.curr.Len() <= dec.index {
|
||||
dec.expandSlice(dec.index + 1)
|
||||
}
|
||||
dec.curr = dec.curr.Index(dec.index)
|
||||
return dec.decode()
|
||||
}
|
||||
case reflect.String:
|
||||
dec.curr.SetString(dec.value)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if num, err := strconv.ParseInt(dec.value, 10, 64); err != nil {
|
||||
return fmt.Errorf(tagName+": the value of field \"%v\" in path \"%v\" should be a valid signed integer number", dec.field, dec.path)
|
||||
} else {
|
||||
dec.curr.SetInt(num)
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
if num, err := strconv.ParseUint(dec.value, 10, 64); err != nil {
|
||||
return fmt.Errorf(tagName+": the value of field \"%v\" in path \"%v\" should be a valid unsigned integer number", dec.field, dec.path)
|
||||
} else {
|
||||
dec.curr.SetUint(num)
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if num, err := strconv.ParseFloat(dec.value, dec.curr.Type().Bits()); err != nil {
|
||||
return fmt.Errorf(tagName+": the value of field \"%v\" in path \"%v\" should be a valid float number", dec.field, dec.path)
|
||||
} else {
|
||||
dec.curr.SetFloat(num)
|
||||
}
|
||||
case reflect.Bool:
|
||||
switch dec.value {
|
||||
case "true", "on", "1":
|
||||
dec.curr.SetBool(true)
|
||||
case "false", "off", "0":
|
||||
dec.curr.SetBool(false)
|
||||
default:
|
||||
return fmt.Errorf(tagName+": the value of field \"%v\" in path \"%v\" is not a valid boolean", dec.field, dec.path)
|
||||
}
|
||||
case reflect.Interface:
|
||||
dec.curr.Set(reflect.ValueOf(dec.value))
|
||||
case reflect.Ptr:
|
||||
dec.curr.Set(reflect.New(dec.curr.Type().Elem()))
|
||||
dec.curr = dec.curr.Elem()
|
||||
return dec.decode()
|
||||
case reflect.Struct:
|
||||
switch dec.curr.Interface().(type) {
|
||||
case time.Time:
|
||||
t, err := time.Parse("2006-01-02", dec.value)
|
||||
if err != nil {
|
||||
return fmt.Errorf(tagName+": the value of field \"%v\" in path \"%v\" is not a valid datetime", dec.field, dec.path)
|
||||
}
|
||||
dec.curr.Set(reflect.ValueOf(t))
|
||||
case url.URL:
|
||||
u, err := url.Parse(dec.value)
|
||||
if err != nil {
|
||||
return fmt.Errorf(tagName+": the value of field \"%v\" in path \"%v\" is not a valid url", dec.field, dec.path)
|
||||
}
|
||||
dec.curr.Set(reflect.ValueOf(*u))
|
||||
default:
|
||||
return fmt.Errorf(tagName+": not supported type for field \"%v\" in path \"%v\"", dec.field, dec.path)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf(tagName+": not supported type for field \"%v\" in path \"%v\"", dec.field, dec.path)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// findField finds a field by its name, if it is not found,
|
||||
// then retry the search examining the tag "form" of every field of struct
|
||||
func (dec *decoder) findStructField() error {
|
||||
var anon reflect.Value
|
||||
|
||||
num := dec.curr.NumField()
|
||||
for i := 0; i < num; i++ {
|
||||
field := dec.curr.Type().Field(i)
|
||||
if field.Name == dec.field {
|
||||
// check if the field's name is equal
|
||||
dec.curr = dec.curr.Field(i)
|
||||
return nil
|
||||
} else if field.Anonymous {
|
||||
// if the field is a anonymous struct, then iterate over its fields
|
||||
tmp := dec.curr
|
||||
dec.curr = dec.curr.FieldByIndex(field.Index)
|
||||
if err := dec.findStructField(); err != nil {
|
||||
dec.curr = tmp
|
||||
continue
|
||||
}
|
||||
// field in anonymous struct is found,
|
||||
// but first it should found the field in the rest of struct
|
||||
// (a field with same name in the current struct should have preference over anonymous struct)
|
||||
anon = dec.curr
|
||||
dec.curr = tmp
|
||||
} else if dec.field == field.Tag.Get(tagName) {
|
||||
|
||||
dec.curr = dec.curr.Field(i)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if anon.IsValid() {
|
||||
dec.curr = anon
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf(tagName+": not found the field \"%v\" in the path \"%v\"", dec.field, dec.path)
|
||||
}
|
||||
|
||||
// expandSlice expands the length and capacity of the current slice
|
||||
func (dec *decoder) expandSlice(length int) {
|
||||
n := reflect.MakeSlice(dec.curr.Type(), length, length)
|
||||
reflect.Copy(n, dec.curr)
|
||||
dec.curr.Set(n)
|
||||
}
|
||||
|
||||
// currentMap gets in d.curr the map concrete for decode the current value
|
||||
func (dec *decoder) currentMap() {
|
||||
n := dec.curr.Type()
|
||||
if dec.curr.IsNil() {
|
||||
dec.curr.Set(reflect.MakeMap(n))
|
||||
m := reflect.New(n.Elem()).Elem()
|
||||
dec.maps = append(dec.maps, &pathMap{dec.curr, dec.field, m, dec.path})
|
||||
dec.curr = m
|
||||
} else if a := dec.maps.find(dec.curr, dec.field); a == nil {
|
||||
m := reflect.New(n.Elem()).Elem()
|
||||
dec.maps = append(dec.maps, &pathMap{dec.curr, dec.field, m, dec.path})
|
||||
dec.curr = m
|
||||
} else {
|
||||
dec.curr = a.value
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
timeType = reflect.TypeOf(time.Time{})
|
||||
timePType = reflect.TypeOf(&time.Time{})
|
||||
)
|
||||
|
||||
// unmarshalText returns a boolean and error. The boolean is true if the
|
||||
// value implements TextUnmarshaler, and false if not.
|
||||
func (dec *decoder) unmarshalText(v reflect.Value) (bool, error) {
|
||||
// skip if the type is time.Time
|
||||
n := v.Type()
|
||||
if n.ConvertibleTo(timeType) || n.ConvertibleTo(timePType) {
|
||||
return false, nil
|
||||
}
|
||||
// check if implements the interface
|
||||
m, ok := v.Interface().(encoding.TextUnmarshaler)
|
||||
addr := v.CanAddr()
|
||||
if !ok && !addr {
|
||||
return false, nil
|
||||
} else if addr {
|
||||
return dec.unmarshalText(v.Addr())
|
||||
}
|
||||
// return result
|
||||
err := m.UnmarshalText([]byte(dec.value))
|
||||
return true, err
|
||||
}
|
||||
|
||||
// BindForm binds the formObject with the form data
|
||||
// it supports any kind of struct
|
||||
func BindForm(ctx context.IContext, formObject interface{}) error {
|
||||
reqCtx := ctx.GetRequestCtx()
|
||||
// first check if we have multipart form
|
||||
multipartForm, err := reqCtx.MultipartForm()
|
||||
if err == nil {
|
||||
//we have multipart form
|
||||
return ErrReadBody.With(Decode(multipartForm.Value, formObject))
|
||||
}
|
||||
// if no multipart and post arguments ( means normal form)
|
||||
|
||||
if reqCtx.PostArgs().Len() == 0 && reqCtx.QueryArgs().Len() == 0 {
|
||||
return ErrReadBody.With(ErrNoForm.Return())
|
||||
}
|
||||
|
||||
form := make(map[string][]string, reqCtx.PostArgs().Len()+reqCtx.QueryArgs().Len())
|
||||
|
||||
reqCtx.PostArgs().VisitAll(func(k []byte, v []byte) {
|
||||
key := string(k)
|
||||
value := string(v)
|
||||
// for slices
|
||||
if form[key] != nil {
|
||||
form[key] = append(form[key], value)
|
||||
} else {
|
||||
form[key] = []string{value}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
reqCtx.QueryArgs().VisitAll(func(k []byte, v []byte) {
|
||||
key := string(k)
|
||||
value := string(v)
|
||||
// for slices
|
||||
if form[key] != nil {
|
||||
form[key] = append(form[key], value)
|
||||
} else {
|
||||
form[key] = []string{value}
|
||||
}
|
||||
})
|
||||
|
||||
return ErrReadBody.With(Decode(form, formObject))
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package bindings
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
// BindJSON reads JSON from request's body
|
||||
func BindJSON(ctx context.IContext, jsonObject interface{}) error {
|
||||
data := ctx.GetRequestCtx().Request.Body()
|
||||
|
||||
decoder := json.NewDecoder(strings.NewReader(string(data)))
|
||||
err := decoder.Decode(jsonObject)
|
||||
|
||||
//err != nil fix by @shiena
|
||||
if err != nil && err != io.EOF {
|
||||
return ErrReadBody.Format("JSON", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package bindings
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
// BindXML reads XML from request's body
|
||||
func BindXML(ctx context.IContext, xmlObject interface{}) error {
|
||||
data := ctx.GetRequestCtx().Request.Body()
|
||||
|
||||
decoder := xml.NewDecoder(strings.NewReader(string(data)))
|
||||
err := decoder.Decode(xmlObject)
|
||||
//err != nil fix by @shiena
|
||||
if err != nil && err != io.EOF {
|
||||
return ErrReadBody.Format("XML", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
457
branch.go
457
branch.go
|
@ -1,457 +0,0 @@
|
|||
// Copyright (c) 2013 Julien Schmidt, Copyright (c) 2016 Gerasimos Maropoulos,
|
||||
|
||||
package iris
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
isStatic BranchCase = iota
|
||||
isRoot
|
||||
hasParams
|
||||
matchEverything
|
||||
)
|
||||
|
||||
type (
|
||||
// PathParameter is a struct which contains Key and Value, used for named path parameters
|
||||
PathParameter struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
// PathParameters type for a slice of PathParameter
|
||||
// Tt's a slice of PathParameter type, because it's faster than map
|
||||
PathParameters []PathParameter
|
||||
|
||||
// BranchCase is the type which the type of Branch using in order to determinate what type (parameterized, anything, static...) is the perticular node
|
||||
BranchCase uint8
|
||||
|
||||
// IBranch is the interface which the type Branch must implement
|
||||
IBranch interface {
|
||||
AddBranch(string, Middleware)
|
||||
AddNode(uint8, string, string, Middleware)
|
||||
GetBranch(string, PathParameters) (Middleware, PathParameters, bool)
|
||||
GivePrecedenceTo(index int) int
|
||||
}
|
||||
|
||||
// Branch is the node of a tree of the routes,
|
||||
// in order to learn how this is working, google 'trie' or watch this lecture: https://www.youtube.com/watch?v=uhAUk63tLRM
|
||||
// this method is used by the BSD's kernel also
|
||||
Branch struct {
|
||||
part string
|
||||
BranchCase BranchCase
|
||||
hasWildNode bool
|
||||
tokens string
|
||||
nodes []*Branch
|
||||
middleware Middleware
|
||||
precedence uint64
|
||||
paramsLen uint8
|
||||
}
|
||||
)
|
||||
|
||||
var _ IBranch = &Branch{}
|
||||
|
||||
// Get returns a value from a key inside this Parameters
|
||||
// If no parameter with this key given then it returns an empty string
|
||||
func (params PathParameters) Get(key string) string {
|
||||
for _, p := range params {
|
||||
if p.Key == key {
|
||||
return p.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// String returns a string implementation of all parameters that this PathParameters object keeps
|
||||
// hasthe form of key1=value1,key2=value2...
|
||||
func (params PathParameters) String() string {
|
||||
var buff bytes.Buffer
|
||||
for i := range params {
|
||||
buff.WriteString(params[i].Key)
|
||||
buff.WriteString("=")
|
||||
buff.WriteString(params[i].Value)
|
||||
if i < len(params)-1 {
|
||||
buff.WriteString(",")
|
||||
}
|
||||
|
||||
}
|
||||
return buff.String()
|
||||
}
|
||||
|
||||
// ParseParams receives a string and returns PathParameters (slice of PathParameter)
|
||||
// received string must have this form: key1=value1,key2=value2...
|
||||
func ParseParams(str string) PathParameters {
|
||||
_paramsstr := strings.Split(str, ",")
|
||||
if len(_paramsstr) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
params := make(PathParameters, 0) // PathParameters{}
|
||||
|
||||
// for i := 0; i < len(_paramsstr); i++ {
|
||||
for i := range _paramsstr {
|
||||
idxOfEq := strings.IndexRune(_paramsstr[i], '=')
|
||||
if idxOfEq == -1 {
|
||||
//error
|
||||
return nil
|
||||
}
|
||||
|
||||
key := _paramsstr[i][:idxOfEq]
|
||||
val := _paramsstr[i][idxOfEq+1:]
|
||||
params = append(params, PathParameter{key, val})
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
// GetParamsLen returns the parameters length from a given path
|
||||
func GetParamsLen(path string) uint8 {
|
||||
var n uint
|
||||
for i := 0; i < len(path); i++ {
|
||||
if path[i] != ':' && path[i] != '*' { // ParameterStartByte & MatchEverythingByte
|
||||
continue
|
||||
}
|
||||
n++
|
||||
}
|
||||
if n >= 255 {
|
||||
return 255
|
||||
}
|
||||
return uint8(n)
|
||||
}
|
||||
|
||||
// AddBranch adds a branch to the existing branch or to the tree if no branch has the prefix of
|
||||
func (b *Branch) AddBranch(path string, middleware Middleware) {
|
||||
fullPath := path
|
||||
b.precedence++
|
||||
numParams := GetParamsLen(path)
|
||||
|
||||
if len(b.part) > 0 || len(b.nodes) > 0 {
|
||||
loop:
|
||||
for {
|
||||
if numParams > b.paramsLen {
|
||||
b.paramsLen = numParams
|
||||
}
|
||||
|
||||
i := 0
|
||||
max := utils.FindLower(len(path), len(b.part))
|
||||
for i < max && path[i] == b.part[i] {
|
||||
i++
|
||||
}
|
||||
|
||||
if i < len(b.part) {
|
||||
node := Branch{
|
||||
part: b.part[i:],
|
||||
hasWildNode: b.hasWildNode,
|
||||
tokens: b.tokens,
|
||||
nodes: b.nodes,
|
||||
middleware: b.middleware,
|
||||
precedence: b.precedence - 1,
|
||||
}
|
||||
|
||||
for i := range node.nodes {
|
||||
if node.nodes[i].paramsLen > node.paramsLen {
|
||||
node.paramsLen = node.nodes[i].paramsLen
|
||||
}
|
||||
}
|
||||
|
||||
b.nodes = []*Branch{&node}
|
||||
b.tokens = string([]byte{b.part[i]})
|
||||
b.part = path[:i]
|
||||
b.middleware = nil
|
||||
b.hasWildNode = false
|
||||
}
|
||||
|
||||
if i < len(path) {
|
||||
path = path[i:]
|
||||
|
||||
if b.hasWildNode {
|
||||
b = b.nodes[0]
|
||||
b.precedence++
|
||||
|
||||
if numParams > b.paramsLen {
|
||||
b.paramsLen = numParams
|
||||
}
|
||||
numParams--
|
||||
|
||||
if len(path) >= len(b.part) && b.part == path[:len(b.part)] {
|
||||
|
||||
if len(b.part) >= len(path) || path[len(b.part)] == '/' {
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
c := path[0]
|
||||
|
||||
if b.BranchCase == hasParams && c == '/' && len(b.nodes) == 1 {
|
||||
b = b.nodes[0]
|
||||
b.precedence++
|
||||
continue loop
|
||||
}
|
||||
//we need the i here to be re-setting, so use the same i variable as we declare it on line 176
|
||||
for i := range b.tokens {
|
||||
if c == b.tokens[i] {
|
||||
i = b.GivePrecedenceTo(i)
|
||||
b = b.nodes[i]
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
|
||||
if c != ParameterStartByte && c != MatchEverythingByte {
|
||||
|
||||
b.tokens += string([]byte{c})
|
||||
node := &Branch{
|
||||
paramsLen: numParams,
|
||||
}
|
||||
b.nodes = append(b.nodes, node)
|
||||
b.GivePrecedenceTo(len(b.tokens) - 1)
|
||||
b = node
|
||||
}
|
||||
b.AddNode(numParams, path, fullPath, middleware)
|
||||
return
|
||||
|
||||
} else if i == len(path) {
|
||||
if b.middleware != nil {
|
||||
return
|
||||
}
|
||||
b.middleware = middleware
|
||||
}
|
||||
return
|
||||
}
|
||||
} else {
|
||||
b.AddNode(numParams, path, fullPath, middleware)
|
||||
b.BranchCase = isRoot
|
||||
}
|
||||
}
|
||||
|
||||
// AddNode adds a branch as children to other Branch
|
||||
func (b *Branch) AddNode(numParams uint8, path string, fullPath string, middleware Middleware) {
|
||||
var offset int
|
||||
|
||||
for i, max := 0, len(path); numParams > 0; i++ {
|
||||
c := path[i]
|
||||
if c != ParameterStartByte && c != MatchEverythingByte {
|
||||
continue
|
||||
}
|
||||
|
||||
end := i + 1
|
||||
for end < max && path[end] != '/' {
|
||||
switch path[end] {
|
||||
case ParameterStartByte, MatchEverythingByte:
|
||||
|
||||
default:
|
||||
end++
|
||||
}
|
||||
}
|
||||
|
||||
if len(b.nodes) > 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if end-i < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
if c == ParameterStartByte {
|
||||
|
||||
if i > 0 {
|
||||
b.part = path[offset:i]
|
||||
offset = i
|
||||
}
|
||||
|
||||
child := &Branch{
|
||||
BranchCase: hasParams,
|
||||
paramsLen: numParams,
|
||||
}
|
||||
b.nodes = []*Branch{child}
|
||||
b.hasWildNode = true
|
||||
b = child
|
||||
b.precedence++
|
||||
numParams--
|
||||
|
||||
if end < max {
|
||||
b.part = path[offset:end]
|
||||
offset = end
|
||||
|
||||
child := &Branch{
|
||||
paramsLen: numParams,
|
||||
precedence: 1,
|
||||
}
|
||||
b.nodes = []*Branch{child}
|
||||
b = child
|
||||
}
|
||||
|
||||
} else {
|
||||
if end != max || numParams > 1 {
|
||||
return
|
||||
}
|
||||
|
||||
if len(b.part) > 0 && b.part[len(b.part)-1] == '/' {
|
||||
return
|
||||
}
|
||||
|
||||
i--
|
||||
if path[i] != '/' {
|
||||
return
|
||||
}
|
||||
|
||||
b.part = path[offset:i]
|
||||
|
||||
child := &Branch{
|
||||
hasWildNode: true,
|
||||
BranchCase: matchEverything,
|
||||
paramsLen: 1,
|
||||
}
|
||||
b.nodes = []*Branch{child}
|
||||
b.tokens = string(path[i])
|
||||
b = child
|
||||
b.precedence++
|
||||
|
||||
child = &Branch{
|
||||
part: path[i:],
|
||||
BranchCase: matchEverything,
|
||||
paramsLen: 1,
|
||||
middleware: middleware,
|
||||
precedence: 1,
|
||||
}
|
||||
b.nodes = []*Branch{child}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
b.part = path[offset:]
|
||||
b.middleware = middleware
|
||||
}
|
||||
|
||||
// GetBranch is used by the Router, it finds and returns the correct branch for a path
|
||||
func (b *Branch) GetBranch(path string, _params PathParameters) (middleware Middleware, params PathParameters, mustRedirect bool) {
|
||||
params = _params
|
||||
loop:
|
||||
for {
|
||||
if len(path) > len(b.part) {
|
||||
if path[:len(b.part)] == b.part {
|
||||
path = path[len(b.part):]
|
||||
|
||||
if !b.hasWildNode {
|
||||
c := path[0]
|
||||
for i := range b.tokens {
|
||||
if c == b.tokens[i] {
|
||||
b = b.nodes[i]
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
|
||||
mustRedirect = (path == Slash && b.middleware != nil)
|
||||
return
|
||||
}
|
||||
|
||||
b = b.nodes[0]
|
||||
switch b.BranchCase {
|
||||
case hasParams:
|
||||
|
||||
end := 0
|
||||
for end < len(path) && path[end] != '/' {
|
||||
end++
|
||||
}
|
||||
|
||||
if cap(params) < int(b.paramsLen) {
|
||||
params = make(PathParameters, 0, b.paramsLen)
|
||||
}
|
||||
i := len(params)
|
||||
params = params[:i+1]
|
||||
params[i].Key = b.part[1:]
|
||||
params[i].Value = path[:end]
|
||||
|
||||
if end < len(path) {
|
||||
if len(b.nodes) > 0 {
|
||||
path = path[end:]
|
||||
b = b.nodes[0]
|
||||
continue loop
|
||||
}
|
||||
|
||||
mustRedirect = (len(path) == end+1)
|
||||
return
|
||||
}
|
||||
|
||||
if middleware = b.middleware; middleware != nil {
|
||||
return
|
||||
} else if len(b.nodes) == 1 {
|
||||
b = b.nodes[0]
|
||||
mustRedirect = (b.part == Slash && b.middleware != nil)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
case matchEverything:
|
||||
if cap(params) < int(b.paramsLen) {
|
||||
params = make(PathParameters, 0, b.paramsLen)
|
||||
}
|
||||
i := len(params)
|
||||
params = params[:i+1]
|
||||
params[i].Key = b.part[2:]
|
||||
params[i].Value = path
|
||||
|
||||
middleware = b.middleware
|
||||
return
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if path == b.part {
|
||||
if middleware = b.middleware; middleware != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if path == Slash && b.hasWildNode && b.BranchCase != isRoot {
|
||||
mustRedirect = true
|
||||
return
|
||||
}
|
||||
|
||||
for i := range b.tokens {
|
||||
if b.tokens[i] == '/' {
|
||||
b = b.nodes[i]
|
||||
mustRedirect = (len(b.part) == 1 && b.middleware != nil) ||
|
||||
(b.BranchCase == matchEverything && b.nodes[0].middleware != nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
mustRedirect = (path == Slash) ||
|
||||
(len(b.part) == len(path)+1 && b.part[len(path)] == '/' &&
|
||||
path == b.part[:len(b.part)-1] && b.middleware != nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// GivePrecedenceTo just adds the priority of this branch by an index
|
||||
func (b *Branch) GivePrecedenceTo(index int) int {
|
||||
b.nodes[index].precedence++
|
||||
_precedence := b.nodes[index].precedence
|
||||
|
||||
newindex := index
|
||||
for newindex > 0 && b.nodes[newindex-1].precedence < _precedence {
|
||||
tmpN := b.nodes[newindex-1]
|
||||
b.nodes[newindex-1] = b.nodes[newindex]
|
||||
b.nodes[newindex] = tmpN
|
||||
|
||||
newindex--
|
||||
}
|
||||
|
||||
if newindex != index {
|
||||
b.tokens = b.tokens[:newindex] +
|
||||
b.tokens[index:index+1] +
|
||||
b.tokens[newindex:index] + b.tokens[index+1:]
|
||||
}
|
||||
|
||||
return newindex
|
||||
}
|
|
@ -4,21 +4,24 @@ import (
|
|||
"github.com/imdario/mergo"
|
||||
)
|
||||
|
||||
// DefaultProfilePath is the default profile(http debug) path which is /debug/pprof
|
||||
const DefaultProfilePath = "/debug/pprof"
|
||||
// Default values for base Iris conf
|
||||
const (
|
||||
DefaultDisablePathCorrection = false
|
||||
DefaultDisablePathEscape = false
|
||||
)
|
||||
|
||||
type (
|
||||
// Iris configs for the station
|
||||
// All fields can be changed before server's listen except the DisablePathCorrection field
|
||||
//
|
||||
// MaxRequestBodySize is the only options that can be changed after server listen -
|
||||
// using Config().MaxRequestBodySize = ...
|
||||
// using Config.MaxRequestBodySize = ...
|
||||
// Render's rest config can be changed after declaration but before server's listen -
|
||||
// using Config().Render.Rest...
|
||||
// using Config.Render.Rest...
|
||||
// Render's Template config can be changed after declaration but before server's listen -
|
||||
// using Config().Render.Template...
|
||||
// using Config.Render.Template...
|
||||
// Sessions config can be changed after declaration but before server's listen -
|
||||
// using Config().Sessions...
|
||||
// using Config.Sessions...
|
||||
// and so on...
|
||||
Iris struct {
|
||||
|
||||
|
@ -56,40 +59,57 @@ type (
|
|||
// By default request body size is -1, unlimited.
|
||||
MaxRequestBodySize int64
|
||||
|
||||
// Profile set to true to enable web pprof (debug profiling)
|
||||
// Default is false, enabling makes available these 7 routes:
|
||||
// /debug/pprof/cmdline
|
||||
// /debug/pprof/profile
|
||||
// /debug/pprof/symbol
|
||||
// /debug/pprof/goroutine
|
||||
// /debug/pprof/heap
|
||||
// /debug/pprof/threadcreate
|
||||
// /debug/pprof/pprof/block
|
||||
Profile bool
|
||||
|
||||
// ProfilePath change it if you want other url path than the default
|
||||
// Default is /debug/pprof , which means yourhost.com/debug/pprof
|
||||
// ProfilePath a the route path, set it to enable http pprof tool
|
||||
// Default is empty, if you set it to a $path, these routes will handled:
|
||||
// $path/cmdline
|
||||
// $path/profile
|
||||
// $path/symbol
|
||||
// $path/goroutine
|
||||
// $path/heap
|
||||
// $path/threadcreate
|
||||
// $path/pprof/block
|
||||
// for example if '/debug/pprof'
|
||||
// http://yourdomain:PORT/debug/pprof/
|
||||
// http://yourdomain:PORT/debug/pprof/cmdline
|
||||
// http://yourdomain:PORT/debug/pprof/profile
|
||||
// http://yourdomain:PORT/debug/pprof/symbol
|
||||
// http://yourdomain:PORT/debug/pprof/goroutine
|
||||
// http://yourdomain:PORT/debug/pprof/heap
|
||||
// http://yourdomain:PORT/debug/pprof/threadcreate
|
||||
// http://yourdomain:PORT/debug/pprof/pprof/block
|
||||
// it can be a subdomain also, for example, if 'debug.'
|
||||
// http://debug.yourdomain:PORT/
|
||||
// http://debug.yourdomain:PORT/cmdline
|
||||
// http://debug.yourdomain:PORT/profile
|
||||
// http://debug.yourdomain:PORT/symbol
|
||||
// http://debug.yourdomain:PORT/goroutine
|
||||
// http://debug.yourdomain:PORT/heap
|
||||
// http://debug.yourdomain:PORT/threadcreate
|
||||
// http://debug.yourdomain:PORT/pprof/block
|
||||
ProfilePath string
|
||||
|
||||
// Logger the configuration for the logger
|
||||
// Iris logs ONLY errors and the banner if enabled
|
||||
// Iris logs ONLY SEMANTIC errors and the banner if enabled
|
||||
Logger Logger
|
||||
|
||||
// Sessions the config for sessions
|
||||
// contains 3(three) properties
|
||||
// Provider: (look /sessions/providers)
|
||||
// Secret: cookie's name (string)
|
||||
// Life: cookie life (time.Duration)
|
||||
// Sessions contains the configs for sessions
|
||||
Sessions Sessions
|
||||
|
||||
// Render contains the configs for template and rest configuration
|
||||
Render Render
|
||||
|
||||
// Websocket contains the configs for Websocket's server integration
|
||||
Websocket Websocket
|
||||
Websocket *Websocket
|
||||
|
||||
// Mail contains the config for the mail sender service
|
||||
// Mail contains the configs for the mail sender service
|
||||
Mail Mail
|
||||
|
||||
// Server contains the configs for the http server
|
||||
// Server configs are the only one which are setted inside base Iris package (from Listen, ListenTLS, ListenUNIX) NO from users
|
||||
//
|
||||
// this field is useful only when you need to READ which is the server's address, certfile & keyfile or unix's mode.
|
||||
//
|
||||
Server Server
|
||||
}
|
||||
|
||||
// Render struct keeps organise all configuration about rendering, templates and rest currently.
|
||||
|
@ -117,16 +137,17 @@ func DefaultRender() Render {
|
|||
// Default returns the default configuration for the Iris staton
|
||||
func Default() Iris {
|
||||
return Iris{
|
||||
DisablePathCorrection: false,
|
||||
DisablePathEscape: false,
|
||||
DisablePathCorrection: DefaultDisablePathCorrection,
|
||||
DisablePathEscape: DefaultDisablePathEscape,
|
||||
DisableBanner: false,
|
||||
MaxRequestBodySize: -1,
|
||||
ProfilePath: DefaultProfilePath,
|
||||
ProfilePath: "",
|
||||
Logger: DefaultLogger(),
|
||||
Sessions: DefaultSessions(),
|
||||
Render: DefaultRender(),
|
||||
Websocket: DefaultWebsocket(),
|
||||
Mail: DefaultMail(),
|
||||
Server: DefaultServer(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
// DefaultServerAddr the default server addr
|
||||
DefaultServerAddr = ":8080"
|
||||
// DefaultServerAddr the default server addr which is: 127.0.0.1:8080
|
||||
DefaultServerAddr = "127.0.0.1:8080"
|
||||
)
|
||||
|
||||
// ServerName the response header of the 'Server' value when writes to the client
|
||||
|
|
|
@ -43,8 +43,8 @@ type Websocket struct {
|
|||
}
|
||||
|
||||
// DefaultWebsocket returns the default config for iris-ws websocket package
|
||||
func DefaultWebsocket() Websocket {
|
||||
return Websocket{
|
||||
func DefaultWebsocket() *Websocket {
|
||||
return &Websocket{
|
||||
WriteTimeout: DefaultWriteTimeout,
|
||||
PongTimeout: DefaultPongTimeout,
|
||||
PingPeriod: DefaultPingPeriod,
|
||||
|
@ -55,11 +55,11 @@ func DefaultWebsocket() Websocket {
|
|||
}
|
||||
|
||||
// Merge merges the default with the given config and returns the result
|
||||
func (c Websocket) Merge(cfg []Websocket) (config Websocket) {
|
||||
func (c *Websocket) Merge(cfg []*Websocket) (config *Websocket) {
|
||||
|
||||
if len(cfg) > 0 {
|
||||
config = cfg[0]
|
||||
mergo.Merge(&config, c)
|
||||
mergo.Merge(config, c)
|
||||
} else {
|
||||
_default := c
|
||||
config = _default
|
||||
|
@ -69,10 +69,10 @@ func (c Websocket) Merge(cfg []Websocket) (config Websocket) {
|
|||
}
|
||||
|
||||
// MergeSingle merges the default with the given config and returns the result
|
||||
func (c Websocket) MergeSingle(cfg Websocket) (config Websocket) {
|
||||
func (c *Websocket) MergeSingle(cfg *Websocket) (config *Websocket) {
|
||||
|
||||
config = cfg
|
||||
mergo.Merge(&config, c)
|
||||
mergo.Merge(config, c)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
676
context.go
676
context.go
|
@ -6,40 +6,72 @@ files: context_renderer.go, context_storage.go, context_request.go, context_resp
|
|||
package iris
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/iris-contrib/formBinder"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/context"
|
||||
"github.com/kataras/iris/errors"
|
||||
"github.com/kataras/iris/sessions/store"
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/klauspost/compress/gzip"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultUserAgent default to 'iris' but it is not used anywhere yet
|
||||
DefaultUserAgent = "iris"
|
||||
defaultUserAgent = "iris"
|
||||
// ContentType represents the header["Content-Type"]
|
||||
ContentType = "Content-Type"
|
||||
contentType = "Content-Type"
|
||||
// ContentLength represents the header["Content-Length"]
|
||||
ContentLength = "Content-Length"
|
||||
contentLength = "Content-Length"
|
||||
// ContentHTML is the string of text/html response headers
|
||||
ContentHTML = "text/html"
|
||||
contentHTML = "text/html"
|
||||
// ContentBINARY is the string of application/octet-stream response headers
|
||||
ContentBINARY = "application/octet-stream"
|
||||
contentBINARY = "application/octet-stream"
|
||||
|
||||
// LastModified "Last-Modified"
|
||||
LastModified = "Last-Modified"
|
||||
lastModified = "Last-Modified"
|
||||
// IfModifiedSince "If-Modified-Since"
|
||||
IfModifiedSince = "If-Modified-Since"
|
||||
ifModifiedSince = "If-Modified-Since"
|
||||
// ContentDisposition "Content-Disposition"
|
||||
ContentDisposition = "Content-Disposition"
|
||||
|
||||
// TimeFormat default time format for any kind of datetime parsing
|
||||
TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT"
|
||||
contentDisposition = "Content-Disposition"
|
||||
|
||||
// stopExecutionPosition used inside the Context, is the number which shows us that the context's middleware manualy stop the execution
|
||||
stopExecutionPosition = 255
|
||||
)
|
||||
|
||||
// this pool is used everywhere needed in the iris for example inside party-> Static
|
||||
var gzipWriterPool = sync.Pool{New: func() interface{} { return &gzip.Writer{} }}
|
||||
|
||||
// errors
|
||||
|
||||
var (
|
||||
errTemplateExecute = errors.New("Unable to execute a template. Trace: %s")
|
||||
errFlashNotFound = errors.New("Unable to get flash message. Trace: Cookie does not exists")
|
||||
errSessionNil = errors.New("Unable to set session, Config().Session.Provider is nil, please refer to the docs!")
|
||||
errNoForm = errors.New("Request has no any valid form")
|
||||
errWriteJSON = errors.New("Before JSON be written to the body, JSON Encoder returned an error. Trace: %s")
|
||||
errRenderMarshalled = errors.New("Before +type Rendering, MarshalIndent returned an error. Trace: %s")
|
||||
errReadBody = errors.New("While trying to read %s from the request body. Trace %s")
|
||||
errServeContent = errors.New("While trying to serve content to the client. Trace %s")
|
||||
)
|
||||
|
||||
type (
|
||||
// Map is just a conversion for a map[string]interface{}
|
||||
Map map[string]interface{}
|
||||
|
@ -47,8 +79,8 @@ type (
|
|||
// it is not good practice to use this object in goroutines, for these cases use the .Clone()
|
||||
Context struct {
|
||||
*fasthttp.RequestCtx
|
||||
Params PathParameters
|
||||
station *Iris
|
||||
Params PathParameters
|
||||
framework *Framework
|
||||
//keep track all registed middleware (handlers)
|
||||
middleware Middleware
|
||||
sessionStore store.IStore
|
||||
|
@ -128,3 +160,621 @@ func (ctx *Context) IsStopped() bool {
|
|||
func (ctx *Context) GetHandlerName() string {
|
||||
return runtime.FuncForPC(reflect.ValueOf(ctx.middleware[len(ctx.middleware)-1]).Pointer()).Name()
|
||||
}
|
||||
|
||||
/* Request */
|
||||
|
||||
// Param returns the string representation of the key's path named parameter's value
|
||||
func (ctx *Context) Param(key string) string {
|
||||
return ctx.Params.Get(key)
|
||||
}
|
||||
|
||||
// ParamInt returns the int representation of the key's path named parameter's value
|
||||
func (ctx *Context) ParamInt(key string) (int, error) {
|
||||
val, err := strconv.Atoi(ctx.Param(key))
|
||||
return val, err
|
||||
}
|
||||
|
||||
// URLParam returns the get parameter from a request , if any
|
||||
func (ctx *Context) URLParam(key string) string {
|
||||
return string(ctx.RequestCtx.Request.URI().QueryArgs().Peek(key))
|
||||
}
|
||||
|
||||
// URLParams returns a map of a list of each url(query) parameter
|
||||
func (ctx *Context) URLParams() map[string]string {
|
||||
urlparams := make(map[string]string)
|
||||
ctx.RequestCtx.Request.URI().QueryArgs().VisitAll(func(key, value []byte) {
|
||||
urlparams[string(key)] = string(value)
|
||||
})
|
||||
return urlparams
|
||||
}
|
||||
|
||||
// URLParamInt returns the get parameter int value from a request , if any
|
||||
func (ctx *Context) URLParamInt(key string) (int, error) {
|
||||
return strconv.Atoi(ctx.URLParam(key))
|
||||
}
|
||||
|
||||
// MethodString returns the HTTP Method
|
||||
func (ctx *Context) MethodString() string {
|
||||
return utils.BytesToString(ctx.Method())
|
||||
}
|
||||
|
||||
// HostString returns the Host of the request( the url as string )
|
||||
func (ctx *Context) HostString() string {
|
||||
return utils.BytesToString(ctx.Host())
|
||||
}
|
||||
|
||||
// VirtualHostname returns the hostname that user registers, host path maybe differs from the real which is HostString, which taken from a net.listener
|
||||
func (ctx *Context) VirtualHostname() string {
|
||||
realhost := ctx.HostString()
|
||||
virtualhost := ctx.framework.HTTPServer.VirtualHostname()
|
||||
hostname := strings.Replace(realhost, "127.0.0.1", virtualhost, 1)
|
||||
hostname = strings.Replace(realhost, "localhost", virtualhost, 1)
|
||||
if portIdx := strings.IndexByte(hostname, ':'); portIdx > 0 {
|
||||
hostname = hostname[0:portIdx]
|
||||
}
|
||||
return hostname
|
||||
}
|
||||
|
||||
// PathString returns the full escaped path as string
|
||||
// for unescaped use: ctx.RequestCtx.RequestURI() or RequestPath(escape bool)
|
||||
func (ctx *Context) PathString() string {
|
||||
return ctx.RequestPath(true)
|
||||
}
|
||||
|
||||
// RequestPath returns the requested path
|
||||
func (ctx *Context) RequestPath(escape bool) string {
|
||||
if escape {
|
||||
return utils.BytesToString(ctx.RequestCtx.Path())
|
||||
}
|
||||
return utils.BytesToString(ctx.RequestCtx.RequestURI())
|
||||
}
|
||||
|
||||
// RequestIP gets just the Remote Address from the client.
|
||||
func (ctx *Context) RequestIP() string {
|
||||
if ip, _, err := net.SplitHostPort(strings.TrimSpace(ctx.RequestCtx.RemoteAddr().String())); err == nil {
|
||||
return ip
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// RemoteAddr is like RequestIP but it checks for proxy servers also, tries to get the real client's request IP
|
||||
func (ctx *Context) RemoteAddr() string {
|
||||
header := string(ctx.RequestCtx.Request.Header.Peek("X-Real-Ip"))
|
||||
realIP := strings.TrimSpace(header)
|
||||
if realIP != "" {
|
||||
return realIP
|
||||
}
|
||||
realIP = string(ctx.RequestCtx.Request.Header.Peek("X-Forwarded-For"))
|
||||
idx := strings.IndexByte(realIP, ',')
|
||||
if idx >= 0 {
|
||||
realIP = realIP[0:idx]
|
||||
}
|
||||
realIP = strings.TrimSpace(realIP)
|
||||
if realIP != "" {
|
||||
return realIP
|
||||
}
|
||||
return ctx.RequestIP()
|
||||
|
||||
}
|
||||
|
||||
// RequestHeader returns the request header's value
|
||||
// accepts one parameter, the key of the header (string)
|
||||
// returns string
|
||||
func (ctx *Context) RequestHeader(k string) string {
|
||||
return utils.BytesToString(ctx.RequestCtx.Request.Header.Peek(k))
|
||||
}
|
||||
|
||||
// PostFormValue returns a single value from post request's data
|
||||
func (ctx *Context) PostFormValue(name string) string {
|
||||
return string(ctx.RequestCtx.PostArgs().Peek(name))
|
||||
}
|
||||
|
||||
// PostFormMulti returns a slice of string from post request's data
|
||||
func (ctx *Context) PostFormMulti(name string) []string {
|
||||
arrBytes := ctx.PostArgs().PeekMulti(name)
|
||||
arrStr := make([]string, len(arrBytes))
|
||||
for i, v := range arrBytes {
|
||||
arrStr[i] = string(v)
|
||||
}
|
||||
return arrStr
|
||||
}
|
||||
|
||||
// Subdomain returns the subdomain (string) of this request, if any
|
||||
func (ctx *Context) Subdomain() (subdomain string) {
|
||||
host := ctx.HostString()
|
||||
if index := strings.IndexByte(host, '.'); index > 0 {
|
||||
subdomain = host[0:index]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// URLEncode returns the path encoded as url
|
||||
// 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
|
||||
/* Credits to Manish Singh @kryptodev for URLEncode */
|
||||
func URLEncode(path string) string {
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
u := fasthttp.AcquireURI()
|
||||
u.SetPath(path)
|
||||
encodedPath := u.String()[8:]
|
||||
fasthttp.ReleaseURI(u)
|
||||
return encodedPath
|
||||
}
|
||||
|
||||
// ReadJSON reads JSON from request's body
|
||||
func (ctx *Context) ReadJSON(jsonObject interface{}) error {
|
||||
data := ctx.RequestCtx.Request.Body()
|
||||
|
||||
decoder := json.NewDecoder(strings.NewReader(string(data)))
|
||||
err := decoder.Decode(jsonObject)
|
||||
|
||||
//err != nil fix by @shiena
|
||||
if err != nil && err != io.EOF {
|
||||
return errReadBody.Format("JSON", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadXML reads XML from request's body
|
||||
func (ctx *Context) ReadXML(xmlObject interface{}) error {
|
||||
data := ctx.RequestCtx.Request.Body()
|
||||
|
||||
decoder := xml.NewDecoder(strings.NewReader(string(data)))
|
||||
err := decoder.Decode(xmlObject)
|
||||
//err != nil fix by @shiena
|
||||
if err != nil && err != io.EOF {
|
||||
return errReadBody.Format("XML", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadForm binds the formObject with the form data
|
||||
// it supports any kind of struct
|
||||
func (ctx *Context) ReadForm(formObject interface{}) error {
|
||||
reqCtx := ctx.RequestCtx
|
||||
// first check if we have multipart form
|
||||
multipartForm, err := reqCtx.MultipartForm()
|
||||
if err == nil {
|
||||
//we have multipart form
|
||||
return errReadBody.With(formBinder.Decode(multipartForm.Value, formObject))
|
||||
}
|
||||
// if no multipart and post arguments ( means normal form)
|
||||
|
||||
if reqCtx.PostArgs().Len() == 0 && reqCtx.QueryArgs().Len() == 0 {
|
||||
return errReadBody.With(errNoForm.Return())
|
||||
}
|
||||
|
||||
form := make(map[string][]string, reqCtx.PostArgs().Len()+reqCtx.QueryArgs().Len())
|
||||
|
||||
reqCtx.PostArgs().VisitAll(func(k []byte, v []byte) {
|
||||
key := string(k)
|
||||
value := string(v)
|
||||
// for slices
|
||||
if form[key] != nil {
|
||||
form[key] = append(form[key], value)
|
||||
} else {
|
||||
form[key] = []string{value}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
reqCtx.QueryArgs().VisitAll(func(k []byte, v []byte) {
|
||||
key := string(k)
|
||||
value := string(v)
|
||||
// for slices
|
||||
if form[key] != nil {
|
||||
form[key] = append(form[key], value)
|
||||
} else {
|
||||
form[key] = []string{value}
|
||||
}
|
||||
})
|
||||
|
||||
return errReadBody.With(formBinder.Decode(form, formObject))
|
||||
}
|
||||
|
||||
/* Response */
|
||||
|
||||
// SetContentType sets the response writer's header key 'Content-Type' to a given value(s)
|
||||
func (ctx *Context) SetContentType(s string) {
|
||||
ctx.RequestCtx.Response.Header.Set(contentType, s)
|
||||
}
|
||||
|
||||
// SetHeader write to the response writer's header to a given key the given value(s)
|
||||
func (ctx *Context) SetHeader(k string, v string) {
|
||||
ctx.RequestCtx.Response.Header.Set(k, v)
|
||||
}
|
||||
|
||||
// Redirect redirect sends a redirect response the client
|
||||
// accepts 2 parameters string and an optional int
|
||||
// first parameter is the url to redirect
|
||||
// second parameter is the http status should send, default is 302 (StatusFound), you can set it to 301 (Permant redirect), if that's nessecery
|
||||
func (ctx *Context) Redirect(urlToRedirect string, statusHeader ...int) {
|
||||
httpStatus := StatusFound // temporary redirect
|
||||
if statusHeader != nil && len(statusHeader) > 0 && statusHeader[0] > 0 {
|
||||
httpStatus = statusHeader[0]
|
||||
}
|
||||
ctx.RequestCtx.Redirect(urlToRedirect, httpStatus)
|
||||
ctx.StopExecution()
|
||||
}
|
||||
|
||||
// RedirectTo does the same thing as Redirect but instead of receiving a uri or path it receives a route name
|
||||
func (ctx *Context) RedirectTo(routeName string, args ...interface{}) {
|
||||
s := ctx.framework.URL(routeName, args...)
|
||||
if s != "" {
|
||||
ctx.Redirect(s, StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
// NotFound emits an error 404 to the client, using the custom http errors
|
||||
// if no custom errors provided then it sends the default error message
|
||||
func (ctx *Context) NotFound() {
|
||||
ctx.framework.EmitError(StatusNotFound, ctx)
|
||||
}
|
||||
|
||||
// Panic emits an error 500 to the client, using the custom http errors
|
||||
// if no custom errors rpovided then it sends the default error message
|
||||
func (ctx *Context) Panic() {
|
||||
ctx.framework.EmitError(StatusInternalServerError, ctx)
|
||||
}
|
||||
|
||||
// EmitError executes the custom error by the http status code passed to the function
|
||||
func (ctx *Context) EmitError(statusCode int) {
|
||||
ctx.framework.EmitError(statusCode, ctx)
|
||||
ctx.StopExecution()
|
||||
}
|
||||
|
||||
// Write writes a string to the client, something like fmt.Printf but for the web
|
||||
func (ctx *Context) Write(format string, a ...interface{}) {
|
||||
//this doesn't work with gzip, so just write the []byte better |ctx.ResponseWriter.WriteString(fmt.Sprintf(format, a...))
|
||||
ctx.RequestCtx.WriteString(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// HTML writes html string with a http status
|
||||
func (ctx *Context) HTML(httpStatus int, htmlContents string) {
|
||||
ctx.SetContentType(contentHTML + ctx.framework.rest.CompiledCharset)
|
||||
ctx.RequestCtx.SetStatusCode(httpStatus)
|
||||
ctx.RequestCtx.WriteString(htmlContents)
|
||||
}
|
||||
|
||||
// Data writes out the raw bytes as binary data.
|
||||
func (ctx *Context) Data(status int, v []byte) error {
|
||||
return ctx.framework.rest.Data(ctx.RequestCtx, status, v)
|
||||
}
|
||||
|
||||
// RenderWithStatus builds up the response from the specified template and bindings.
|
||||
// Note: parameter layout has meaning only when using the iris.HTMLTemplate
|
||||
func (ctx *Context) RenderWithStatus(status int, name string, binding interface{}, layout ...string) error {
|
||||
ctx.SetStatusCode(status)
|
||||
return ctx.framework.templates.Render(ctx, name, binding, layout...)
|
||||
}
|
||||
|
||||
// Render same as .RenderWithStatus but with status to iris.StatusOK (200)
|
||||
func (ctx *Context) Render(name string, binding interface{}, layout ...string) error {
|
||||
return ctx.RenderWithStatus(StatusOK, name, binding, layout...)
|
||||
}
|
||||
|
||||
// MustRender same as .Render but returns 500 internal server http status (error) if rendering fail
|
||||
func (ctx *Context) MustRender(name string, binding interface{}, layout ...string) {
|
||||
if err := ctx.Render(name, binding, layout...); err != nil {
|
||||
ctx.Panic()
|
||||
ctx.framework.Logger.Dangerf("MustRender panics for client with IP: %s On template: %s", ctx.RemoteAddr(), name)
|
||||
}
|
||||
}
|
||||
|
||||
// TemplateString accepts a template filename, its context data and returns the result of the parsed template (string)
|
||||
// if any error returns empty string
|
||||
func (ctx *Context) TemplateString(name string, binding interface{}, layout ...string) string {
|
||||
return ctx.framework.TemplateString(name, binding, layout...)
|
||||
}
|
||||
|
||||
// JSON marshals the given interface object and writes the JSON response.
|
||||
func (ctx *Context) JSON(status int, v interface{}) error {
|
||||
return ctx.framework.rest.JSON(ctx.RequestCtx, status, v)
|
||||
}
|
||||
|
||||
// JSONP marshals the given interface object and writes the JSON response.
|
||||
func (ctx *Context) JSONP(status int, callback string, v interface{}) error {
|
||||
return ctx.framework.rest.JSONP(ctx.RequestCtx, status, callback, v)
|
||||
}
|
||||
|
||||
// Text writes out a string as plain text.
|
||||
func (ctx *Context) Text(status int, v string) error {
|
||||
return ctx.framework.rest.Text(ctx.RequestCtx, status, v)
|
||||
}
|
||||
|
||||
// XML marshals the given interface object and writes the XML response.
|
||||
func (ctx *Context) XML(status int, v interface{}) error {
|
||||
return ctx.framework.rest.XML(ctx.RequestCtx, status, v)
|
||||
}
|
||||
|
||||
// MarkdownString parses the (dynamic) markdown string and returns the converted html string
|
||||
func (ctx *Context) MarkdownString(markdownText string) string {
|
||||
return ctx.framework.rest.Markdown([]byte(markdownText))
|
||||
}
|
||||
|
||||
// Markdown parses and renders to the client a particular (dynamic) markdown string
|
||||
// accepts two parameters
|
||||
// first is the http status code
|
||||
// second is the markdown string
|
||||
func (ctx *Context) Markdown(status int, markdown string) {
|
||||
ctx.HTML(status, ctx.MarkdownString(markdown))
|
||||
}
|
||||
|
||||
// ExecuteTemplate executes a simple html template, you can use that if you already have the cached templates
|
||||
// the recommended way to render is to use iris.Templates("./templates/path/*.html") and ctx.RenderFile("filename.html",struct{})
|
||||
// accepts 2 parameters
|
||||
// the first parameter is the template (*template.Template)
|
||||
// the second parameter is the page context (interfac{})
|
||||
// returns an error if any errors occurs while executing this template
|
||||
func (ctx *Context) ExecuteTemplate(tmpl *template.Template, pageContext interface{}) error {
|
||||
ctx.RequestCtx.SetContentType(contentHTML + ctx.framework.rest.CompiledCharset)
|
||||
return errTemplateExecute.With(tmpl.Execute(ctx.RequestCtx.Response.BodyWriter(), pageContext))
|
||||
}
|
||||
|
||||
// ServeContent serves content, headers are autoset
|
||||
// receives three parameters, it's low-level function, instead you can use .ServeFile(string)
|
||||
//
|
||||
// You can define your own "Content-Type" header also, after this function call
|
||||
func (ctx *Context) ServeContent(content io.ReadSeeker, filename string, modtime time.Time, gzipCompression bool) error {
|
||||
if t, err := time.Parse(config.TimeFormat, ctx.RequestHeader(ifModifiedSince)); err == nil && modtime.Before(t.Add(1*time.Second)) {
|
||||
ctx.RequestCtx.Response.Header.Del(contentType)
|
||||
ctx.RequestCtx.Response.Header.Del(contentLength)
|
||||
ctx.RequestCtx.SetStatusCode(StatusNotModified)
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.RequestCtx.Response.Header.Set(contentType, utils.TypeByExtension(filename))
|
||||
ctx.RequestCtx.Response.Header.Set(lastModified, modtime.UTC().Format(config.TimeFormat))
|
||||
ctx.RequestCtx.SetStatusCode(StatusOK)
|
||||
var out io.Writer
|
||||
if gzipCompression {
|
||||
ctx.RequestCtx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
gzipWriter := gzipWriterPool.Get().(*gzip.Writer)
|
||||
gzipWriter.Reset(ctx.RequestCtx.Response.BodyWriter())
|
||||
defer gzipWriter.Close()
|
||||
defer gzipWriterPool.Put(gzipWriter)
|
||||
out = gzipWriter
|
||||
} else {
|
||||
out = ctx.RequestCtx.Response.BodyWriter()
|
||||
|
||||
}
|
||||
_, err := io.Copy(out, content)
|
||||
return errServeContent.With(err)
|
||||
}
|
||||
|
||||
// ServeFile serves a view file, to send a file ( zip for example) to the client you should use the SendFile(serverfilename,clientfilename)
|
||||
// receives two parameters
|
||||
// filename/path (string)
|
||||
// gzipCompression (bool)
|
||||
//
|
||||
// You can define your own "Content-Type" header also, after this function call
|
||||
func (ctx *Context) ServeFile(filename string, gzipCompression bool) error {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%d", 404)
|
||||
}
|
||||
defer f.Close()
|
||||
fi, _ := f.Stat()
|
||||
if fi.IsDir() {
|
||||
filename = path.Join(filename, "index.html")
|
||||
f, err = os.Open(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%d", 404)
|
||||
}
|
||||
fi, _ = f.Stat()
|
||||
}
|
||||
return ctx.ServeContent(f, fi.Name(), fi.ModTime(), gzipCompression)
|
||||
}
|
||||
|
||||
// SendFile sends file for force-download to the client
|
||||
//
|
||||
// You can define your own "Content-Type" header also, after this function call
|
||||
// for example: ctx.Response.Header.Set("Content-Type","thecontent/type")
|
||||
func (ctx *Context) SendFile(filename string, destinationName string) error {
|
||||
err := ctx.ServeFile(filename, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.RequestCtx.Response.Header.Set(contentDisposition, "attachment;filename="+destinationName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stream same as StreamWriter
|
||||
func (ctx *Context) Stream(cb func(writer *bufio.Writer)) {
|
||||
ctx.StreamWriter(cb)
|
||||
}
|
||||
|
||||
// StreamWriter registers the given stream writer for populating
|
||||
// response body.
|
||||
//
|
||||
//
|
||||
// This function may be used in the following cases:
|
||||
//
|
||||
// * if response body is too big (more than 10MB).
|
||||
// * if response body is streamed from slow external sources.
|
||||
// * if response body must be streamed to the client in chunks.
|
||||
// (aka `http server push`).
|
||||
func (ctx *Context) StreamWriter(cb func(writer *bufio.Writer)) {
|
||||
ctx.RequestCtx.SetBodyStreamWriter(cb)
|
||||
}
|
||||
|
||||
// StreamReader sets response body stream and, optionally body size.
|
||||
//
|
||||
// If bodySize is >= 0, then the bodyStream must provide exactly bodySize bytes
|
||||
// before returning io.EOF.
|
||||
//
|
||||
// If bodySize < 0, then bodyStream is read until io.EOF.
|
||||
//
|
||||
// bodyStream.Close() is called after finishing reading all body data
|
||||
// if it implements io.Closer.
|
||||
//
|
||||
// See also StreamReader.
|
||||
func (ctx *Context) StreamReader(bodyStream io.Reader, bodySize int) {
|
||||
ctx.RequestCtx.Response.SetBodyStream(bodyStream, bodySize)
|
||||
}
|
||||
|
||||
/* Storage */
|
||||
|
||||
// Get returns the user's value from a key
|
||||
// if doesn't exists returns nil
|
||||
func (ctx *Context) Get(key string) interface{} {
|
||||
return ctx.RequestCtx.UserValue(key)
|
||||
}
|
||||
|
||||
// GetFmt returns a value which has this format: func(format string, args ...interface{}) string
|
||||
// if doesn't exists returns nil
|
||||
func (ctx *Context) GetFmt(key string) func(format string, args ...interface{}) string {
|
||||
if v, ok := ctx.Get(key).(func(format string, args ...interface{}) string); ok {
|
||||
return v
|
||||
}
|
||||
return func(format string, args ...interface{}) string { return "" }
|
||||
|
||||
}
|
||||
|
||||
// GetString same as Get but returns the value as string
|
||||
// if nothing founds returns empty string ""
|
||||
func (ctx *Context) GetString(key string) string {
|
||||
if v, ok := ctx.Get(key).(string); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetInt same as Get but returns the value as int
|
||||
// if nothing founds returns -1
|
||||
func (ctx *Context) GetInt(key string) int {
|
||||
if v, ok := ctx.Get(key).(int); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// Set sets a value to a key in the values map
|
||||
func (ctx *Context) Set(key string, value interface{}) {
|
||||
ctx.RequestCtx.SetUserValue(key, value)
|
||||
}
|
||||
|
||||
// GetCookie returns cookie's value by it's name
|
||||
// returns empty string if nothing was found
|
||||
func (ctx *Context) GetCookie(name string) (val string) {
|
||||
bcookie := ctx.RequestCtx.Request.Header.Cookie(name)
|
||||
if bcookie != nil {
|
||||
val = string(bcookie)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetCookie adds a cookie
|
||||
func (ctx *Context) SetCookie(cookie *fasthttp.Cookie) {
|
||||
ctx.RequestCtx.Response.Header.SetCookie(cookie)
|
||||
}
|
||||
|
||||
// SetCookieKV adds a cookie, receives just a key(string) and a value(string)
|
||||
func (ctx *Context) SetCookieKV(key, value string) {
|
||||
c := fasthttp.AcquireCookie() // &fasthttp.Cookie{}
|
||||
c.SetKey(key)
|
||||
c.SetValue(value)
|
||||
c.SetHTTPOnly(true)
|
||||
c.SetExpire(time.Now().Add(time.Duration(120) * time.Minute))
|
||||
ctx.SetCookie(c)
|
||||
fasthttp.ReleaseCookie(c)
|
||||
}
|
||||
|
||||
// RemoveCookie deletes a cookie by it's name/key
|
||||
func (ctx *Context) RemoveCookie(name string) {
|
||||
cookie := fasthttp.AcquireCookie()
|
||||
cookie.SetKey(name)
|
||||
cookie.SetValue("")
|
||||
cookie.SetPath("/")
|
||||
cookie.SetHTTPOnly(true)
|
||||
exp := time.Now().Add(-time.Duration(1) * time.Minute) //RFC says 1 second, but make sure 1 minute because we are using fasthttp
|
||||
cookie.SetExpire(exp)
|
||||
ctx.Response.Header.SetCookie(cookie)
|
||||
fasthttp.ReleaseCookie(cookie)
|
||||
}
|
||||
|
||||
// GetFlash get a flash message by it's key
|
||||
// after this action the messages is removed
|
||||
// returns string, if the cookie doesn't exists the string is empty
|
||||
func (ctx *Context) GetFlash(key string) string {
|
||||
val, err := ctx.GetFlashBytes(key)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(val)
|
||||
}
|
||||
|
||||
// GetFlashBytes get a flash message by it's key
|
||||
// after this action the messages is removed
|
||||
// returns []byte along with an error if the cookie doesn't exists or decode fails
|
||||
func (ctx *Context) GetFlashBytes(key string) (value []byte, err error) {
|
||||
cookieValue := string(ctx.RequestCtx.Request.Header.Cookie(key))
|
||||
if cookieValue == "" {
|
||||
err = errFlashNotFound.Return()
|
||||
} else {
|
||||
value, err = base64.URLEncoding.DecodeString(cookieValue)
|
||||
//remove the message
|
||||
ctx.RemoveCookie(key)
|
||||
//it should'b be removed until the next reload, so we don't do that: ctx.Request.Header.SetCookie(key, "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetFlash sets a flash message, accepts 2 parameters the key(string) and the value(string)
|
||||
func (ctx *Context) SetFlash(key string, value string) {
|
||||
ctx.SetFlashBytes(key, utils.StringToBytes(value))
|
||||
}
|
||||
|
||||
// SetFlashBytes sets a flash message, accepts 2 parameters the key(string) and the value([]byte)
|
||||
func (ctx *Context) SetFlashBytes(key string, value []byte) {
|
||||
c := fasthttp.AcquireCookie()
|
||||
c.SetKey(key)
|
||||
c.SetValue(base64.URLEncoding.EncodeToString(value))
|
||||
c.SetPath("/")
|
||||
c.SetHTTPOnly(true)
|
||||
ctx.RequestCtx.Response.Header.SetCookie(c)
|
||||
fasthttp.ReleaseCookie(c)
|
||||
}
|
||||
|
||||
// Session returns the current session store, returns nil if provider is ""
|
||||
func (ctx *Context) Session() store.IStore {
|
||||
if ctx.framework.sessions == nil || ctx.framework.Config.Sessions.Provider == "" { //the second check can be changed on runtime, users are able to turn off the sessions by setting provider to ""
|
||||
return nil
|
||||
}
|
||||
|
||||
if ctx.sessionStore == nil {
|
||||
ctx.sessionStore = ctx.framework.sessions.Start(ctx)
|
||||
}
|
||||
return ctx.sessionStore
|
||||
}
|
||||
|
||||
// SessionDestroy destroys the whole session, calls the provider's destroy and remove the cookie
|
||||
func (ctx *Context) SessionDestroy() {
|
||||
if ctx.framework.sessions != nil {
|
||||
if store := ctx.Session(); store != nil {
|
||||
ctx.framework.sessions.Destroy(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// SendMail sends a mail to recipients
|
||||
// the body can be html also
|
||||
func (ctx *Context) SendMail(subject string, body string, to ...string) error {
|
||||
return ctx.framework.SendMail(subject, body, to...)
|
||||
}
|
||||
|
||||
// Log logs to the iris defined logger
|
||||
func (ctx *Context) Log(format string, a ...interface{}) {
|
||||
ctx.framework.Logger.Printf(format, a...)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ type (
|
|||
IContextBinder
|
||||
IContextRequest
|
||||
IContextResponse
|
||||
|
||||
SendMail(string, string, ...string) error
|
||||
Log(string, ...interface{})
|
||||
Reset(*fasthttp.RequestCtx)
|
||||
GetRequestCtx() *fasthttp.RequestCtx
|
||||
Clone() IContext
|
||||
|
@ -39,17 +40,18 @@ type (
|
|||
// IContextRenderer is part of the IContext
|
||||
IContextRenderer interface {
|
||||
Write(string, ...interface{})
|
||||
WriteHTML(int, string)
|
||||
HTML(int, string)
|
||||
// Data writes out the raw bytes as binary data.
|
||||
Data(status int, v []byte) error
|
||||
// HTML builds up the response from the specified template and bindings.
|
||||
HTML(status int, name string, binding interface{}, layout ...string) error
|
||||
// Render same as .HTML but with status to iris.StatusOK (200)
|
||||
// RenderWithStatus builds up the response from the specified template and bindings.
|
||||
RenderWithStatus(status int, name string, binding interface{}, layout ...string) error
|
||||
// Render same as .RenderWithStatus but with status to iris.StatusOK (200)
|
||||
Render(name string, binding interface{}, layout ...string) error
|
||||
// MustRender same as .Render but returns 500 internal server http status (error) if rendering fail
|
||||
MustRender(name string, binding interface{}, layout ...string)
|
||||
// RenderString accepts a template filename, its context data and returns the result of the parsed template (string)
|
||||
RenderString(name string, binding interface{}, layout ...string) (result string, err error)
|
||||
// TemplateString accepts a template filename, its context data and returns the result of the parsed template (string)
|
||||
// if any error returns empty string
|
||||
TemplateString(name string, binding interface{}, layout ...string) string
|
||||
// MarkdownString parses the (dynamic) markdown string and returns the converted html string
|
||||
MarkdownString(markdown string) string
|
||||
// Markdown parses and renders to the client a particular (dynamic) markdown string
|
||||
|
@ -86,10 +88,13 @@ type (
|
|||
HostString() string
|
||||
Subdomain() string
|
||||
PathString() string
|
||||
RequestPath(bool) string
|
||||
RequestIP() string
|
||||
RemoteAddr() string
|
||||
RequestHeader(k string) string
|
||||
PostFormValue(string) string
|
||||
// PostFormMulti returns a slice of string from post request's data
|
||||
PostFormMulti(string) []string
|
||||
}
|
||||
|
||||
// IContextResponse is part of the IContext
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/klauspost/compress/gzip"
|
||||
)
|
||||
|
||||
// Write writes a string via the context's ResponseWriter
|
||||
func (ctx *Context) Write(format string, a ...interface{}) {
|
||||
//this doesn't work with gzip, so just write the []byte better |ctx.ResponseWriter.WriteString(fmt.Sprintf(format, a...))
|
||||
ctx.RequestCtx.WriteString(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// WriteHTML writes html string with a http status
|
||||
func (ctx *Context) WriteHTML(httpStatus int, htmlContents string) {
|
||||
ctx.SetContentType(ContentHTML + ctx.station.rest.CompiledCharset)
|
||||
ctx.RequestCtx.SetStatusCode(httpStatus)
|
||||
ctx.RequestCtx.WriteString(htmlContents)
|
||||
}
|
||||
|
||||
// Data writes out the raw bytes as binary data.
|
||||
func (ctx *Context) Data(status int, v []byte) error {
|
||||
return ctx.station.rest.Data(ctx.RequestCtx, status, v)
|
||||
}
|
||||
|
||||
// HTML builds up the response from the specified template and bindings.
|
||||
// Note: parameter layout has meaning only when using the iris.HTMLTemplate
|
||||
func (ctx *Context) HTML(status int, name string, binding interface{}, layout ...string) error {
|
||||
ctx.SetStatusCode(status)
|
||||
return ctx.station.templates.Render(ctx, name, binding, layout...)
|
||||
}
|
||||
|
||||
// Render same as .HTML but with status to iris.StatusOK (200)
|
||||
func (ctx *Context) Render(name string, binding interface{}, layout ...string) error {
|
||||
return ctx.HTML(StatusOK, name, binding, layout...)
|
||||
}
|
||||
|
||||
// MustRender same as .Render but returns 500 internal server http status (error) if rendering fail
|
||||
func (ctx *Context) MustRender(name string, binding interface{}, layout ...string) {
|
||||
if err := ctx.Render(name, binding, layout...); err != nil {
|
||||
ctx.Panic()
|
||||
}
|
||||
}
|
||||
|
||||
// RenderString accepts a template filename, its context data and returns the result of the parsed template (string)
|
||||
func (ctx *Context) RenderString(name string, binding interface{}, layout ...string) (result string, err error) {
|
||||
return ctx.station.templates.RenderString(name, binding, layout...)
|
||||
}
|
||||
|
||||
// JSON marshals the given interface object and writes the JSON response.
|
||||
func (ctx *Context) JSON(status int, v interface{}) error {
|
||||
return ctx.station.rest.JSON(ctx.RequestCtx, status, v)
|
||||
}
|
||||
|
||||
// JSONP marshals the given interface object and writes the JSON response.
|
||||
func (ctx *Context) JSONP(status int, callback string, v interface{}) error {
|
||||
return ctx.station.rest.JSONP(ctx.RequestCtx, status, callback, v)
|
||||
}
|
||||
|
||||
// Text writes out a string as plain text.
|
||||
func (ctx *Context) Text(status int, v string) error {
|
||||
return ctx.station.rest.Text(ctx.RequestCtx, status, v)
|
||||
}
|
||||
|
||||
// XML marshals the given interface object and writes the XML response.
|
||||
func (ctx *Context) XML(status int, v interface{}) error {
|
||||
return ctx.station.rest.XML(ctx.RequestCtx, status, v)
|
||||
}
|
||||
|
||||
// MarkdownString parses the (dynamic) markdown string and returns the converted html string
|
||||
func (ctx *Context) MarkdownString(markdown string) string {
|
||||
return ctx.station.rest.Markdown([]byte(markdown))
|
||||
}
|
||||
|
||||
// Markdown parses and renders to the client a particular (dynamic) markdown string
|
||||
// accepts two parameters
|
||||
// first is the http status code
|
||||
// second is the markdown string
|
||||
func (ctx *Context) Markdown(status int, markdown string) {
|
||||
ctx.WriteHTML(status, ctx.MarkdownString(markdown))
|
||||
}
|
||||
|
||||
// ExecuteTemplate executes a simple html template, you can use that if you already have the cached templates
|
||||
// the recommended way to render is to use iris.Templates("./templates/path/*.html") and ctx.RenderFile("filename.html",struct{})
|
||||
// accepts 2 parameters
|
||||
// the first parameter is the template (*template.Template)
|
||||
// the second parameter is the page context (interfac{})
|
||||
// returns an error if any errors occurs while executing this template
|
||||
func (ctx *Context) ExecuteTemplate(tmpl *template.Template, pageContext interface{}) error {
|
||||
ctx.RequestCtx.SetContentType(ContentHTML + ctx.station.rest.CompiledCharset)
|
||||
return ErrTemplateExecute.With(tmpl.Execute(ctx.RequestCtx.Response.BodyWriter(), pageContext))
|
||||
}
|
||||
|
||||
// ServeContent serves content, headers are autoset
|
||||
// receives three parameters, it's low-level function, instead you can use .ServeFile(string)
|
||||
//
|
||||
// You can define your own "Content-Type" header also, after this function call
|
||||
func (ctx *Context) ServeContent(content io.ReadSeeker, filename string, modtime time.Time, gzipCompression bool) error {
|
||||
if t, err := time.Parse(TimeFormat, ctx.RequestHeader(IfModifiedSince)); err == nil && modtime.Before(t.Add(1*time.Second)) {
|
||||
ctx.RequestCtx.Response.Header.Del(ContentType)
|
||||
ctx.RequestCtx.Response.Header.Del(ContentLength)
|
||||
ctx.RequestCtx.SetStatusCode(StatusNotModified)
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx.RequestCtx.Response.Header.Set(ContentType, utils.TypeByExtension(filename))
|
||||
ctx.RequestCtx.Response.Header.Set(LastModified, modtime.UTC().Format(TimeFormat))
|
||||
ctx.RequestCtx.SetStatusCode(StatusOK)
|
||||
var out io.Writer
|
||||
if gzipCompression {
|
||||
ctx.RequestCtx.Response.Header.Add("Content-Encoding", "gzip")
|
||||
gzipWriter := ctx.station.gzipWriterPool.Get().(*gzip.Writer)
|
||||
gzipWriter.Reset(ctx.RequestCtx.Response.BodyWriter())
|
||||
defer gzipWriter.Close()
|
||||
defer ctx.station.gzipWriterPool.Put(gzipWriter)
|
||||
out = gzipWriter
|
||||
} else {
|
||||
out = ctx.RequestCtx.Response.BodyWriter()
|
||||
|
||||
}
|
||||
_, err := io.Copy(out, content)
|
||||
return ErrServeContent.With(err)
|
||||
}
|
||||
|
||||
// ServeFile serves a view file, to send a file ( zip for example) to the client you should use the SendFile(serverfilename,clientfilename)
|
||||
// receives two parameters
|
||||
// filename/path (string)
|
||||
// gzipCompression (bool)
|
||||
//
|
||||
// You can define your own "Content-Type" header also, after this function call
|
||||
func (ctx *Context) ServeFile(filename string, gzipCompression bool) error {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%d", 404)
|
||||
}
|
||||
defer f.Close()
|
||||
fi, _ := f.Stat()
|
||||
if fi.IsDir() {
|
||||
filename = path.Join(filename, "index.html")
|
||||
f, err = os.Open(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%d", 404)
|
||||
}
|
||||
fi, _ = f.Stat()
|
||||
}
|
||||
return ctx.ServeContent(f, fi.Name(), fi.ModTime(), gzipCompression)
|
||||
}
|
||||
|
||||
// SendFile sends file for force-download to the client
|
||||
//
|
||||
// You can define your own "Content-Type" header also, after this function call
|
||||
// for example: ctx.Response.Header.Set("Content-Type","thecontent/type")
|
||||
func (ctx *Context) SendFile(filename string, destinationName string) error {
|
||||
err := ctx.ServeFile(filename, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.RequestCtx.Response.Header.Set(ContentDisposition, "attachment;filename="+destinationName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stream same as StreamWriter
|
||||
func (ctx *Context) Stream(cb func(writer *bufio.Writer)) {
|
||||
ctx.StreamWriter(cb)
|
||||
}
|
||||
|
||||
// StreamWriter registers the given stream writer for populating
|
||||
// response body.
|
||||
//
|
||||
//
|
||||
// This function may be used in the following cases:
|
||||
//
|
||||
// * if response body is too big (more than 10MB).
|
||||
// * if response body is streamed from slow external sources.
|
||||
// * if response body must be streamed to the client in chunks.
|
||||
// (aka `http server push`).
|
||||
func (ctx *Context) StreamWriter(cb func(writer *bufio.Writer)) {
|
||||
ctx.RequestCtx.SetBodyStreamWriter(cb)
|
||||
}
|
||||
|
||||
// StreamReader sets response body stream and, optionally body size.
|
||||
//
|
||||
// If bodySize is >= 0, then the bodyStream must provide exactly bodySize bytes
|
||||
// before returning io.EOF.
|
||||
//
|
||||
// If bodySize < 0, then bodyStream is read until io.EOF.
|
||||
//
|
||||
// bodyStream.Close() is called after finishing reading all body data
|
||||
// if it implements io.Closer.
|
||||
//
|
||||
// See also StreamReader.
|
||||
func (ctx *Context) StreamReader(bodyStream io.Reader, bodySize int) {
|
||||
ctx.RequestCtx.Response.SetBodyStream(bodyStream, bodySize)
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/bindings"
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Param returns the string representation of the key's path named parameter's value
|
||||
func (ctx *Context) Param(key string) string {
|
||||
return ctx.Params.Get(key)
|
||||
}
|
||||
|
||||
// ParamInt returns the int representation of the key's path named parameter's value
|
||||
func (ctx *Context) ParamInt(key string) (int, error) {
|
||||
val, err := strconv.Atoi(ctx.Param(key))
|
||||
return val, err
|
||||
}
|
||||
|
||||
// URLParam returns the get parameter from a request , if any
|
||||
func (ctx *Context) URLParam(key string) string {
|
||||
return string(ctx.RequestCtx.Request.URI().QueryArgs().Peek(key))
|
||||
}
|
||||
|
||||
// URLParams returns a map of a list of each url(query) parameter
|
||||
func (ctx *Context) URLParams() map[string]string {
|
||||
urlparams := make(map[string]string)
|
||||
ctx.RequestCtx.Request.URI().QueryArgs().VisitAll(func(key, value []byte) {
|
||||
urlparams[string(key)] = string(value)
|
||||
})
|
||||
return urlparams
|
||||
}
|
||||
|
||||
// URLParamInt returns the get parameter int value from a request , if any
|
||||
func (ctx *Context) URLParamInt(key string) (int, error) {
|
||||
return strconv.Atoi(ctx.URLParam(key))
|
||||
}
|
||||
|
||||
// MethodString returns the HTTP Method
|
||||
func (ctx *Context) MethodString() string {
|
||||
return utils.BytesToString(ctx.Method())
|
||||
}
|
||||
|
||||
// HostString returns the Host of the request( the url as string )
|
||||
func (ctx *Context) HostString() string {
|
||||
return utils.BytesToString(ctx.Host())
|
||||
}
|
||||
|
||||
// PathString returns the full path as string
|
||||
func (ctx *Context) PathString() string {
|
||||
return utils.BytesToString(ctx.Path())
|
||||
}
|
||||
|
||||
// RequestIP gets just the Remote Address from the client.
|
||||
func (ctx *Context) RequestIP() string {
|
||||
if ip, _, err := net.SplitHostPort(strings.TrimSpace(ctx.RequestCtx.RemoteAddr().String())); err == nil {
|
||||
return ip
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// RemoteAddr is like RequestIP but it checks for proxy servers also, tries to get the real client's request IP
|
||||
func (ctx *Context) RemoteAddr() string {
|
||||
header := string(ctx.RequestCtx.Request.Header.Peek("X-Real-Ip"))
|
||||
realIP := strings.TrimSpace(header)
|
||||
if realIP != "" {
|
||||
return realIP
|
||||
}
|
||||
realIP = string(ctx.RequestCtx.Request.Header.Peek("X-Forwarded-For"))
|
||||
idx := strings.IndexByte(realIP, ',')
|
||||
if idx >= 0 {
|
||||
realIP = realIP[0:idx]
|
||||
}
|
||||
realIP = strings.TrimSpace(realIP)
|
||||
if realIP != "" {
|
||||
return realIP
|
||||
}
|
||||
return ctx.RequestIP()
|
||||
|
||||
}
|
||||
|
||||
// RequestHeader returns the request header's value
|
||||
// accepts one parameter, the key of the header (string)
|
||||
// returns string
|
||||
func (ctx *Context) RequestHeader(k string) string {
|
||||
return utils.BytesToString(ctx.RequestCtx.Request.Header.Peek(k))
|
||||
}
|
||||
|
||||
// PostFormValue returns a single value from post request's data
|
||||
func (ctx *Context) PostFormValue(name string) string {
|
||||
return string(ctx.RequestCtx.PostArgs().Peek(name))
|
||||
}
|
||||
|
||||
// Subdomain returns the subdomain (string) of this request, if any
|
||||
func (ctx *Context) Subdomain() (subdomain string) {
|
||||
host := ctx.HostString()
|
||||
if index := strings.IndexByte(host, '.'); index > 0 {
|
||||
subdomain = host[0:index]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// URLEncode returns the path encoded as url
|
||||
// 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
|
||||
/* Credits to Manish Singh @kryptodev for URLEncode */
|
||||
func URLEncode(path string) string {
|
||||
if path == "" {
|
||||
return ""
|
||||
}
|
||||
u := fasthttp.AcquireURI()
|
||||
u.SetPath(path)
|
||||
encodedPath := u.String()[8:]
|
||||
fasthttp.ReleaseURI(u)
|
||||
return encodedPath
|
||||
}
|
||||
|
||||
// ReadJSON reads JSON from request's body
|
||||
func (ctx *Context) ReadJSON(jsonObject interface{}) error {
|
||||
return bindings.BindJSON(ctx, jsonObject)
|
||||
}
|
||||
|
||||
// ReadXML reads XML from request's body
|
||||
func (ctx *Context) ReadXML(xmlObject interface{}) error {
|
||||
return bindings.BindXML(ctx, xmlObject)
|
||||
}
|
||||
|
||||
// ReadForm binds the formObject with the form data
|
||||
// it supports any kind of struct
|
||||
func (ctx *Context) ReadForm(formObject interface{}) error {
|
||||
return bindings.BindForm(ctx, formObject)
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
package iris
|
||||
|
||||
// SetContentType sets the response writer's header key 'Content-Type' to a given value(s)
|
||||
func (ctx *Context) SetContentType(s string) {
|
||||
ctx.RequestCtx.Response.Header.Set(ContentType, s)
|
||||
}
|
||||
|
||||
// SetHeader write to the response writer's header to a given key the given value(s)
|
||||
func (ctx *Context) SetHeader(k string, v string) {
|
||||
ctx.RequestCtx.Response.Header.Set(k, v)
|
||||
}
|
||||
|
||||
// Redirect redirect sends a redirect response the client
|
||||
// accepts 2 parameters string and an optional int
|
||||
// first parameter is the url to redirect
|
||||
// second parameter is the http status should send, default is 302 (StatusFound), you can set it to 301 (Permant redirect), if that's nessecery
|
||||
func (ctx *Context) Redirect(urlToRedirect string, statusHeader ...int) {
|
||||
httpStatus := StatusFound // temporary redirect
|
||||
if statusHeader != nil && len(statusHeader) > 0 && statusHeader[0] > 0 {
|
||||
httpStatus = statusHeader[0]
|
||||
}
|
||||
ctx.RequestCtx.Redirect(urlToRedirect, httpStatus)
|
||||
ctx.StopExecution()
|
||||
}
|
||||
|
||||
// RedirectTo does the same thing as Redirect but instead of receiving a uri or path it receives a route name
|
||||
func (ctx *Context) RedirectTo(routeName string, args ...interface{}) {
|
||||
s := ctx.station.RouteByName(routeName).ParseURI(args...)
|
||||
if s != "" {
|
||||
ctx.Redirect(s, StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
// Error handling
|
||||
|
||||
// NotFound emits an error 404 to the client, using the custom http errors
|
||||
// if no custom errors provided then it sends the default http.NotFound
|
||||
func (ctx *Context) NotFound() {
|
||||
ctx.StopExecution()
|
||||
ctx.station.EmitError(404, ctx)
|
||||
}
|
||||
|
||||
// Panic stops the executions of the context and returns the registed panic handler
|
||||
// or if not, the default which is 500 http status to the client
|
||||
//
|
||||
// This function is useful when you use the recovery middleware, which is auto-executing the (custom, registed) 500 internal server error.
|
||||
func (ctx *Context) Panic() {
|
||||
ctx.StopExecution()
|
||||
ctx.station.EmitError(500, ctx)
|
||||
}
|
||||
|
||||
// EmitError executes the custom error by the http status code passed to the function
|
||||
func (ctx *Context) EmitError(statusCode int) {
|
||||
ctx.station.EmitError(statusCode, ctx)
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/sessions/store"
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// After v2.2.3 Get/GetFmt/GetString/GetInt/Set are all return values from the RequestCtx.userValues they are reseting on each connection.
|
||||
|
||||
// Get returns the user's value from a key
|
||||
// if doesn't exists returns nil
|
||||
func (ctx *Context) Get(key string) interface{} {
|
||||
return ctx.RequestCtx.UserValue(key)
|
||||
}
|
||||
|
||||
// GetFmt returns a value which has this format: func(format string, args ...interface{}) string
|
||||
// if doesn't exists returns nil
|
||||
func (ctx *Context) GetFmt(key string) func(format string, args ...interface{}) string {
|
||||
if v, ok := ctx.Get(key).(func(format string, args ...interface{}) string); ok {
|
||||
return v
|
||||
}
|
||||
return func(format string, args ...interface{}) string { return "" }
|
||||
|
||||
}
|
||||
|
||||
// GetString same as Get but returns the value as string
|
||||
// if nothing founds returns empty string ""
|
||||
func (ctx *Context) GetString(key string) string {
|
||||
if v, ok := ctx.Get(key).(string); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetInt same as Get but returns the value as int
|
||||
// if nothing founds returns -1
|
||||
func (ctx *Context) GetInt(key string) int {
|
||||
if v, ok := ctx.Get(key).(int); ok {
|
||||
return v
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// Set sets a value to a key in the values map
|
||||
func (ctx *Context) Set(key string, value interface{}) {
|
||||
ctx.RequestCtx.SetUserValue(key, value)
|
||||
}
|
||||
|
||||
// GetCookie returns cookie's value by it's name
|
||||
// returns empty string if nothing was found
|
||||
func (ctx *Context) GetCookie(name string) (val string) {
|
||||
bcookie := ctx.RequestCtx.Request.Header.Cookie(name)
|
||||
if bcookie != nil {
|
||||
val = string(bcookie)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetCookie adds a cookie
|
||||
func (ctx *Context) SetCookie(cookie *fasthttp.Cookie) {
|
||||
ctx.RequestCtx.Response.Header.SetCookie(cookie)
|
||||
}
|
||||
|
||||
// SetCookieKV adds a cookie, receives just a key(string) and a value(string)
|
||||
func (ctx *Context) SetCookieKV(key, value string) {
|
||||
c := fasthttp.AcquireCookie() // &fasthttp.Cookie{}
|
||||
c.SetKey(key)
|
||||
c.SetValue(value)
|
||||
c.SetHTTPOnly(true)
|
||||
c.SetExpire(time.Now().Add(time.Duration(120) * time.Minute))
|
||||
ctx.SetCookie(c)
|
||||
fasthttp.ReleaseCookie(c)
|
||||
}
|
||||
|
||||
// RemoveCookie deletes a cookie by it's name/key
|
||||
func (ctx *Context) RemoveCookie(name string) {
|
||||
cookie := fasthttp.AcquireCookie()
|
||||
cookie.SetKey(name)
|
||||
cookie.SetValue("")
|
||||
cookie.SetPath("/")
|
||||
cookie.SetHTTPOnly(true)
|
||||
exp := time.Now().Add(-time.Duration(1) * time.Minute) //RFC says 1 second, but make sure 1 minute because we are using fasthttp
|
||||
cookie.SetExpire(exp)
|
||||
ctx.Response.Header.SetCookie(cookie)
|
||||
fasthttp.ReleaseCookie(cookie)
|
||||
}
|
||||
|
||||
// GetFlash get a flash message by it's key
|
||||
// after this action the messages is removed
|
||||
// returns string, if the cookie doesn't exists the string is empty
|
||||
func (ctx *Context) GetFlash(key string) string {
|
||||
val, err := ctx.GetFlashBytes(key)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(val)
|
||||
}
|
||||
|
||||
// GetFlashBytes get a flash message by it's key
|
||||
// after this action the messages is removed
|
||||
// returns []byte along with an error if the cookie doesn't exists or decode fails
|
||||
func (ctx *Context) GetFlashBytes(key string) (value []byte, err error) {
|
||||
cookieValue := string(ctx.RequestCtx.Request.Header.Cookie(key))
|
||||
if cookieValue == "" {
|
||||
err = ErrFlashNotFound.Return()
|
||||
} else {
|
||||
value, err = base64.URLEncoding.DecodeString(cookieValue)
|
||||
//remove the message
|
||||
ctx.RemoveCookie(key)
|
||||
//it should'b be removed until the next reload, so we don't do that: ctx.Request.Header.SetCookie(key, "")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetFlash sets a flash message, accepts 2 parameters the key(string) and the value(string)
|
||||
func (ctx *Context) SetFlash(key string, value string) {
|
||||
ctx.SetFlashBytes(key, utils.StringToBytes(value))
|
||||
}
|
||||
|
||||
// SetFlashBytes sets a flash message, accepts 2 parameters the key(string) and the value([]byte)
|
||||
func (ctx *Context) SetFlashBytes(key string, value []byte) {
|
||||
c := fasthttp.AcquireCookie()
|
||||
c.SetKey(key)
|
||||
c.SetValue(base64.URLEncoding.EncodeToString(value))
|
||||
c.SetPath("/")
|
||||
c.SetHTTPOnly(true)
|
||||
ctx.RequestCtx.Response.Header.SetCookie(c)
|
||||
fasthttp.ReleaseCookie(c)
|
||||
}
|
||||
|
||||
// Session returns the current session store, returns nil if provider is ""
|
||||
func (ctx *Context) Session() store.IStore {
|
||||
if ctx.station.sessionManager == nil || ctx.station.config.Sessions.Provider == "" { //the second check can be changed on runtime, users are able to turn off the sessions by setting provider to ""
|
||||
return nil
|
||||
}
|
||||
|
||||
if ctx.sessionStore == nil {
|
||||
ctx.sessionStore = ctx.station.sessionManager.Start(ctx)
|
||||
}
|
||||
return ctx.sessionStore
|
||||
}
|
||||
|
||||
// SessionDestroy destroys the whole session, calls the provider's destroy and remove the cookie
|
||||
func (ctx *Context) SessionDestroy() {
|
||||
if ctx.station.sessionManager != nil {
|
||||
if store := ctx.Session(); store != nil {
|
||||
ctx.station.sessionManager.Destroy(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
44
errors.go
44
errors.go
|
@ -1,44 +0,0 @@
|
|||
package iris
|
||||
|
||||
import "github.com/kataras/iris/errors"
|
||||
|
||||
var (
|
||||
// Router, Party & Handler
|
||||
|
||||
// ErrHandler returns na error with message: 'Passed argument is not func(*Context) neither an object which implements the iris.Handler with Serve(ctx *Context)
|
||||
// It seems to be a +type Points to: +pointer.'
|
||||
ErrHandler = errors.New("Passed argument is not func(*Context) neither an object which implements the iris.Handler with Serve(ctx *Context)\n It seems to be a %T Points to: %v.")
|
||||
// ErrHandleAnnotated returns an error with message: 'HandleAnnotated parse: +specific error(s)'
|
||||
ErrHandleAnnotated = errors.New("HandleAnnotated parse: %s")
|
||||
// ErrControllerContextNotFound returns an error with message: 'Context *iris.Context could not be found, the Controller won't be registed.'
|
||||
ErrControllerContextNotFound = errors.New("Context *iris.Context could not be found, the Controller won't be registed.")
|
||||
// ErrDirectoryFileNotFound returns an error with message: 'Directory or file %s couldn't found. Trace: +error trace'
|
||||
ErrDirectoryFileNotFound = errors.New("Directory or file %s couldn't found. Trace: %s")
|
||||
// ErrRenderRouteNotFound returns an error with message 'Route with name +route_name not found', used inside 'url' template func
|
||||
ErrRenderRouteNotFound = errors.New("Route with name %s not found")
|
||||
|
||||
// Plugin
|
||||
|
||||
// ErrPluginAlreadyExists returns an error with message: 'Cannot activate the same plugin again, plugin '+plugin name[+plugin description]' is already exists'
|
||||
ErrPluginAlreadyExists = errors.New("Cannot use the same plugin again, '%s[%s]' is already exists")
|
||||
// ErrPluginActivate returns an error with message: 'While trying to activate plugin '+plugin name'. Trace: +specific error'
|
||||
ErrPluginActivate = errors.New("While trying to activate plugin '%s'. Trace: %s")
|
||||
// ErrPluginRemoveNoPlugins returns an error with message: 'No plugins are registed yet, you cannot remove a plugin from an empty list!'
|
||||
ErrPluginRemoveNoPlugins = errors.New("No plugins are registed yet, you cannot remove a plugin from an empty list!")
|
||||
// ErrPluginRemoveEmptyName returns an error with message: 'Plugin with an empty name cannot be removed'
|
||||
ErrPluginRemoveEmptyName = errors.New("Plugin with an empty name cannot be removed")
|
||||
// ErrPluginRemoveNotFound returns an error with message: 'Cannot remove a plugin which doesn't exists'
|
||||
ErrPluginRemoveNotFound = errors.New("Cannot remove a plugin which doesn't exists")
|
||||
// Context other
|
||||
|
||||
// ErrServeContent returns an error with message: 'While trying to serve content to the client. Trace +specific error'
|
||||
ErrServeContent = errors.New("While trying to serve content to the client. Trace %s")
|
||||
|
||||
// ErrTemplateExecute returns an error with message:'Unable to execute a template. Trace: +specific error'
|
||||
ErrTemplateExecute = errors.New("Unable to execute a template. Trace: %s")
|
||||
|
||||
// ErrFlashNotFound returns an error with message: 'Unable to get flash message. Trace: Cookie does not exists'
|
||||
ErrFlashNotFound = errors.New("Unable to get flash message. Trace: Cookie does not exists")
|
||||
// ErrSessionNil returns an error with message: 'Unable to set session, Config().Session.Provider is nil, please refer to the docs!'
|
||||
ErrSessionNil = errors.New("Unable to set session, Config().Session.Provider is nil, please refer to the docs!")
|
||||
)
|
|
@ -19,7 +19,7 @@ func (e *Error) Error() string {
|
|||
|
||||
// Format returns a formatted new error based on the arguments
|
||||
func (e *Error) Format(args ...interface{}) error {
|
||||
return fmt.Errorf(e.message, args)
|
||||
return fmt.Errorf(e.message, args...)
|
||||
}
|
||||
|
||||
// With does the same thing as Format but it receives an error type which if it's nil it returns a nil error
|
||||
|
@ -33,7 +33,7 @@ func (e *Error) With(err error) error {
|
|||
|
||||
// Return returns the actual error as it is
|
||||
func (e *Error) Return() error {
|
||||
return fmt.Errorf(e.message)
|
||||
return e.Format()
|
||||
}
|
||||
|
||||
// Panic output the message and after panics
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/server"
|
||||
"golang.org/x/net/netutil"
|
||||
)
|
||||
|
||||
|
@ -19,8 +18,8 @@ import (
|
|||
// It may be used directly in the same way as iris.Server, or may
|
||||
// be constructed with the global functions in this package.
|
||||
type Server struct {
|
||||
*server.Server
|
||||
station *iris.Iris
|
||||
*iris.Server
|
||||
station *iris.Framework
|
||||
// Timeout is the duration to allow outstanding requests to survive
|
||||
// before forcefully terminating them.
|
||||
Timeout time.Duration
|
||||
|
@ -67,18 +66,18 @@ type Server struct {
|
|||
connections map[net.Conn]struct{}
|
||||
}
|
||||
|
||||
// Run serves the http.Handler with graceful shutdown enabled.
|
||||
// Run serves the iris.Handler with graceful shutdown enabled.
|
||||
//
|
||||
// timeout is the duration to wait until killing active requests and stopping the server.
|
||||
// If timeout is 0, the server never times out. It waits for all active requests to finish.
|
||||
// we don't pass an iris.RequestHandler , because we need iris.station.server to be setted in order the station.Close() to work
|
||||
func Run(addr string, timeout time.Duration, n *iris.Iris) {
|
||||
func Run(addr string, timeout time.Duration, s *iris.Framework) {
|
||||
srv := &Server{
|
||||
Timeout: timeout,
|
||||
Logger: DefaultLogger(),
|
||||
Logger: s.Logger,
|
||||
station: s,
|
||||
Server: s.NoListen(),
|
||||
}
|
||||
srv.station = n
|
||||
srv.Server = srv.station.PreListen(config.Server{ListeningAddr: addr})
|
||||
|
||||
if err := srv.listenAndServe(); err != nil {
|
||||
if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") {
|
||||
|
@ -92,13 +91,14 @@ func Run(addr string, timeout time.Duration, n *iris.Iris) {
|
|||
//
|
||||
// Unlike Run this version will not exit the program if an error is encountered but will
|
||||
// return it instead.
|
||||
func RunWithErr(addr string, timeout time.Duration, n *iris.Iris) error {
|
||||
func RunWithErr(addr string, timeout time.Duration, s *iris.Framework) error {
|
||||
srv := &Server{
|
||||
Timeout: timeout,
|
||||
Logger: DefaultLogger(),
|
||||
Logger: s.Logger,
|
||||
station: s,
|
||||
Server: s.NoListen(),
|
||||
}
|
||||
srv.station = n
|
||||
srv.Server = srv.station.PreListen(config.Server{ListeningAddr: addr})
|
||||
|
||||
return srv.listenAndServe()
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ func (srv *Server) serve(listener net.Listener) error {
|
|||
|
||||
// Serve with graceful listener.
|
||||
// Execution blocks here until listener.Close() is called, above.
|
||||
srv.station.PostListen()
|
||||
srv.station.NoListen()
|
||||
err := srv.Server.Serve(listener)
|
||||
if err != nil {
|
||||
// If the underlying listening is closed, Serve returns an error
|
||||
|
@ -218,7 +218,7 @@ func (srv *Server) manageConnections(add, remove chan net.Conn, shutdown chan ch
|
|||
case <-kill:
|
||||
for k := range srv.connections {
|
||||
if err := k.Close(); err != nil {
|
||||
srv.log("[IRIS GRACEFUL ERROR] %s", err.Error())
|
||||
srv.log("[IRIS GRACEFUL ERROR]" + err.Error())
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@ -252,7 +252,7 @@ func (srv *Server) handleInterrupt(interrupt chan os.Signal, quitting chan struc
|
|||
close(quitting)
|
||||
srv.Server.DisableKeepalive = true
|
||||
if err := listener.Close(); err != nil {
|
||||
srv.log("[IRIS GRACEFUL ERROR] %s", err.Error())
|
||||
srv.log("[IRIS GRACEFUL ERROR]" + err.Error())
|
||||
}
|
||||
|
||||
if srv.ShutdownInitiated != nil {
|
||||
|
|
119
handler.go
119
handler.go
|
@ -1,119 +0,0 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/valyala/fasthttp/fasthttpadaptor"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
// Handler the main Iris Handler interface.
|
||||
Handler interface {
|
||||
Serve(ctx *Context)
|
||||
}
|
||||
|
||||
// HandlerFunc type is an adapter to allow the use of
|
||||
// ordinary functions as HTTP handlers. If f is a function
|
||||
// with the appropriate signature, HandlerFunc(f) is a
|
||||
// Handler that calls f.
|
||||
HandlerFunc func(*Context)
|
||||
|
||||
// HandlerAPI allow the use of a custom struct as API handler(s) for a particular request path
|
||||
// It's just an interface {}, we keep it here to make things more readable.
|
||||
HandlerAPI interface {
|
||||
// we don't use context.IContext because of some methods as Get() is already inside the IContext interface and conficts with the Get()
|
||||
// we want to use for API.
|
||||
// a valid controller has this form:
|
||||
/*
|
||||
type index struct {
|
||||
*iris.Context
|
||||
}
|
||||
|
||||
// OR
|
||||
type index struct {
|
||||
Context *iris.Context
|
||||
}
|
||||
|
||||
func (i index) Get() {
|
||||
i.Write("Hello from /")
|
||||
}
|
||||
|
||||
func (i index) GetBy(id string) {} // /:namedParameter
|
||||
//POST,PUT,DELETE...
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
//IMiddlewareSupporter is an interface which all routers must implement
|
||||
IMiddlewareSupporter interface {
|
||||
Use(handlers ...Handler)
|
||||
UseFunc(handlersFn ...HandlerFunc)
|
||||
}
|
||||
|
||||
// Middleware is just a slice of Handler []func(c *Context)
|
||||
Middleware []Handler
|
||||
)
|
||||
|
||||
// Serve serves the handler, is like ServeHTTP for Iris
|
||||
func (h HandlerFunc) Serve(ctx *Context) {
|
||||
h(ctx)
|
||||
}
|
||||
|
||||
// ToHandler converts an http.Handler or http.HandlerFunc to an iris.Handler
|
||||
func ToHandler(handler interface{}) Handler {
|
||||
//this is not the best way to do it, but I dont have any options right now.
|
||||
switch handler.(type) {
|
||||
case Handler:
|
||||
//it's already an iris handler
|
||||
return handler.(Handler)
|
||||
case http.Handler:
|
||||
//it's http.Handler
|
||||
h := fasthttpadaptor.NewFastHTTPHandlerFunc(handler.(http.Handler).ServeHTTP)
|
||||
|
||||
return ToHandlerFastHTTP(h)
|
||||
case func(http.ResponseWriter, *http.Request):
|
||||
//it's http.HandlerFunc
|
||||
h := fasthttpadaptor.NewFastHTTPHandlerFunc(handler.(func(http.ResponseWriter, *http.Request)))
|
||||
return ToHandlerFastHTTP(h)
|
||||
default:
|
||||
panic(ErrHandler.Format(handler, handler))
|
||||
}
|
||||
}
|
||||
|
||||
// ToHandlerFunc converts an http.Handler or http.HandlerFunc to an iris.HandlerFunc
|
||||
func ToHandlerFunc(handler interface{}) HandlerFunc {
|
||||
return ToHandler(handler).Serve
|
||||
}
|
||||
|
||||
// ToHandlerFastHTTP converts an fasthttp.RequestHandler to an iris.Handler
|
||||
func ToHandlerFastHTTP(h fasthttp.RequestHandler) Handler {
|
||||
return HandlerFunc((func(ctx *Context) {
|
||||
h(ctx.RequestCtx)
|
||||
}))
|
||||
}
|
||||
|
||||
// ConvertToHandlers accepts list of HandlerFunc and returns list of Handler
|
||||
// this can be renamed to convertToMiddleware also because it returns a list of []Handler which is what Middleware is
|
||||
func ConvertToHandlers(handlersFn []HandlerFunc) []Handler {
|
||||
hlen := len(handlersFn)
|
||||
mlist := make([]Handler, hlen)
|
||||
for i := 0; i < hlen; i++ {
|
||||
mlist[i] = Handler(handlersFn[i])
|
||||
}
|
||||
return mlist
|
||||
}
|
||||
|
||||
// JoinMiddleware uses to create a copy of all middleware and return them in order to use inside the node
|
||||
func JoinMiddleware(middleware1 Middleware, middleware2 Middleware) Middleware {
|
||||
nowLen := len(middleware1)
|
||||
totalLen := nowLen + len(middleware2)
|
||||
// create a new slice of middleware in order to store all handlers, the already handlers(middleware) and the new
|
||||
newMiddleware := make(Middleware, totalLen)
|
||||
//copy the already middleware to the just created
|
||||
copy(newMiddleware, middleware1)
|
||||
//start from there we finish, and store the new middleware too
|
||||
copy(newMiddleware[nowLen:], middleware2)
|
||||
return newMiddleware
|
||||
}
|
228
httperror.go
228
httperror.go
|
@ -1,228 +0,0 @@
|
|||
package iris
|
||||
|
||||
//taken from net/http
|
||||
const (
|
||||
StatusContinue = 100
|
||||
StatusSwitchingProtocols = 101
|
||||
|
||||
StatusOK = 200
|
||||
StatusCreated = 201
|
||||
StatusAccepted = 202
|
||||
StatusNonAuthoritativeInfo = 203
|
||||
StatusNoContent = 204
|
||||
StatusResetContent = 205
|
||||
StatusPartialContent = 206
|
||||
|
||||
StatusMultipleChoices = 300
|
||||
StatusMovedPermanently = 301
|
||||
StatusFound = 302
|
||||
StatusSeeOther = 303
|
||||
StatusNotModified = 304
|
||||
StatusUseProxy = 305
|
||||
StatusTemporaryRedirect = 307
|
||||
|
||||
StatusBadRequest = 400
|
||||
StatusUnauthorized = 401
|
||||
StatusPaymentRequired = 402
|
||||
StatusForbidden = 403
|
||||
StatusNotFound = 404
|
||||
StatusMethodNotAllowed = 405
|
||||
StatusNotAcceptable = 406
|
||||
StatusProxyAuthRequired = 407
|
||||
StatusRequestTimeout = 408
|
||||
StatusConflict = 409
|
||||
StatusGone = 410
|
||||
StatusLengthRequired = 411
|
||||
StatusPreconditionFailed = 412
|
||||
StatusRequestEntityTooLarge = 413
|
||||
StatusRequestURITooLong = 414
|
||||
StatusUnsupportedMediaType = 415
|
||||
StatusRequestedRangeNotSatisfiable = 416
|
||||
StatusExpectationFailed = 417
|
||||
StatusTeapot = 418
|
||||
StatusPreconditionRequired = 428
|
||||
StatusTooManyRequests = 429
|
||||
StatusRequestHeaderFieldsTooLarge = 431
|
||||
StatusUnavailableForLegalReasons = 451
|
||||
|
||||
StatusInternalServerError = 500
|
||||
StatusNotImplemented = 501
|
||||
StatusBadGateway = 502
|
||||
StatusServiceUnavailable = 503
|
||||
StatusGatewayTimeout = 504
|
||||
StatusHTTPVersionNotSupported = 505
|
||||
StatusNetworkAuthenticationRequired = 511
|
||||
)
|
||||
|
||||
var statusText = map[int]string{
|
||||
StatusContinue: "Continue",
|
||||
StatusSwitchingProtocols: "Switching Protocols",
|
||||
|
||||
StatusOK: "OK",
|
||||
StatusCreated: "Created",
|
||||
StatusAccepted: "Accepted",
|
||||
StatusNonAuthoritativeInfo: "Non-Authoritative Information",
|
||||
StatusNoContent: "No Content",
|
||||
StatusResetContent: "Reset Content",
|
||||
StatusPartialContent: "Partial Content",
|
||||
|
||||
StatusMultipleChoices: "Multiple Choices",
|
||||
StatusMovedPermanently: "Moved Permanently",
|
||||
StatusFound: "Found",
|
||||
StatusSeeOther: "See Other",
|
||||
StatusNotModified: "Not Modified",
|
||||
StatusUseProxy: "Use Proxy",
|
||||
StatusTemporaryRedirect: "Temporary Redirect",
|
||||
|
||||
StatusBadRequest: "Bad Request",
|
||||
StatusUnauthorized: "Unauthorized",
|
||||
StatusPaymentRequired: "Payment Required",
|
||||
StatusForbidden: "Forbidden",
|
||||
StatusNotFound: "Not Found",
|
||||
StatusMethodNotAllowed: "Method Not Allowed",
|
||||
StatusNotAcceptable: "Not Acceptable",
|
||||
StatusProxyAuthRequired: "Proxy Authentication Required",
|
||||
StatusRequestTimeout: "Request Timeout",
|
||||
StatusConflict: "Conflict",
|
||||
StatusGone: "Gone",
|
||||
StatusLengthRequired: "Length Required",
|
||||
StatusPreconditionFailed: "Precondition Failed",
|
||||
StatusRequestEntityTooLarge: "Request Entity Too Large",
|
||||
StatusRequestURITooLong: "Request URI Too Long",
|
||||
StatusUnsupportedMediaType: "Unsupported Media Type",
|
||||
StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable",
|
||||
StatusExpectationFailed: "Expectation Failed",
|
||||
StatusTeapot: "I'm a teapot",
|
||||
StatusPreconditionRequired: "Precondition Required",
|
||||
StatusTooManyRequests: "Too Many Requests",
|
||||
StatusRequestHeaderFieldsTooLarge: "Request Header Fields Too Large",
|
||||
StatusUnavailableForLegalReasons: "Unavailable For Legal Reasons",
|
||||
|
||||
StatusInternalServerError: "Internal Server Error",
|
||||
StatusNotImplemented: "Not Implemented",
|
||||
StatusBadGateway: "Bad Gateway",
|
||||
StatusServiceUnavailable: "Service Unavailable",
|
||||
StatusGatewayTimeout: "Gateway Timeout",
|
||||
StatusHTTPVersionNotSupported: "HTTP Version Not Supported",
|
||||
StatusNetworkAuthenticationRequired: "Network Authentication Required",
|
||||
}
|
||||
|
||||
// StatusText returns a text for the HTTP status code. It returns the empty
|
||||
// string if the code is unknown.
|
||||
func StatusText(code int) string {
|
||||
return statusText[code]
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
type (
|
||||
|
||||
// HTTPErrorHandler is just an object which stores a http status code and a handler
|
||||
HTTPErrorHandler struct {
|
||||
code int
|
||||
handler HandlerFunc
|
||||
}
|
||||
|
||||
// HTTPErrorContainer is the struct which contains the handlers which will execute if http error occurs
|
||||
// One struct per Server instance, the meaning of this is that the developer can change the default error message and replace them with his/her own completely custom handlers
|
||||
//
|
||||
// Example of usage:
|
||||
// iris.OnError(405, func (ctx *iris.Context){ c.SendStatus(405,"Method not allowed!!!")})
|
||||
// and inside the handler which you have access to the current Context:
|
||||
// ctx.EmitError(405)
|
||||
HTTPErrorContainer struct {
|
||||
// Errors contains all the httperrorhandlers
|
||||
Errors []*HTTPErrorHandler
|
||||
}
|
||||
)
|
||||
|
||||
// HTTPErrorHandlerFunc creates a handler which is responsible to send a particular error to the client
|
||||
func HTTPErrorHandlerFunc(statusCode int, message string) HandlerFunc {
|
||||
return func(ctx *Context) {
|
||||
ctx.SetStatusCode(statusCode)
|
||||
ctx.SetBodyString(message)
|
||||
}
|
||||
}
|
||||
|
||||
// GetCode returns the http status code value
|
||||
func (e *HTTPErrorHandler) GetCode() int {
|
||||
return e.code
|
||||
}
|
||||
|
||||
// GetHandler returns the handler which is type of HandlerFunc
|
||||
func (e *HTTPErrorHandler) GetHandler() HandlerFunc {
|
||||
return e.handler
|
||||
}
|
||||
|
||||
// SetHandler sets the handler (type of HandlerFunc) to this particular ErrorHandler
|
||||
func (e *HTTPErrorHandler) SetHandler(h HandlerFunc) {
|
||||
e.handler = h
|
||||
}
|
||||
|
||||
// defaultHTTPErrors creates and returns an instance of HTTPErrorContainer with default handlers
|
||||
func defaultHTTPErrors() *HTTPErrorContainer {
|
||||
httperrors := new(HTTPErrorContainer)
|
||||
httperrors.Errors = make([]*HTTPErrorHandler, 0)
|
||||
httperrors.OnError(StatusNotFound, HTTPErrorHandlerFunc(StatusNotFound, statusText[StatusNotFound]))
|
||||
httperrors.OnError(StatusInternalServerError, HTTPErrorHandlerFunc(StatusInternalServerError, statusText[StatusInternalServerError]))
|
||||
return httperrors
|
||||
}
|
||||
|
||||
// GetByCode returns the error handler by it's http status code
|
||||
func (he *HTTPErrorContainer) GetByCode(httpStatus int) *HTTPErrorHandler {
|
||||
if he != nil {
|
||||
for _, h := range he.Errors {
|
||||
if h.GetCode() == httpStatus {
|
||||
return h
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnError Registers a handler for a specific http error status
|
||||
func (he *HTTPErrorContainer) OnError(httpStatus int, handler HandlerFunc) {
|
||||
if httpStatus == StatusOK {
|
||||
return
|
||||
}
|
||||
|
||||
if errH := he.GetByCode(httpStatus); errH != nil {
|
||||
|
||||
errH.SetHandler(handler)
|
||||
} else {
|
||||
he.Errors = append(he.Errors, &HTTPErrorHandler{code: httpStatus, handler: handler})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///TODO: the errors must have .Next too, as middlewares inside the Context, if I let it as it is then we have problem
|
||||
// we cannot set a logger and a custom handler at one error because now the error handler takes only one handelrFunc and executes there from here...
|
||||
|
||||
// EmitError executes the handler of the given error http status code
|
||||
func (he *HTTPErrorContainer) EmitError(errCode int, ctx *Context) {
|
||||
ctx.ResetBody()
|
||||
if errHandler := he.GetByCode(errCode); errHandler != nil {
|
||||
ctx.SetStatusCode(errCode) // for any case, user can change it after if want to
|
||||
errHandler.GetHandler().Serve(ctx)
|
||||
} else {
|
||||
//if no error is registed, then register it with the default http error text, and re-run the Emit
|
||||
he.OnError(errCode, func(c *Context) {
|
||||
c.SetStatusCode(errCode)
|
||||
c.SetBodyString(StatusText(errCode))
|
||||
})
|
||||
he.EmitError(errCode, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// OnNotFound sets the handler for http status 404,
|
||||
// default is a response with text: 'Not Found' and status: 404
|
||||
func (he *HTTPErrorContainer) OnNotFound(handlerFunc HandlerFunc) {
|
||||
he.OnError(StatusNotFound, handlerFunc)
|
||||
}
|
||||
|
||||
// OnPanic sets the handler for http status 500,
|
||||
// default is a response with text: The server encountered an unexpected condition which prevented it from fulfilling the request. and status: 500
|
||||
func (he *HTTPErrorContainer) OnPanic(handlerFunc HandlerFunc) {
|
||||
he.OnError(StatusInternalServerError, handlerFunc)
|
||||
}
|
193
initiatory.go
Normal file
193
initiatory.go
Normal file
|
@ -0,0 +1,193 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/websocket"
|
||||
|
||||
"github.com/kataras/iris/mail"
|
||||
"github.com/kataras/iris/render/rest"
|
||||
"github.com/kataras/iris/render/template"
|
||||
"github.com/kataras/iris/sessions"
|
||||
///NOTE: register the session providers, but the s.Config.Sessions.Provider will be used only, if this empty then sessions are disabled.
|
||||
_ "github.com/kataras/iris/sessions/providers/memory"
|
||||
_ "github.com/kataras/iris/sessions/providers/redis"
|
||||
)
|
||||
|
||||
// Default entry, use it with iris.$anyPublicFunc
|
||||
var (
|
||||
Default *Framework
|
||||
Config *config.Iris
|
||||
Logger *logger.Logger
|
||||
Plugins PluginContainer
|
||||
Websocket websocket.Server
|
||||
HTTPServer *Server
|
||||
)
|
||||
|
||||
func init() {
|
||||
Default = New()
|
||||
Config = Default.Config
|
||||
Logger = Default.Logger
|
||||
Plugins = Default.Plugins
|
||||
Websocket = Default.Websocket
|
||||
HTTPServer = Default.HTTPServer
|
||||
}
|
||||
|
||||
const (
|
||||
/* conversional */
|
||||
|
||||
// HTMLEngine conversion for config.HTMLEngine
|
||||
HTMLEngine = config.HTMLEngine
|
||||
// PongoEngine conversion for config.PongoEngine
|
||||
PongoEngine = config.PongoEngine
|
||||
// MarkdownEngine conversion for config.MarkdownEngine
|
||||
MarkdownEngine = config.MarkdownEngine
|
||||
// JadeEngine conversion for config.JadeEngine
|
||||
JadeEngine = config.JadeEngine
|
||||
// AmberEngine conversion for config.AmberEngine
|
||||
AmberEngine = config.AmberEngine
|
||||
|
||||
// DefaultEngine conversion for config.DefaultEngine
|
||||
DefaultEngine = config.DefaultEngine
|
||||
// NoEngine conversion for config.NoEngine
|
||||
NoEngine = config.NoEngine
|
||||
// NoLayout to disable layout for a particular template file
|
||||
// conversion for config.NoLayout
|
||||
NoLayout = config.NoLayout
|
||||
|
||||
/* end conversional */
|
||||
)
|
||||
|
||||
// Framework is our God |\| Google.Search('Greek mythology Iris')
|
||||
//
|
||||
// Implements the FrameworkAPI
|
||||
type Framework struct {
|
||||
*muxAPI
|
||||
rest *rest.Render
|
||||
templates *template.Template
|
||||
sessions *sessions.Manager
|
||||
mailer mail.Service
|
||||
// fields which are useful to the user/dev
|
||||
HTTPServer *Server
|
||||
Config *config.Iris
|
||||
Logger *logger.Logger
|
||||
Plugins PluginContainer
|
||||
Websocket websocket.Server
|
||||
}
|
||||
|
||||
// New creates and returns a new Iris station aka Framework.
|
||||
//
|
||||
// Receives an optional config.Iris as parameter
|
||||
// If empty then config.Default() is used instead
|
||||
func New(cfg ...config.Iris) *Framework {
|
||||
c := config.Default().Merge(cfg)
|
||||
|
||||
// we always use 's' no 'f' because 's' is easier for me to remember because of 'station'
|
||||
// some things never change :)
|
||||
s := &Framework{Config: &c}
|
||||
{
|
||||
///NOTE: set all with s.Config pointer
|
||||
// set the Logger
|
||||
s.Logger = logger.New(s.Config.Logger)
|
||||
// set the plugin container
|
||||
s.Plugins = &pluginContainer{logger: s.Logger}
|
||||
// set the websocket server
|
||||
s.Websocket = websocket.NewServer(s.Config.Websocket)
|
||||
// set the servemux, which will provide us the public API also, with its context pool
|
||||
mux := newServeMux(sync.Pool{New: func() interface{} { return &Context{framework: s} }})
|
||||
// set the public router API (and party)
|
||||
s.muxAPI = &muxAPI{mux: mux, relativePath: "/"}
|
||||
// set the server
|
||||
s.HTTPServer = newServer(&s.Config.Server)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Framework) initialize() {
|
||||
// set sessions
|
||||
if s.Config.Sessions.Provider != "" {
|
||||
s.sessions = sessions.New(s.Config.Sessions)
|
||||
}
|
||||
|
||||
//set the rest
|
||||
s.rest = rest.New(s.Config.Render.Rest)
|
||||
|
||||
//set mail and templates if not already setted
|
||||
s.prepareMailer()
|
||||
s.prepareTemplates()
|
||||
|
||||
// listen to websocket connections
|
||||
websocket.RegisterServer(s, s.Websocket, s.Logger)
|
||||
|
||||
// prepare the mux
|
||||
s.mux.setCorrectPath(!s.Config.DisablePathCorrection)
|
||||
s.mux.setEscapePath(!s.Config.DisablePathEscape)
|
||||
s.mux.setHost(s.HTTPServer.VirtualHostname())
|
||||
// set the debug profiling handlers if ProfilePath is setted
|
||||
if debugPath := s.Config.ProfilePath; debugPath != "" {
|
||||
s.Handle(MethodGet, debugPath+"/*action", profileMiddleware(debugPath)...)
|
||||
}
|
||||
|
||||
// prepare the server
|
||||
s.HTTPServer.SetHandler(s.mux)
|
||||
|
||||
if s.Config.MaxRequestBodySize > 0 {
|
||||
s.HTTPServer.MaxRequestBodySize = int(s.Config.MaxRequestBodySize)
|
||||
}
|
||||
}
|
||||
|
||||
// prepareMailer sets the mailer if not nil, we make this check because of .SendMail, which can be called before Listen
|
||||
func (s *Framework) prepareMailer() {
|
||||
// prepare the mail service
|
||||
if s.mailer == nil {
|
||||
s.mailer = mail.New(s.Config.Mail)
|
||||
}
|
||||
}
|
||||
|
||||
// prepareTemplates sets the templates if not nil, we make this check because of .TemplateString, which can be called before Listen
|
||||
func (s *Framework) prepareTemplates() {
|
||||
// prepare the templates
|
||||
if s.templates == nil {
|
||||
// These functions are directly contact with Iris' functionality.
|
||||
funcs := map[string]interface{}{
|
||||
"url": s.URL,
|
||||
"urlpath": s.Path,
|
||||
}
|
||||
|
||||
template.RegisterSharedFuncs(funcs)
|
||||
|
||||
s.templates = template.New(s.Config.Render.Template)
|
||||
}
|
||||
}
|
||||
|
||||
// openServer is internal method, open the server with specific options passed by the Listen and ListenTLS
|
||||
// it's a blocking func
|
||||
func (s *Framework) openServer() (err error) {
|
||||
s.initialize()
|
||||
s.Plugins.DoPreListen(s)
|
||||
if err = s.HTTPServer.open(); err == nil {
|
||||
// print the banner
|
||||
if !s.Config.DisableBanner {
|
||||
s.Logger.PrintBanner(banner,
|
||||
fmt.Sprintf("%s: Running at %s\n", time.Now().Format(config.TimeFormat),
|
||||
s.HTTPServer.Host()))
|
||||
}
|
||||
s.Plugins.DoPostListen(s)
|
||||
ch := make(chan os.Signal)
|
||||
<-ch
|
||||
s.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// closeServer is used to close the tcp listener from the server, returns an error
|
||||
func (s *Framework) closeServer() error {
|
||||
s.Plugins.DoPreClose(s)
|
||||
return s.HTTPServer.close()
|
||||
}
|
|
@ -1,412 +0,0 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/mail"
|
||||
"github.com/kataras/iris/render/rest"
|
||||
"github.com/kataras/iris/render/template"
|
||||
"github.com/kataras/iris/server"
|
||||
"github.com/kataras/iris/websocket"
|
||||
)
|
||||
|
||||
// DefaultIris in order to use iris.Get(...,...) we need a default Iris on the package level
|
||||
var DefaultIris = New()
|
||||
|
||||
// Listen starts the standalone http server
|
||||
// which listens to the addr parameter which as the form of
|
||||
// host:port or just port
|
||||
//
|
||||
// It panics on error if you need a func to return an error use the ListenWithErr
|
||||
// ex: iris.Listen(":8080")
|
||||
func Listen(addr string) {
|
||||
DefaultIris.Listen(addr)
|
||||
}
|
||||
|
||||
// ListenWithErr starts the standalone http server
|
||||
// which listens to the addr parameter which as the form of
|
||||
// host:port or just port
|
||||
//
|
||||
// It returns an error you are responsible how to handle this
|
||||
// if you need a func to panic on error use the Listen
|
||||
// ex: log.Fatal(iris.ListenWithErr(":8080"))
|
||||
func ListenWithErr(addr string) error {
|
||||
return DefaultIris.ListenWithErr(addr)
|
||||
}
|
||||
|
||||
// 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 or just port
|
||||
//
|
||||
// It panics on error if you need a func to return an error use the ListenTLSWithErr
|
||||
// ex: iris.ListenTLS(":8080","yourfile.cert","yourfile.key")
|
||||
func ListenTLS(addr string, certFile, keyFile string) {
|
||||
DefaultIris.ListenTLS(addr, certFile, keyFile)
|
||||
}
|
||||
|
||||
// ListenTLSWithErr 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 or just port
|
||||
//
|
||||
// It returns an error you are responsible how to handle this
|
||||
// if you need a func to panic on error use the ListenTLS
|
||||
// ex: log.Fatal(iris.ListenTLSWithErr(":8080","yourfile.cert","yourfile.key"))
|
||||
func ListenTLSWithErr(addr string, certFile, keyFile string) error {
|
||||
return DefaultIris.ListenTLSWithErr(addr, certFile, keyFile)
|
||||
}
|
||||
|
||||
// Close is used to close the net.Listener of the standalone http server which has already running via .Listen
|
||||
func Close() { DefaultIris.Close() }
|
||||
|
||||
// Router implementation
|
||||
|
||||
// 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 Party(path string, handlersFn ...HandlerFunc) IParty {
|
||||
return DefaultIris.Party(path, handlersFn...)
|
||||
}
|
||||
|
||||
// Handle registers a route to the server's router
|
||||
// if empty method is passed then registers handler(s) for all methods, same as .Any
|
||||
func Handle(method string, registedPath string, handlers ...Handler) IRoute {
|
||||
return DefaultIris.Handle(method, registedPath, handlers...)
|
||||
}
|
||||
|
||||
// HandleFunc registers a route with a method, path string, and a handler
|
||||
func HandleFunc(method string, path string, handlersFn ...HandlerFunc) IRoute {
|
||||
return DefaultIris.HandleFunc(method, path, handlersFn...)
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
// example: subdomains_2
|
||||
func Wildcard(method string, registedPath string, handlersFn ...HandlerFunc) {
|
||||
DefaultIris.Wildcard(method, registedPath, handlersFn...)
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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 API(registedPath string, controller HandlerAPI, middlewares ...HandlerFunc) error {
|
||||
return DefaultIris.API(registedPath, controller, middlewares...)
|
||||
}
|
||||
|
||||
// Use appends a middleware to the route or to the router if it's called from router
|
||||
func Use(handlers ...Handler) {
|
||||
DefaultIris.Use(handlers...)
|
||||
}
|
||||
|
||||
// UseFunc same as Use but it accepts/receives ...HandlerFunc instead of ...Handler
|
||||
// form of acceptable: func(c *iris.Context){//first middleware}, func(c *iris.Context){//second middleware}
|
||||
func UseFunc(handlersFn ...HandlerFunc) {
|
||||
DefaultIris.UseFunc(handlersFn...)
|
||||
}
|
||||
|
||||
// Get registers a route for the Get http method
|
||||
func Get(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return DefaultIris.Get(path, handlersFn...)
|
||||
}
|
||||
|
||||
// Post registers a route for the Post http method
|
||||
func Post(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return DefaultIris.Post(path, handlersFn...)
|
||||
}
|
||||
|
||||
// Put registers a route for the Put http method
|
||||
func Put(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return DefaultIris.Put(path, handlersFn...)
|
||||
}
|
||||
|
||||
// Delete registers a route for the Delete http method
|
||||
func Delete(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return DefaultIris.Delete(path, handlersFn...)
|
||||
}
|
||||
|
||||
// Connect registers a route for the Connect http method
|
||||
func Connect(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return DefaultIris.Connect(path, handlersFn...)
|
||||
}
|
||||
|
||||
// Head registers a route for the Head http method
|
||||
func Head(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return DefaultIris.Head(path, handlersFn...)
|
||||
}
|
||||
|
||||
// Options registers a route for the Options http method
|
||||
func Options(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return DefaultIris.Options(path, handlersFn...)
|
||||
}
|
||||
|
||||
// Patch registers a route for the Patch http method
|
||||
func Patch(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return DefaultIris.Patch(path, handlersFn...)
|
||||
}
|
||||
|
||||
// Trace registers a route for the Trace http methodd
|
||||
func Trace(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return DefaultIris.Trace(path, handlersFn...)
|
||||
}
|
||||
|
||||
// Any registers a route for ALL of the http methods (Get,Post,Put,Head,Patch,Options,Connect,Delete)
|
||||
func Any(path string, handlersFn ...HandlerFunc) []IRoute {
|
||||
return DefaultIris.Any(path, handlersFn...)
|
||||
}
|
||||
|
||||
// RouteByName returns a route by its name,if not found then returns a route with empty path
|
||||
// Note that the searching is case-sensitive
|
||||
func RouteByName(lookUpName string) IRoute {
|
||||
return DefaultIris.RouteByName(lookUpName)
|
||||
}
|
||||
|
||||
// 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 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 StaticHandlerFunc(systemPath string, stripSlashes int, compress bool, generateIndexPages bool, indexNames []string) HandlerFunc {
|
||||
return DefaultIris.StaticHandlerFunc(systemPath, stripSlashes, compress, generateIndexPages, indexNames)
|
||||
}
|
||||
|
||||
// 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 Static(reqPath string, systemPath string, stripSlashes int) {
|
||||
DefaultIris.Static(reqPath, systemPath, stripSlashes)
|
||||
}
|
||||
|
||||
// StaticFS registers a route which serves a system directory
|
||||
// generates an index page which list all files
|
||||
// uses compression which file cache, 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 StaticFS(reqPath string, systemPath string, stripSlashes int) {
|
||||
DefaultIris.StaticFS(reqPath, systemPath, stripSlashes)
|
||||
}
|
||||
|
||||
// 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: ""
|
||||
func StaticWeb(reqPath string, systemPath string, stripSlashes int) {
|
||||
DefaultIris.StaticWeb(reqPath, systemPath, stripSlashes)
|
||||
}
|
||||
|
||||
// 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 StaticServe(systemPath string, requestPath ...string) {
|
||||
DefaultIris.StaticServe(systemPath, requestPath...)
|
||||
}
|
||||
|
||||
// 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 Favicon(favPath string, requestPath ...string) error {
|
||||
return DefaultIris.Favicon(favPath)
|
||||
}
|
||||
|
||||
// StaticContent serves bytes, memory cached, on the reqPath
|
||||
func StaticContent(reqPath string, contentType string, content []byte) {
|
||||
DefaultIris.StaticContent(reqPath, contentType, content)
|
||||
}
|
||||
|
||||
// OnError Registers a handler for a specific http error status
|
||||
func OnError(httpStatus int, handler HandlerFunc) {
|
||||
DefaultIris.OnError(httpStatus, handler)
|
||||
}
|
||||
|
||||
// EmitError executes the handler of the given error http status code
|
||||
func EmitError(httpStatus int, ctx *Context) {
|
||||
DefaultIris.EmitError(httpStatus, ctx)
|
||||
}
|
||||
|
||||
// OnNotFound sets the handler for http status 404,
|
||||
// default is a response with text: 'Not Found' and status: 404
|
||||
func OnNotFound(handlerFunc HandlerFunc) {
|
||||
DefaultIris.OnNotFound(handlerFunc)
|
||||
}
|
||||
|
||||
// OnPanic sets the handler for http status 500,
|
||||
// default is a response with text: The server encountered an unexpected condition which prevented it from fulfilling the request. and status: 500
|
||||
func OnPanic(handlerFunc HandlerFunc) {
|
||||
DefaultIris.OnPanic(handlerFunc)
|
||||
}
|
||||
|
||||
// ***********************
|
||||
// Export DefaultIris's exported properties
|
||||
// ***********************
|
||||
|
||||
// Server returns the server
|
||||
func Server() *server.Server {
|
||||
return DefaultIris.Server()
|
||||
}
|
||||
|
||||
// Plugins returns the plugin container
|
||||
func Plugins() *PluginContainer {
|
||||
return DefaultIris.Plugins()
|
||||
}
|
||||
|
||||
// Config returns the configs
|
||||
func Config() *config.Iris {
|
||||
return DefaultIris.Config()
|
||||
}
|
||||
|
||||
// Logger returns the logger
|
||||
func Logger() *logger.Logger {
|
||||
return DefaultIris.Logger()
|
||||
}
|
||||
|
||||
// Rest returns the rest render
|
||||
func Rest() *rest.Render {
|
||||
return DefaultIris.Rest()
|
||||
}
|
||||
|
||||
// Templates returns the template render
|
||||
func Templates() *template.Template {
|
||||
return DefaultIris.Templates()
|
||||
}
|
||||
|
||||
// Websocket returns the websocket server
|
||||
func Websocket() websocket.Server {
|
||||
return DefaultIris.Websocket()
|
||||
}
|
||||
|
||||
// Mail returns the mail sender service
|
||||
func Mail() mail.Service {
|
||||
return DefaultIris.Mail()
|
||||
}
|
|
@ -17,7 +17,7 @@ var (
|
|||
|
||||
// Logger the logger
|
||||
type Logger struct {
|
||||
config config.Logger
|
||||
config *config.Logger
|
||||
underline *color.Color
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ func attr(sgr int) color.Attribute {
|
|||
func New(c config.Logger) *Logger {
|
||||
color.Output = colorable.NewColorable(c.Out)
|
||||
|
||||
l := &Logger{c, color.New(attr(c.ColorBgDefault), attr(c.ColorFgDefault), color.Bold)}
|
||||
l := &Logger{&c, color.New(attr(c.ColorBgDefault), attr(c.ColorFgDefault), color.Bold)}
|
||||
return l
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ func (l *Logger) IsEnabled() bool {
|
|||
// ResetColors sets the colors to the default
|
||||
// this func is called every time a success, info, warning, or danger message is printed
|
||||
func (l *Logger) ResetColors() {
|
||||
l.underline.Add(attr(l.config.ColorBgDefault), attr(l.config.ColorFgBanner), color.Bold)
|
||||
l.underline.Add(attr(l.config.ColorBgDefault), attr(l.config.ColorFgDefault))
|
||||
}
|
||||
|
||||
// PrintBanner prints a text (banner) with BannerFgColor, BannerBgColor and a success message at the end
|
||||
|
@ -83,6 +83,7 @@ func (l *Logger) Printf(format string, a ...interface{}) {
|
|||
// Arguments are handled in the manner of fmt.Print.
|
||||
func (l *Logger) Print(a interface{}) {
|
||||
if !l.config.Disabled {
|
||||
l.ResetColors()
|
||||
l.Printf("%#v", a)
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +126,6 @@ func (l *Logger) Sucessf(format string, a ...interface{}) {
|
|||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorBgSuccess), attr(l.config.ColorFgSuccess))
|
||||
l.Printf(format, a...)
|
||||
l.ResetColors()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,6 @@ func (l *Logger) Infof(format string, a ...interface{}) {
|
|||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorBgInfo), attr(l.config.ColorFgInfo))
|
||||
l.Printf(format, a...)
|
||||
l.ResetColors()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +144,6 @@ func (l *Logger) Warningf(format string, a ...interface{}) {
|
|||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorBgWarning), attr(l.config.ColorFgWarning))
|
||||
l.Printf(format, a...)
|
||||
l.ResetColors()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,7 +153,6 @@ func (l *Logger) Dangerf(format string, a ...interface{}) {
|
|||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorBgDanger), attr(l.config.ColorFgDanger))
|
||||
l.Printf(format, a...)
|
||||
l.ResetColors()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,6 +162,5 @@ func (l *Logger) Otherf(format string, a ...interface{}) {
|
|||
if !l.config.Disabled {
|
||||
l.underline.Add(attr(l.config.ColorBgOther), attr(l.config.ColorFgOther))
|
||||
l.Printf(format, a...)
|
||||
l.ResetColors()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,23 +6,26 @@ import (
|
|||
"net/mail"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
var buf = utils.NewBufferPool(64)
|
||||
var once sync.Once
|
||||
|
||||
type (
|
||||
// Service is the interface which mail sender should implement
|
||||
Service interface {
|
||||
// Send sends a mail to recipients
|
||||
// the body can be html also
|
||||
Send(to []string, subject, body string) error
|
||||
Send(string, string, ...string) error
|
||||
UpdateConfig(config.Mail)
|
||||
}
|
||||
|
||||
mailer struct {
|
||||
config config.Mail
|
||||
config *config.Mail
|
||||
fromAddr mail.Address
|
||||
auth smtp.Auth
|
||||
authenticated bool
|
||||
|
@ -31,44 +34,46 @@ type (
|
|||
|
||||
// New creates and returns a new Service
|
||||
func New(cfg config.Mail) Service {
|
||||
m := &mailer{config: cfg}
|
||||
|
||||
m := &mailer{config: &cfg}
|
||||
if cfg.FromAlias == "" {
|
||||
if !cfg.UseCommand && cfg.Username != "" && strings.Contains(cfg.Username, "@") {
|
||||
m.fromAddr = mail.Address{cfg.Username[0:strings.IndexByte(cfg.Username, '@')], cfg.Username}
|
||||
m.fromAddr = mail.Address{Name: cfg.Username[0:strings.IndexByte(cfg.Username, '@')], Address: cfg.Username}
|
||||
}
|
||||
} else {
|
||||
m.fromAddr = mail.Address{cfg.FromAlias, cfg.Username}
|
||||
m.fromAddr = mail.Address{Name: cfg.FromAlias, Address: cfg.Username}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *mailer) UpdateConfig(cfg config.Mail) {
|
||||
m.config = &cfg
|
||||
}
|
||||
|
||||
// Send sends a mail to recipients
|
||||
// the body can be html also
|
||||
func (m *mailer) Send(to []string, subject, body string) error {
|
||||
func (m *mailer) Send(subject string, body string, to ...string) error {
|
||||
if m.config.UseCommand {
|
||||
return m.sendCmd(to, subject, body)
|
||||
return m.sendCmd(subject, body, to)
|
||||
}
|
||||
|
||||
return m.sendSMTP(to, subject, body)
|
||||
return m.sendSMTP(subject, body, to)
|
||||
}
|
||||
|
||||
func (m *mailer) sendSMTP(to []string, subject, body string) error {
|
||||
func (m *mailer) sendSMTP(subject string, body string, to []string) error {
|
||||
buffer := buf.Get()
|
||||
defer buf.Put(buffer)
|
||||
|
||||
if !m.authenticated {
|
||||
if m.config.Username == "" || m.config.Password == "" || m.config.Host == "" || m.config.Port <= 0 {
|
||||
cfg := m.config
|
||||
if cfg.Username == "" || cfg.Password == "" || cfg.Host == "" || cfg.Port <= 0 {
|
||||
return fmt.Errorf("Username, Password, Host & Port cannot be empty when using SMTP!")
|
||||
}
|
||||
m.auth = smtp.PlainAuth("", m.config.Username, m.config.Password, m.config.Host)
|
||||
m.auth = smtp.PlainAuth("", cfg.Username, cfg.Password, cfg.Host)
|
||||
m.authenticated = true
|
||||
}
|
||||
|
||||
fullhost := fmt.Sprintf("%s:%d", m.config.Host, m.config.Port)
|
||||
|
||||
/* START: This one helped me https://gist.github.com/andelf/5004821 */
|
||||
header := make(map[string]string)
|
||||
header["From"] = m.fromAddr.String()
|
||||
header["To"] = strings.Join(to, ",")
|
||||
|
@ -83,8 +88,6 @@ func (m *mailer) sendSMTP(to []string, subject, body string) error {
|
|||
}
|
||||
message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body))
|
||||
|
||||
/* END */
|
||||
|
||||
return smtp.SendMail(
|
||||
fmt.Sprintf(fullhost),
|
||||
m.auth,
|
||||
|
@ -94,14 +97,27 @@ func (m *mailer) sendSMTP(to []string, subject, body string) error {
|
|||
)
|
||||
}
|
||||
|
||||
func (m *mailer) sendCmd(to []string, subject, body string) error {
|
||||
func (m *mailer) sendCmd(subject string, body string, to []string) error {
|
||||
buffer := buf.Get()
|
||||
defer buf.Put(buffer)
|
||||
|
||||
cmd := utils.CommandBuilder("mail", "-s", subject, strings.Join(to, ","))
|
||||
cmd.AppendArguments("-a", "Content-type: text/html") //always html on
|
||||
header := make(map[string]string)
|
||||
header["To"] = strings.Join(to, ",")
|
||||
header["Subject"] = subject
|
||||
header["MIME-Version"] = "1.0"
|
||||
header["Content-Type"] = "text/html; charset=\"utf-8\""
|
||||
header["Content-Transfer-Encoding"] = "base64"
|
||||
|
||||
message := ""
|
||||
for k, v := range header {
|
||||
message += fmt.Sprintf("%s: %s\r\n", k, v)
|
||||
}
|
||||
message += "\r\n" + base64.StdEncoding.EncodeToString([]byte(body))
|
||||
buffer.WriteString(message)
|
||||
// fix by @qskousen
|
||||
cmd := utils.CommandBuilder("sendmail", "-F", m.fromAddr.Name, "-f", m.fromAddr.Address, "-t")
|
||||
|
||||
cmd.Stdin = buffer
|
||||
_, err := cmd.Output()
|
||||
_, err := cmd.CombinedOutput()
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ func (b *basicAuthMiddleware) Serve(ctx *iris.Context) {
|
|||
auth.logged = true
|
||||
}
|
||||
|
||||
if time.Now().Before(auth.expires) {
|
||||
if time.Now().After(auth.expires) {
|
||||
b.askForCredentials(ctx) // ask for authentication again
|
||||
return
|
||||
}
|
||||
|
|
|
@ -11,11 +11,9 @@ import (
|
|||
|
||||
// Options are the options of the logger middlweare
|
||||
// contains 5 bools
|
||||
// Latency, Status, IP, Method, Path
|
||||
// Status, IP, Method, Path, EnableColors
|
||||
// if set to true then these will print
|
||||
type Options struct {
|
||||
// Latency displays latency (bool)
|
||||
Latency bool
|
||||
// Status displays status code (bool)
|
||||
Status bool
|
||||
// IP displays request's remote address (bool)
|
||||
|
@ -24,11 +22,13 @@ type Options struct {
|
|||
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, true}
|
||||
return Options{true, true, true, true, false}
|
||||
}
|
||||
|
||||
type loggerMiddleware struct {
|
||||
|
@ -45,17 +45,13 @@ func (l *loggerMiddleware) Serve(ctx *iris.Context) {
|
|||
path = ctx.PathString()
|
||||
method = ctx.MethodString()
|
||||
|
||||
if l.options.Latency {
|
||||
startTime = time.Now()
|
||||
}
|
||||
startTime = time.Now()
|
||||
|
||||
ctx.Next()
|
||||
if l.options.Latency {
|
||||
//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)
|
||||
}
|
||||
//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())
|
||||
|
@ -74,14 +70,18 @@ func (l *loggerMiddleware) Serve(ctx *iris.Context) {
|
|||
}
|
||||
|
||||
//finally print the logs
|
||||
if l.options.Latency {
|
||||
l.Otherf("%s %v %4v %s %s %s \n", date, status, latency, ip, method, path)
|
||||
} else {
|
||||
l.Otherf("%s %v %s %s %s \n", date, status, ip, method, path)
|
||||
}
|
||||
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...)
|
||||
}
|
||||
}
|
||||
|
||||
// Default returns the logger middleware as Handler with the default settings
|
||||
func New(theLogger *logger.Logger, options ...Options) iris.HandlerFunc {
|
||||
if theLogger == nil {
|
||||
|
|
123
npm/npm.go
123
npm/npm.go
|
@ -1,123 +0,0 @@
|
|||
package npm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
// NodeModules is the path of the root npm modules
|
||||
// Ex: C:\\Users\\kataras\\AppData\\Roaming\\npm\\node_modules
|
||||
NodeModules string
|
||||
)
|
||||
|
||||
type (
|
||||
// Result holds Message and Error, if error != nil then the npm command has failed
|
||||
Result struct {
|
||||
// Message the message (string)
|
||||
Message string
|
||||
// Error the error (if any)
|
||||
Error error
|
||||
}
|
||||
)
|
||||
|
||||
// init sets the root directory for the node_modules
|
||||
func init() {
|
||||
NodeModules = utils.MustCommand("npm", "root", "-g") //here it ends with \n we have to remove it
|
||||
NodeModules = NodeModules[0 : len(NodeModules)-1]
|
||||
}
|
||||
|
||||
func success(output string, a ...interface{}) Result {
|
||||
return Result{fmt.Sprintf(output, a...), nil}
|
||||
}
|
||||
|
||||
func fail(errMsg string, a ...interface{}) Result {
|
||||
return Result{"", fmt.Errorf("\n"+errMsg, a...)}
|
||||
}
|
||||
|
||||
// Output returns the error message if result.Error exists, otherwise returns the result.Message
|
||||
func (res Result) Output() (out string) {
|
||||
if res.Error != nil {
|
||||
out = res.Error.Error()
|
||||
} else {
|
||||
out = res.Message
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Install installs a module
|
||||
func Install(moduleName string) Result {
|
||||
finish := make(chan bool)
|
||||
|
||||
go func() {
|
||||
print("\n|")
|
||||
print("_")
|
||||
print("|")
|
||||
|
||||
for {
|
||||
select {
|
||||
case v := <-finish:
|
||||
{
|
||||
if v {
|
||||
print("\010\010\010") //remove the loading chars
|
||||
close(finish)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
default:
|
||||
print("\010\010-")
|
||||
time.Sleep(time.Second / 2)
|
||||
print("\010\\")
|
||||
time.Sleep(time.Second / 2)
|
||||
print("\010|")
|
||||
time.Sleep(time.Second / 2)
|
||||
print("\010/")
|
||||
time.Sleep(time.Second / 2)
|
||||
print("\010-")
|
||||
time.Sleep(time.Second / 2)
|
||||
print("|")
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
out, err := utils.Command("npm", "install", moduleName, "-g")
|
||||
finish <- true
|
||||
if err != nil {
|
||||
return fail("Error installing module %s. Trace: %s", moduleName, err.Error())
|
||||
}
|
||||
|
||||
return success("\n%s installed %s", moduleName, out)
|
||||
|
||||
}
|
||||
|
||||
// Unistall removes a module
|
||||
func Unistall(moduleName string) Result {
|
||||
out, err := utils.Command("npm", "unistall", "-g", moduleName)
|
||||
if err != nil {
|
||||
return fail("Error unstalling module %s. Trace: %s", moduleName, err.Error())
|
||||
}
|
||||
return success("\n %s unistalled %s", moduleName, out)
|
||||
|
||||
}
|
||||
|
||||
// Abs returns the absolute path of the global node_modules directory + relative
|
||||
func Abs(relativePath string) string {
|
||||
return NodeModules + utils.PathSeparator + strings.Replace(relativePath, "/", utils.PathSeparator, -1)
|
||||
}
|
||||
|
||||
// Exists returns true if a module exists
|
||||
// here we have two options
|
||||
//1 . search by command something like npm -ls -g --depth=x
|
||||
//2. search on files, we choose the second
|
||||
func Exists(executableRelativePath string) bool {
|
||||
execAbsPath := Abs(executableRelativePath)
|
||||
if execAbsPath == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return utils.Exists(execAbsPath)
|
||||
}
|
642
party.go
642
party.go
|
@ -1,642 +0,0 @@
|
|||
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 {
|
||||
Handle(string, string, ...Handler) IRoute
|
||||
HandleFunc(string, string, ...HandlerFunc) IRoute
|
||||
Wildcard(string, string, ...HandlerFunc)
|
||||
API(path string, controller HandlerAPI, middlewares ...HandlerFunc) error
|
||||
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
|
||||
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
|
||||
// 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 {
|
||||
if method == "" { // then use like it was .Any
|
||||
for _, k := range AllMethods {
|
||||
p.Handle(k, registedPath, handlers...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
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)
|
||||
route := NewRoute(method, path, middleware, p.station)
|
||||
p.station.plugins.DoPreHandle(route)
|
||||
p.station.addRoute(route)
|
||||
p.station.plugins.DoPostHandle(route)
|
||||
return route
|
||||
}
|
||||
|
||||
// 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)
|
||||
func (p *GardenParty) HandleFunc(method string, registedPath string, handlersFn ...HandlerFunc) IRoute {
|
||||
return p.Handle(method, registedPath, ConvertToHandlers(handlersFn)...)
|
||||
}
|
||||
|
||||
// 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...)
|
||||
}
|
||||
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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) {
|
||||
var handlersFn []HandlerFunc
|
||||
|
||||
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) {
|
||||
var handlersFn []HandlerFunc
|
||||
|
||||
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
|
||||
func (p *GardenParty) Get(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return p.HandleFunc(MethodGet, path, handlersFn...).Name
|
||||
}
|
||||
|
||||
// Post registers a route for the Post http method
|
||||
func (p *GardenParty) Post(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return p.HandleFunc(MethodPost, path, handlersFn...).Name
|
||||
}
|
||||
|
||||
// Put registers a route for the Put http method
|
||||
func (p *GardenParty) Put(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return p.HandleFunc(MethodPut, path, handlersFn...).Name
|
||||
}
|
||||
|
||||
// Delete registers a route for the Delete http method
|
||||
func (p *GardenParty) Delete(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return p.HandleFunc(MethodDelete, path, handlersFn...).Name
|
||||
}
|
||||
|
||||
// Connect registers a route for the Connect http method
|
||||
func (p *GardenParty) Connect(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return p.HandleFunc(MethodConnect, path, handlersFn...).Name
|
||||
}
|
||||
|
||||
// Head registers a route for the Head http method
|
||||
func (p *GardenParty) Head(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return p.HandleFunc(MethodHead, path, handlersFn...).Name
|
||||
}
|
||||
|
||||
// Options registers a route for the Options http method
|
||||
func (p *GardenParty) Options(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return p.HandleFunc(MethodOptions, path, handlersFn...).Name
|
||||
}
|
||||
|
||||
// Patch registers a route for the Patch http method
|
||||
func (p *GardenParty) Patch(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return p.HandleFunc(MethodPatch, path, handlersFn...).Name
|
||||
}
|
||||
|
||||
// Trace registers a route for the Trace http method
|
||||
func (p *GardenParty) Trace(path string, handlersFn ...HandlerFunc) RouteNameFunc {
|
||||
return p.HandleFunc(MethodTrace, path, handlersFn...).Name
|
||||
}
|
||||
|
||||
// Any registers a route for ALL of the http methods (Get,Post,Put,Head,Patch,Options,Connect,Delete)
|
||||
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
|
||||
}
|
||||
|
||||
return theRoutes
|
||||
}
|
||||
|
||||
// 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
|
||||
relative += Slash
|
||||
}
|
||||
|
||||
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, ".") {
|
||||
//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
|
||||
}
|
||||
} 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)
|
||||
|
||||
if strafter[0] == SlashByte && strings.Count(strafter, ".") >= 2 {
|
||||
//it's domain, remove the first slash
|
||||
strafter = strafter[1:]
|
||||
}
|
||||
|
||||
return strafter
|
||||
}
|
376
plugin.go
376
plugin.go
|
@ -1,128 +1,133 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/errors"
|
||||
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
// errPluginAlreadyExists returns an error with message: 'Cannot activate the same plugin again, plugin '+plugin name[+plugin description]' is already exists'
|
||||
errPluginAlreadyExists = errors.New("Cannot use the same plugin again, '%s[%s]' is already exists")
|
||||
// errPluginActivate returns an error with message: 'While trying to activate plugin '+plugin name'. Trace: +specific error'
|
||||
errPluginActivate = errors.New("While trying to activate plugin '%s'. Trace: %s")
|
||||
// errPluginRemoveNoPlugins returns an error with message: 'No plugins are registed yet, you cannot remove a plugin from an empty list!'
|
||||
errPluginRemoveNoPlugins = errors.New("No plugins are registed yet, you cannot remove a plugin from an empty list!")
|
||||
// errPluginRemoveEmptyName returns an error with message: 'Plugin with an empty name cannot be removed'
|
||||
errPluginRemoveEmptyName = errors.New("Plugin with an empty name cannot be removed")
|
||||
// errPluginRemoveNotFound returns an error with message: 'Cannot remove a plugin which doesn't exists'
|
||||
errPluginRemoveNotFound = errors.New("Cannot remove a plugin which doesn't exists")
|
||||
)
|
||||
|
||||
type (
|
||||
// IPlugin just an empty base for plugins
|
||||
// A Plugin can be added with: .Add(PreHandleFunc(func(IRoute))) and so on... or
|
||||
// .Add(myPlugin{}) which myPlugin is a struct with any of the methods below or
|
||||
// .PreHandle(PreHandleFunc), .PostHandle(func(IRoute)) and so on...
|
||||
IPlugin interface {
|
||||
// Plugin just an empty base for plugins
|
||||
// A Plugin can be added with: .Add(PreListenFunc(func(*Framework))) and so on... or
|
||||
// .Add(myPlugin{},myPlugin2{}) which myPlugin is a struct with any of the methods below or
|
||||
// .PostListen(func(*Framework)) and so on...
|
||||
Plugin interface {
|
||||
}
|
||||
|
||||
// IPluginGetName implements the GetName() string method
|
||||
IPluginGetName interface {
|
||||
// pluginGetName implements the GetName() string method
|
||||
pluginGetName interface {
|
||||
// GetName has to returns the name of the plugin, a name is unique
|
||||
// name has to be not dependent from other methods of the plugin,
|
||||
// because it is being called even before the Activate
|
||||
GetName() string
|
||||
}
|
||||
|
||||
// IPluginGetDescription implements the GetDescription() string method
|
||||
IPluginGetDescription interface {
|
||||
// pluginGetDescription implements the GetDescription() string method
|
||||
pluginGetDescription interface {
|
||||
// GetDescription has to returns the description of what the plugins is used for
|
||||
GetDescription() string
|
||||
}
|
||||
|
||||
// IPluginActivate implements the Activate(IPluginContainer) error method
|
||||
IPluginActivate interface {
|
||||
// pluginActivate implements the Activate(pluginContainer) error method
|
||||
pluginActivate interface {
|
||||
// Activate called BEFORE the plugin being added to the plugins list,
|
||||
// if Activate returns none nil error then the plugin is not being added to the list
|
||||
// it is being called only one time
|
||||
//
|
||||
// PluginContainer parameter used to add other plugins if that's necessary by the plugin
|
||||
Activate(IPluginContainer) error
|
||||
Activate(PluginContainer) error
|
||||
}
|
||||
|
||||
// IPluginPreHandle implements the PreHandle(IRoute) method
|
||||
IPluginPreHandle interface {
|
||||
// PreHandle it's being called every time BEFORE a Route is registed to the Router
|
||||
//
|
||||
// parameter is the Route
|
||||
PreHandle(IRoute)
|
||||
}
|
||||
// PreHandleFunc implements the simple function listener for the PreHandle(IRoute)
|
||||
PreHandleFunc func(IRoute)
|
||||
// IPluginPostHandle implements the PostHandle(IRoute) method
|
||||
IPluginPostHandle interface {
|
||||
// PostHandle it's being called every time AFTER a Route successfully registed to the Router
|
||||
//
|
||||
// parameter is the Route
|
||||
PostHandle(IRoute)
|
||||
}
|
||||
// PostHandleFunc implements the simple function listener for the PostHandle(IRoute)
|
||||
PostHandleFunc func(IRoute)
|
||||
// IPluginPreListen implements the PreListen(*Iris) method
|
||||
IPluginPreListen interface {
|
||||
// pluginPreListen implements the PreListen(*Framework) method
|
||||
pluginPreListen interface {
|
||||
// PreListen it's being called only one time, BEFORE the Server is started (if .Listen called)
|
||||
// is used to do work at the time all other things are ready to go
|
||||
// parameter is the station
|
||||
PreListen(*Iris)
|
||||
PreListen(*Framework)
|
||||
}
|
||||
// PreListenFunc implements the simple function listener for the PreListen(*Iris)
|
||||
PreListenFunc func(*Iris)
|
||||
// IPluginPostListen implements the PostListen(*Iris) method
|
||||
IPluginPostListen interface {
|
||||
// PreListenFunc implements the simple function listener for the PreListen(*Framework)
|
||||
PreListenFunc func(*Framework)
|
||||
// pluginPostListen implements the PostListen(*Framework) method
|
||||
pluginPostListen interface {
|
||||
// PostListen it's being called only one time, AFTER the Server is started (if .Listen called)
|
||||
// parameter is the station
|
||||
PostListen(*Iris)
|
||||
PostListen(*Framework)
|
||||
}
|
||||
// PostListenFunc implements the simple function listener for the PostListen(*Iris)
|
||||
PostListenFunc func(*Iris)
|
||||
// IPluginPreClose implements the PreClose(*Iris) method
|
||||
IPluginPreClose interface {
|
||||
// PostListenFunc implements the simple function listener for the PostListen(*Framework)
|
||||
PostListenFunc func(*Framework)
|
||||
// pluginPreClose implements the PreClose(*Framework) method
|
||||
pluginPreClose interface {
|
||||
// PreClose it's being called only one time, BEFORE the Iris .Close method
|
||||
// any plugin cleanup/clear memory happens here
|
||||
//
|
||||
// The plugin is deactivated after this state
|
||||
PreClose(*Iris)
|
||||
PreClose(*Framework)
|
||||
}
|
||||
// PreCloseFunc implements the simple function listener for the PreClose(*Iris)
|
||||
PreCloseFunc func(*Iris)
|
||||
// PreCloseFunc implements the simple function listener for the PreClose(*Framework)
|
||||
PreCloseFunc func(*Framework)
|
||||
|
||||
// IPluginPreDownload It's for the future, not being used, I need to create
|
||||
// pluginPreDownload It's for the future, not being used, I need to create
|
||||
// and return an ActivatedPlugin type which will have it's methods, and pass it on .Activate
|
||||
// but now we return the whole pluginContainer, which I can't determinate which plugin tries to
|
||||
// download something, so we will leave it here for the future.
|
||||
IPluginPreDownload interface {
|
||||
pluginPreDownload interface {
|
||||
// PreDownload it's being called every time a plugin tries to download something
|
||||
//
|
||||
// first parameter is the plugin
|
||||
// second parameter is the download url
|
||||
// must return a boolean, if false then the plugin is not permmited to download this file
|
||||
PreDownload(plugin IPlugin, downloadURL string) // bool
|
||||
PreDownload(plugin Plugin, downloadURL string) // bool
|
||||
}
|
||||
|
||||
// PreDownloadFunc implements the simple function listener for the PreDownload(IPlugin,string)
|
||||
PreDownloadFunc func(IPlugin, string)
|
||||
// PreDownloadFunc implements the simple function listener for the PreDownload(plugin,string)
|
||||
PreDownloadFunc func(Plugin, string)
|
||||
|
||||
// IPluginContainer is the interface which the PluginContainer should implements
|
||||
IPluginContainer interface {
|
||||
Add(plugin IPlugin) error
|
||||
Remove(pluginName string) error
|
||||
GetName(plugin IPlugin) string
|
||||
GetDescription(plugin IPlugin) string
|
||||
GetByName(pluginName string) IPlugin
|
||||
Printf(format string, a ...interface{})
|
||||
DoPreHandle(route IRoute)
|
||||
DoPostHandle(route IRoute)
|
||||
DoPreListen(station *Iris)
|
||||
DoPostListen(station *Iris)
|
||||
DoPreClose(station *Iris)
|
||||
DoPreDownload(pluginTryToDownload IPlugin, downloadURL string)
|
||||
GetAll() []IPlugin
|
||||
// PluginContainer is the interface which the pluginContainer should implements
|
||||
PluginContainer interface {
|
||||
Add(...Plugin) error
|
||||
Remove(string) error
|
||||
GetName(Plugin) string
|
||||
GetDescription(Plugin) string
|
||||
GetByName(string) Plugin
|
||||
Printf(string, ...interface{})
|
||||
PreListen(PreListenFunc)
|
||||
DoPreListen(*Framework)
|
||||
DoPreListenParallel(*Framework)
|
||||
PostListen(PostListenFunc)
|
||||
DoPostListen(*Framework)
|
||||
PreClose(PreCloseFunc)
|
||||
DoPreClose(*Framework)
|
||||
PreDownload(PreDownloadFunc)
|
||||
DoPreDownload(Plugin, string)
|
||||
// custom event callbacks
|
||||
On(string, ...func())
|
||||
Call(string)
|
||||
//
|
||||
GetAll() []Plugin
|
||||
// GetDownloader is the only one module that is used and fire listeners at the same time in this file
|
||||
GetDownloader() IDownloadManager
|
||||
GetDownloader() PluginDownloadManager
|
||||
}
|
||||
// IDownloadManager is the interface which the DownloadManager should implements
|
||||
IDownloadManager interface {
|
||||
DirectoryExists(dir string) bool
|
||||
DownloadZip(zipURL string, targetDir string) (string, error)
|
||||
Unzip(archive string, target string) (string, error)
|
||||
Remove(filePath string) error
|
||||
// PluginDownloadManager is the interface which the DownloadManager should implements
|
||||
PluginDownloadManager interface {
|
||||
DirectoryExists(string) bool
|
||||
DownloadZip(string, string) (string, error)
|
||||
Unzip(string, string) (string, error)
|
||||
Remove(string) error
|
||||
// install is just the flow of: downloadZip -> unzip -> removeFile(zippedFile)
|
||||
// accepts 2 parameters
|
||||
//
|
||||
|
@ -136,37 +141,23 @@ type (
|
|||
Install(remoteFileZip string, targetDirectory string) (string, error)
|
||||
}
|
||||
|
||||
// DownloadManager is just a struch which exports the util's downloadZip, directoryExists, unzip methods, used by the plugins via the PluginContainer
|
||||
DownloadManager struct {
|
||||
// pluginDownloadManager is just a struch which exports the util's downloadZip, directoryExists, unzip methods, used by the plugins via the pluginContainer
|
||||
pluginDownloadManager struct {
|
||||
}
|
||||
)
|
||||
|
||||
// convert the functions to IPlugin
|
||||
|
||||
// PreHandle it's being called every time BEFORE a Route is registed to the Router
|
||||
//
|
||||
// parameter is the Route
|
||||
func (fn PreHandleFunc) PreHandle(route IRoute) {
|
||||
fn(route)
|
||||
}
|
||||
|
||||
// PostHandle it's being called every time AFTER a Route successfully registed to the Router
|
||||
//
|
||||
// parameter is the Route
|
||||
func (fn PostHandleFunc) PostHandle(route IRoute) {
|
||||
fn(route)
|
||||
}
|
||||
// convert the functions to plugin
|
||||
|
||||
// PreListen it's being called only one time, BEFORE the Server is started (if .Listen called)
|
||||
// is used to do work at the time all other things are ready to go
|
||||
// parameter is the station
|
||||
func (fn PreListenFunc) PreListen(station *Iris) {
|
||||
func (fn PreListenFunc) PreListen(station *Framework) {
|
||||
fn(station)
|
||||
}
|
||||
|
||||
// PostListen it's being called only one time, AFTER the Server is started (if .Listen called)
|
||||
// parameter is the station
|
||||
func (fn PostListenFunc) PostListen(station *Iris) {
|
||||
func (fn PostListenFunc) PostListen(station *Framework) {
|
||||
fn(station)
|
||||
}
|
||||
|
||||
|
@ -174,7 +165,7 @@ func (fn PostListenFunc) PostListen(station *Iris) {
|
|||
// any plugin cleanup/clear memory happens here
|
||||
//
|
||||
// The plugin is deactivated after this state
|
||||
func (fn PreCloseFunc) PreClose(station *Iris) {
|
||||
func (fn PreCloseFunc) PreClose(station *Framework) {
|
||||
fn(station)
|
||||
}
|
||||
|
||||
|
@ -183,83 +174,90 @@ func (fn PreCloseFunc) PreClose(station *Iris) {
|
|||
// first parameter is the plugin
|
||||
// second parameter is the download url
|
||||
// must return a boolean, if false then the plugin is not permmited to download this file
|
||||
func (fn PreDownloadFunc) PreDownload(pl IPlugin, downloadURL string) {
|
||||
func (fn PreDownloadFunc) PreDownload(pl Plugin, downloadURL string) {
|
||||
fn(pl, downloadURL)
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var _ IDownloadManager = &DownloadManager{}
|
||||
var _ IPluginContainer = &PluginContainer{}
|
||||
var _ PluginDownloadManager = &pluginDownloadManager{}
|
||||
var _ PluginContainer = &pluginContainer{}
|
||||
|
||||
// DirectoryExists returns true if a given local directory exists
|
||||
func (d *DownloadManager) DirectoryExists(dir string) bool {
|
||||
func (d *pluginDownloadManager) DirectoryExists(dir string) bool {
|
||||
return utils.DirectoryExists(dir)
|
||||
}
|
||||
|
||||
// DownloadZip downlodas a zip to the given local path location
|
||||
func (d *DownloadManager) DownloadZip(zipURL string, targetDir string) (string, error) {
|
||||
func (d *pluginDownloadManager) DownloadZip(zipURL string, targetDir string) (string, error) {
|
||||
return utils.DownloadZip(zipURL, targetDir)
|
||||
}
|
||||
|
||||
// Unzip unzips a zip to the given local path location
|
||||
func (d *DownloadManager) Unzip(archive string, target string) (string, error) {
|
||||
func (d *pluginDownloadManager) Unzip(archive string, target string) (string, error) {
|
||||
return utils.Unzip(archive, target)
|
||||
}
|
||||
|
||||
// Remove deletes/removes/rm a file
|
||||
func (d *DownloadManager) Remove(filePath string) error {
|
||||
func (d *pluginDownloadManager) Remove(filePath string) error {
|
||||
return utils.RemoveFile(filePath)
|
||||
}
|
||||
|
||||
// Install is just the flow of the: DownloadZip->Unzip->Remove the zip
|
||||
func (d *DownloadManager) Install(remoteFileZip string, targetDirectory string) (string, error) {
|
||||
func (d *pluginDownloadManager) Install(remoteFileZip string, targetDirectory string) (string, error) {
|
||||
return utils.Install(remoteFileZip, targetDirectory)
|
||||
}
|
||||
|
||||
// PluginContainer is the base container of all Iris, registed plugins
|
||||
type PluginContainer struct {
|
||||
activatedPlugins []IPlugin
|
||||
downloader *DownloadManager
|
||||
// pluginContainer is the base container of all Iris, registed plugins
|
||||
type pluginContainer struct {
|
||||
activatedPlugins []Plugin
|
||||
customEvents map[string][]func()
|
||||
downloader *pluginDownloadManager
|
||||
logger *logger.Logger
|
||||
}
|
||||
|
||||
// Add activates the plugins and if succeed then adds it to the activated plugins list
|
||||
func (p *PluginContainer) Add(plugin IPlugin) error {
|
||||
if p.activatedPlugins == nil {
|
||||
p.activatedPlugins = make([]IPlugin, 0)
|
||||
}
|
||||
|
||||
// Check if it's a plugin first, has Activate GetName
|
||||
|
||||
// Check if the plugin already exists
|
||||
pName := p.GetName(plugin)
|
||||
if pName != "" && p.GetByName(pName) != nil {
|
||||
return ErrPluginAlreadyExists.Format(pName, p.GetDescription(plugin))
|
||||
}
|
||||
// Activate the plugin, if no error then add it to the plugins
|
||||
if pluginObj, ok := plugin.(IPluginActivate); ok {
|
||||
err := pluginObj.Activate(p)
|
||||
if err != nil {
|
||||
return ErrPluginActivate.Format(pName, err.Error())
|
||||
func (p *pluginContainer) Add(plugins ...Plugin) error {
|
||||
for _, plugin := range plugins {
|
||||
if p.activatedPlugins == nil {
|
||||
p.activatedPlugins = make([]Plugin, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// All ok, add it to the plugins list
|
||||
p.activatedPlugins = append(p.activatedPlugins, plugin)
|
||||
// Check if it's a plugin first, has Activate GetName
|
||||
|
||||
// Check if the plugin already exists
|
||||
pName := p.GetName(plugin)
|
||||
if pName != "" && p.GetByName(pName) != nil {
|
||||
return errPluginAlreadyExists.Format(pName, p.GetDescription(plugin))
|
||||
}
|
||||
// Activate the plugin, if no error then add it to the plugins
|
||||
if pluginObj, ok := plugin.(pluginActivate); ok {
|
||||
err := pluginObj.Activate(p)
|
||||
if err != nil {
|
||||
return errPluginActivate.Format(pName, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// All ok, add it to the plugins list
|
||||
p.activatedPlugins = append(p.activatedPlugins, plugin)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pluginContainer) Reset() {
|
||||
|
||||
}
|
||||
|
||||
// Remove removes a plugin by it's name, if pluginName is empty "" or no plugin found with this name, then nothing is removed and a specific error is returned.
|
||||
// This doesn't calls the PreClose method
|
||||
func (p *PluginContainer) Remove(pluginName string) error {
|
||||
func (p *pluginContainer) Remove(pluginName string) error {
|
||||
if p.activatedPlugins == nil {
|
||||
return ErrPluginRemoveNoPlugins.Return()
|
||||
return errPluginRemoveNoPlugins.Return()
|
||||
}
|
||||
|
||||
if pluginName == "" {
|
||||
//return error: cannot delete an unamed plugin
|
||||
return ErrPluginRemoveEmptyName.Return()
|
||||
return errPluginRemoveEmptyName.Return()
|
||||
}
|
||||
|
||||
indexToRemove := -1
|
||||
|
@ -269,7 +267,7 @@ func (p *PluginContainer) Remove(pluginName string) error {
|
|||
}
|
||||
}
|
||||
if indexToRemove == -1 { //if index stills -1 then no plugin was found with this name, just return an error. it is not a critical error.
|
||||
return ErrPluginRemoveNotFound.Return()
|
||||
return errPluginRemoveNotFound.Return()
|
||||
}
|
||||
|
||||
p.activatedPlugins = append(p.activatedPlugins[:indexToRemove], p.activatedPlugins[indexToRemove+1:]...)
|
||||
|
@ -278,29 +276,29 @@ func (p *PluginContainer) Remove(pluginName string) error {
|
|||
}
|
||||
|
||||
// GetName returns the name of a plugin, if no GetName() implemented it returns an empty string ""
|
||||
func (p *PluginContainer) GetName(plugin IPlugin) string {
|
||||
if pluginObj, ok := plugin.(IPluginGetName); ok {
|
||||
func (p *pluginContainer) GetName(plugin Plugin) string {
|
||||
if pluginObj, ok := plugin.(pluginGetName); ok {
|
||||
return pluginObj.GetName()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetDescription returns the name of a plugin, if no GetDescription() implemented it returns an empty string ""
|
||||
func (p *PluginContainer) GetDescription(plugin IPlugin) string {
|
||||
if pluginObj, ok := plugin.(IPluginGetDescription); ok {
|
||||
func (p *pluginContainer) GetDescription(plugin Plugin) string {
|
||||
if pluginObj, ok := plugin.(pluginGetDescription); ok {
|
||||
return pluginObj.GetDescription()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetByName returns a plugin instance by it's name
|
||||
func (p *PluginContainer) GetByName(pluginName string) IPlugin {
|
||||
func (p *pluginContainer) GetByName(pluginName string) Plugin {
|
||||
if p.activatedPlugins == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := range p.activatedPlugins {
|
||||
if pluginObj, ok := p.activatedPlugins[i].(IPluginGetName); ok {
|
||||
if pluginObj, ok := p.activatedPlugins[i].(pluginGetName); ok {
|
||||
if pluginObj.GetName() == pluginName {
|
||||
return pluginObj
|
||||
}
|
||||
|
@ -311,114 +309,130 @@ func (p *PluginContainer) GetByName(pluginName string) IPlugin {
|
|||
}
|
||||
|
||||
// GetAll returns all activated plugins
|
||||
func (p *PluginContainer) GetAll() []IPlugin {
|
||||
func (p *pluginContainer) GetAll() []Plugin {
|
||||
return p.activatedPlugins
|
||||
}
|
||||
|
||||
// GetDownloader returns the download manager
|
||||
func (p *PluginContainer) GetDownloader() IDownloadManager {
|
||||
func (p *pluginContainer) GetDownloader() PluginDownloadManager {
|
||||
// create it if and only if it used somewhere
|
||||
if p.downloader == nil {
|
||||
p.downloader = &DownloadManager{}
|
||||
p.downloader = &pluginDownloadManager{}
|
||||
}
|
||||
return p.downloader
|
||||
}
|
||||
|
||||
// Printf sends plain text to any registed logger (future), some plugins maybe want use this method
|
||||
// maybe at the future I change it, instead of sync even-driven to async channels...
|
||||
func (p *PluginContainer) Printf(format string, a ...interface{}) {
|
||||
func (p *pluginContainer) Printf(format string, a ...interface{}) {
|
||||
if p.logger != nil {
|
||||
p.logger.Printf(format, a...) //for now just this.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PreHandle adds a PreHandle plugin-function to the plugin flow container
|
||||
func (p *PluginContainer) PreHandle(fn PreHandleFunc) {
|
||||
p.Add(fn)
|
||||
}
|
||||
|
||||
// DoPreHandle raise all plugins which has the PreHandle method
|
||||
func (p *PluginContainer) DoPreHandle(route IRoute) {
|
||||
for i := range p.activatedPlugins {
|
||||
// check if this method exists on our plugin obj, these are optionaly and call it
|
||||
if pluginObj, ok := p.activatedPlugins[i].(IPluginPreHandle); ok {
|
||||
pluginObj.PreHandle(route)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PostHandle adds a PostHandle plugin-function to the plugin flow container
|
||||
func (p *PluginContainer) PostHandle(fn PostHandleFunc) {
|
||||
p.Add(fn)
|
||||
}
|
||||
|
||||
// DoPostHandle raise all plugins which has the DoPostHandle method
|
||||
func (p *PluginContainer) DoPostHandle(route IRoute) {
|
||||
for i := range p.activatedPlugins {
|
||||
// check if this method exists on our plugin obj, these are optionaly and call it
|
||||
if pluginObj, ok := p.activatedPlugins[i].(IPluginPostHandle); ok {
|
||||
pluginObj.PostHandle(route)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PreListen adds a PreListen plugin-function to the plugin flow container
|
||||
func (p *PluginContainer) PreListen(fn PreListenFunc) {
|
||||
func (p *pluginContainer) PreListen(fn PreListenFunc) {
|
||||
p.Add(fn)
|
||||
}
|
||||
|
||||
// DoPreListen raise all plugins which has the DoPreListen method
|
||||
func (p *PluginContainer) DoPreListen(station *Iris) {
|
||||
func (p *pluginContainer) DoPreListen(station *Framework) {
|
||||
for i := range p.activatedPlugins {
|
||||
// check if this method exists on our plugin obj, these are optionaly and call it
|
||||
if pluginObj, ok := p.activatedPlugins[i].(IPluginPreListen); ok {
|
||||
if pluginObj, ok := p.activatedPlugins[i].(pluginPreListen); ok {
|
||||
pluginObj.PreListen(station)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DoPreListenParallel raise all PreListen plugins 'at the same time'
|
||||
func (p *pluginContainer) DoPreListenParallel(station *Framework) {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, plugin := range p.activatedPlugins {
|
||||
wg.Add(1)
|
||||
// check if this method exists on our plugin obj, these are optionaly and call it
|
||||
go func(plugin Plugin) {
|
||||
if pluginObj, ok := plugin.(pluginPreListen); ok {
|
||||
pluginObj.PreListen(station)
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
|
||||
}(plugin)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
}
|
||||
|
||||
// PostListen adds a PostListen plugin-function to the plugin flow container
|
||||
func (p *PluginContainer) PostListen(fn PostListenFunc) {
|
||||
func (p *pluginContainer) PostListen(fn PostListenFunc) {
|
||||
p.Add(fn)
|
||||
}
|
||||
|
||||
// DoPostListen raise all plugins which has the DoPostListen method
|
||||
func (p *PluginContainer) DoPostListen(station *Iris) {
|
||||
func (p *pluginContainer) DoPostListen(station *Framework) {
|
||||
for i := range p.activatedPlugins {
|
||||
// check if this method exists on our plugin obj, these are optionaly and call it
|
||||
if pluginObj, ok := p.activatedPlugins[i].(IPluginPostListen); ok {
|
||||
if pluginObj, ok := p.activatedPlugins[i].(pluginPostListen); ok {
|
||||
pluginObj.PostListen(station)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PreClose adds a PreClose plugin-function to the plugin flow container
|
||||
func (p *PluginContainer) PreClose(fn PreCloseFunc) {
|
||||
func (p *pluginContainer) PreClose(fn PreCloseFunc) {
|
||||
p.Add(fn)
|
||||
}
|
||||
|
||||
// DoPreClose raise all plugins which has the DoPreClose method
|
||||
func (p *PluginContainer) DoPreClose(station *Iris) {
|
||||
func (p *pluginContainer) DoPreClose(station *Framework) {
|
||||
for i := range p.activatedPlugins {
|
||||
// check if this method exists on our plugin obj, these are optionaly and call it
|
||||
if pluginObj, ok := p.activatedPlugins[i].(IPluginPreClose); ok {
|
||||
if pluginObj, ok := p.activatedPlugins[i].(pluginPreClose); ok {
|
||||
pluginObj.PreClose(station)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PreDownload adds a PreDownload plugin-function to the plugin flow container
|
||||
func (p *PluginContainer) PreDownload(fn PreDownloadFunc) {
|
||||
func (p *pluginContainer) PreDownload(fn PreDownloadFunc) {
|
||||
p.Add(fn)
|
||||
}
|
||||
|
||||
// DoPreDownload raise all plugins which has the DoPreDownload method
|
||||
func (p *PluginContainer) DoPreDownload(pluginTryToDownload IPlugin, downloadURL string) {
|
||||
func (p *pluginContainer) DoPreDownload(pluginTryToDownload Plugin, downloadURL string) {
|
||||
for i := range p.activatedPlugins {
|
||||
// check if this method exists on our plugin obj, these are optionaly and call it
|
||||
if pluginObj, ok := p.activatedPlugins[i].(IPluginPreDownload); ok {
|
||||
if pluginObj, ok := p.activatedPlugins[i].(pluginPreDownload); ok {
|
||||
pluginObj.PreDownload(pluginTryToDownload, downloadURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// On registers a custom event
|
||||
// these are not registed as plugins, they are hidden events
|
||||
func (p *pluginContainer) On(name string, fns ...func()) {
|
||||
if p.customEvents == nil {
|
||||
p.customEvents = make(map[string][]func(), 0)
|
||||
}
|
||||
if p.customEvents[name] == nil {
|
||||
p.customEvents[name] = make([]func(), 0)
|
||||
}
|
||||
p.customEvents[name] = append(p.customEvents[name], fns...)
|
||||
}
|
||||
|
||||
// Call fires the custom event
|
||||
func (p *pluginContainer) Call(name string) {
|
||||
if p.customEvents == nil {
|
||||
return
|
||||
}
|
||||
if fns := p.customEvents[name]; fns != nil {
|
||||
for _, fn := range fns {
|
||||
fn()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
func main(){
|
||||
e := editor.New("username","password").Port(4444).Dir("/path/to/the/client/side/directory")
|
||||
|
||||
iris.Plugins().Add(e)
|
||||
iris.Plugins.Add(e)
|
||||
|
||||
iris.Get("/", func (ctx *iris.Context){})
|
||||
|
||||
|
|
|
@ -11,10 +11,10 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/iris-contrib/npm"
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/npm"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
|
@ -85,13 +85,13 @@ func (e *Plugin) GetDescription() string {
|
|||
}
|
||||
|
||||
// PreListen runs before the server's listens, saves the keyfile,certfile and the host from the Iris station to listen for
|
||||
func (e *Plugin) PreListen(s *iris.Iris) {
|
||||
e.logger = s.Logger()
|
||||
e.keyfile = s.Server().Config.KeyFile
|
||||
e.certfile = s.Server().Config.CertFile
|
||||
func (e *Plugin) PreListen(s *iris.Framework) {
|
||||
e.logger = s.Logger
|
||||
e.keyfile = s.Config.Server.KeyFile
|
||||
e.certfile = s.Config.Server.CertFile
|
||||
|
||||
if e.config.Host == "" {
|
||||
h := s.Server().Config.ListeningAddr
|
||||
h := s.Config.Server.ListeningAddr
|
||||
|
||||
if idx := strings.Index(h, ":"); idx >= 0 {
|
||||
h = h[0:idx]
|
||||
|
@ -107,7 +107,7 @@ func (e *Plugin) PreListen(s *iris.Iris) {
|
|||
}
|
||||
|
||||
// PreClose kills the editor's server when Iris is closed
|
||||
func (e *Plugin) PreClose(s *iris.Iris) {
|
||||
func (e *Plugin) PreClose(s *iris.Framework) {
|
||||
if e.process != nil {
|
||||
err := e.process.Kill()
|
||||
if err != nil {
|
||||
|
|
|
@ -28,7 +28,7 @@ import (
|
|||
|
||||
func main() {
|
||||
|
||||
iris.Plugins().Add(iriscontrol.Web(9090, map[string]string{
|
||||
iris.Plugins.Add(iriscontrol.Web(9090, map[string]string{
|
||||
"irisusername1": "irispassword1",
|
||||
"irisusername2": "irispassowrd2",
|
||||
}))
|
||||
|
@ -39,7 +39,6 @@ func main() {
|
|||
iris.Post("/something", func(ctx *iris.Context) {
|
||||
})
|
||||
|
||||
fmt.Printf("Iris is listening on :%d", 8080)
|
||||
iris.Listen(":8080")
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/plugin/routesinfo"
|
||||
)
|
||||
|
||||
var pathSeperator = string(os.PathSeparator)
|
||||
|
@ -27,13 +26,13 @@ func (i *irisControlPlugin) startControlPanel() {
|
|||
}
|
||||
|
||||
i.server = iris.New()
|
||||
i.server.Config().DisableBanner = true
|
||||
i.server.Config().Render.Template.Directory = installationPath + "templates"
|
||||
i.server.Config.DisableBanner = true
|
||||
i.server.Config.Render.Template.Directory = installationPath + "templates"
|
||||
//i.server.SetRenderConfig(i.server.Config.Render)
|
||||
i.setPluginsInfo()
|
||||
i.setPanelRoutes()
|
||||
|
||||
go i.server.Listen(strconv.Itoa(i.options.Port))
|
||||
go i.server.Listen(":" + strconv.Itoa(i.options.Port))
|
||||
|
||||
i.pluginContainer.Printf("[%s] %s is running at port %d", time.Now().UTC().String(), Name, i.options.Port)
|
||||
|
||||
|
@ -43,7 +42,7 @@ func (i *irisControlPlugin) startControlPanel() {
|
|||
// contains a boolean if server is running, the routes and the plugins
|
||||
type DashboardPage struct {
|
||||
ServerIsRunning bool
|
||||
Routes []routesinfo.RouteInfo
|
||||
Routes []iris.Route
|
||||
Plugins []PluginInfo
|
||||
LastOperationDateStr string
|
||||
}
|
||||
|
@ -52,7 +51,18 @@ func (i *irisControlPlugin) setPluginsInfo() {
|
|||
plugins := i.pluginContainer.GetAll()
|
||||
i.plugins = make([]PluginInfo, 0, len(plugins))
|
||||
for _, plugin := range plugins {
|
||||
i.plugins = append(i.plugins, PluginInfo{Name: i.pluginContainer.GetName(plugin), Description: i.pluginContainer.GetDescription(plugin)})
|
||||
name := i.pluginContainer.GetName(plugin)
|
||||
desc := i.pluginContainer.GetDescription(plugin)
|
||||
if name == "" {
|
||||
// means an iris internaly plugin or a nameless plugin
|
||||
name = "Internal Iris Plugin"
|
||||
}
|
||||
if desc == "" {
|
||||
// means an iris internaly plugin or a descriptionless plugin
|
||||
desc = "Propably an internal Iris Plugin - no description provided"
|
||||
}
|
||||
|
||||
i.plugins = append(i.plugins, PluginInfo{Name: name, Description: desc})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,8 +85,8 @@ func (i *irisControlPlugin) setPanelRoutes() {
|
|||
i.server.Use(i.authFunc)
|
||||
i.server.Get("/", func(ctx *iris.Context) {
|
||||
ctx.Render("index.html", DashboardPage{
|
||||
ServerIsRunning: i.station.Server().IsListening(),
|
||||
Routes: i.routes.All(),
|
||||
ServerIsRunning: i.station.HTTPServer.IsListening(),
|
||||
Routes: i.routes,
|
||||
Plugins: i.plugins,
|
||||
LastOperationDateStr: i.lastOperationDate.Format(config.TimeFormat),
|
||||
})
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
package iriscontrol
|
||||
|
||||
// NOT READY YET
|
||||
|
||||
// PluginInfo holds the Name and the description of the registed plugins
|
||||
// PluginInfo the name and the description of a plugin
|
||||
type PluginInfo struct {
|
||||
Name string
|
||||
Description string
|
||||
}
|
||||
|
||||
//func getPluginlist...
|
||||
|
|
|
@ -6,8 +6,6 @@ import (
|
|||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/middleware/basicauth"
|
||||
"github.com/kataras/iris/plugin/routesinfo"
|
||||
"github.com/kataras/iris/server"
|
||||
)
|
||||
|
||||
// Name the name(string) of this plugin which is Iris Control
|
||||
|
@ -16,17 +14,17 @@ const Name = "Iris Control"
|
|||
type irisControlPlugin struct {
|
||||
options config.IrisControl
|
||||
// the pluginContainer is the container which keeps this plugin from the main user's iris instance
|
||||
pluginContainer iris.IPluginContainer
|
||||
pluginContainer iris.PluginContainer
|
||||
// the station object of the main user's iris instance
|
||||
station *iris.Iris
|
||||
station *iris.Framework
|
||||
//a copy of the server which the main user's iris is listening for
|
||||
stationServer *server.Server
|
||||
stationServer *iris.Server
|
||||
|
||||
// the server is this plugin's server object, it is managed by this plugin only
|
||||
server *iris.Iris
|
||||
server *iris.Framework
|
||||
//
|
||||
//infos
|
||||
routes *routesinfo.Plugin
|
||||
routes []iris.Route
|
||||
plugins []PluginInfo
|
||||
// last time the server was on
|
||||
lastOperationDate time.Time
|
||||
|
@ -37,7 +35,7 @@ type irisControlPlugin struct {
|
|||
|
||||
// New returns the plugin which is ready-to-use inside iris.Plugin method
|
||||
// receives config.IrisControl
|
||||
func New(cfg ...config.IrisControl) iris.IPlugin {
|
||||
func New(cfg ...config.IrisControl) iris.Plugin {
|
||||
c := config.DefaultIrisControl()
|
||||
if len(cfg) > 0 {
|
||||
c = cfg[0]
|
||||
|
@ -48,21 +46,20 @@ func New(cfg ...config.IrisControl) iris.IPlugin {
|
|||
|
||||
auth := basicauth.Default(c.Users)
|
||||
|
||||
return &irisControlPlugin{options: c, authFunc: auth, routes: routesinfo.RoutesInfo()}
|
||||
return &irisControlPlugin{options: c, authFunc: auth, routes: make([]iris.Route, 0)}
|
||||
}
|
||||
|
||||
// Web set the options for the plugin and return the plugin which is ready-to-use inside iris.Plugin method
|
||||
// first parameter is port
|
||||
// second parameter is map of users (username:password)
|
||||
func Web(port int, users map[string]string) iris.IPlugin {
|
||||
return New(config.IrisControl{port, users})
|
||||
func Web(port int, users map[string]string) iris.Plugin {
|
||||
return New(config.IrisControl{Port: port, Users: users})
|
||||
}
|
||||
|
||||
// implement the base IPlugin
|
||||
|
||||
func (i *irisControlPlugin) Activate(container iris.IPluginContainer) error {
|
||||
func (i *irisControlPlugin) Activate(container iris.PluginContainer) error {
|
||||
i.pluginContainer = container
|
||||
container.Add(i.routes) // add the routesinfo plugin to the main server
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -78,25 +75,21 @@ func (i irisControlPlugin) GetDescription() string {
|
|||
|
||||
// implement the rest of the plugin
|
||||
|
||||
// PostHandle
|
||||
func (i *irisControlPlugin) PostHandle(route iris.IRoute) {
|
||||
|
||||
}
|
||||
|
||||
// PostListen sets the station object after the main server starts
|
||||
// starts the actual work of the plugin
|
||||
func (i *irisControlPlugin) PostListen(s *iris.Iris) {
|
||||
func (i *irisControlPlugin) PostListen(s *iris.Framework) {
|
||||
//if the first time, because other times start/stop of the server so listen and no listen will be only from the control panel
|
||||
if i.station == nil {
|
||||
i.station = s
|
||||
i.stationServer = i.station.Server()
|
||||
i.stationServer = i.station.HTTPServer
|
||||
i.lastOperationDate = time.Now()
|
||||
i.routes = s.Lookups()
|
||||
i.startControlPanel()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (i *irisControlPlugin) PreClose(s *iris.Iris) {
|
||||
func (i *irisControlPlugin) PreClose(s *iris.Framework) {
|
||||
// Do nothing. This is a wrapper of the main server if we destroy when users stop the main server then we cannot continue the control panel i.Destroy()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,19 +2,19 @@ package iriscontrol
|
|||
|
||||
// for the main server
|
||||
func (i *irisControlPlugin) StartServer() {
|
||||
if i.station.Server().IsListening() == false {
|
||||
if i.station.Server().IsSecure() {
|
||||
if i.station.HTTPServer.IsListening() == false {
|
||||
if i.station.HTTPServer.IsSecure() {
|
||||
//listen with ListenTLS
|
||||
i.station.ListenTLS(i.station.Server().Config.ListeningAddr, i.station.Server().Config.CertFile, i.station.Server().Config.KeyFile)
|
||||
i.station.ListenTLS(i.station.Config.Server.ListeningAddr, i.station.Config.Server.CertFile, i.station.Config.Server.KeyFile)
|
||||
} else {
|
||||
//listen normal
|
||||
i.station.Listen(i.station.Server().Config.ListeningAddr)
|
||||
i.station.Listen(i.station.Config.Server.ListeningAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *irisControlPlugin) StopServer() {
|
||||
if i.station.Server().IsListening() {
|
||||
if i.station.HTTPServer.IsListening() {
|
||||
i.station.Close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
## RoutesInfo plugin
|
||||
|
||||
This plugin collects & stores all registered routes and gives information about them.
|
||||
|
||||
#### The RouteInfo
|
||||
|
||||
```go
|
||||
|
||||
type RouteInfo struct {
|
||||
Method string
|
||||
Domain string
|
||||
Path string
|
||||
RegistedAt time.Time
|
||||
}
|
||||
|
||||
```
|
||||
## How to use
|
||||
|
||||
```go
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/plugin/routesinfo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
info := routesinfo.New()
|
||||
iris.Plugins().Add(info)
|
||||
|
||||
iris.Get("/yourpath", func(c *iris.Context) {
|
||||
c.Write("yourpath")
|
||||
})
|
||||
|
||||
iris.Post("/otherpostpath", func(c *iris.Context) {
|
||||
c.Write("other post path")
|
||||
})
|
||||
|
||||
all := info.All()
|
||||
// allget := info.ByMethod("GET") -> slice
|
||||
// alllocalhost := info.ByDomain("localhost") -> slice
|
||||
// bypath:= info.ByPath("/yourpath") -> slice
|
||||
// bydomainandmethod:= info.ByDomainAndMethod("localhost","GET") -> slice
|
||||
// bymethodandpath:= info.ByMethodAndPath("GET","/yourpath") -> single (it could be slice for all domains too but it's not)
|
||||
|
||||
println("The first registed route was: ", all[0].Path, "registed at: ", all[0].RegistedAt.String())
|
||||
println("All routes info:")
|
||||
for i:= range all {
|
||||
println(all[i].String())
|
||||
//outputs->
|
||||
// Domain: localhost Method: GET Path: /yourpath RegistedAt: 2016/03/27 15:27:05:029 ...
|
||||
// Domain: localhost Method: POST Path: /otherpostpath RegistedAt: 2016/03/27 15:27:05:030 ...
|
||||
}
|
||||
iris.Listen(":8080")
|
||||
|
||||
}
|
||||
|
||||
|
||||
```
|
|
@ -1,151 +0,0 @@
|
|||
package routesinfo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
//Name the name of the plugin, is "RoutesInfo"
|
||||
const Name = "RoutesInfo"
|
||||
|
||||
// RouteInfo holds the method, domain, path and registered time of a route
|
||||
type RouteInfo struct {
|
||||
Method string
|
||||
Domain string
|
||||
Path string
|
||||
RegistedAt time.Time
|
||||
}
|
||||
|
||||
// String returns the string presentation of the Route(Info)
|
||||
func (ri RouteInfo) String() string {
|
||||
if ri.Domain == "" {
|
||||
ri.Domain = "localhost" // only for printing, this doesn't save it, no pointer.
|
||||
}
|
||||
return fmt.Sprintf("Domain: %s Method: %s Path: %s RegistedAt: %s", ri.Domain, ri.Method, ri.Path, ri.RegistedAt.String())
|
||||
}
|
||||
|
||||
// Plugin the routes info plugin, holds the routes as RouteInfo objects
|
||||
type Plugin struct {
|
||||
routes []RouteInfo
|
||||
}
|
||||
|
||||
// implement the base IPlugin
|
||||
|
||||
// GetName ...
|
||||
func (r Plugin) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
// GetDescription RoutesInfo gives information about the registed routes
|
||||
func (r Plugin) GetDescription() string {
|
||||
return Name + " gives information about the registed routes.\n"
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// implement the rest of the plugin
|
||||
|
||||
// PostHandle collect the registed routes information
|
||||
func (r *Plugin) PostHandle(route iris.IRoute) {
|
||||
if r.routes == nil {
|
||||
r.routes = make([]RouteInfo, 0)
|
||||
}
|
||||
r.routes = append(r.routes, RouteInfo{route.GetMethod(), route.GetDomain(), route.GetPath(), time.Now()})
|
||||
}
|
||||
|
||||
// All returns all routeinfos
|
||||
// returns a slice
|
||||
func (r Plugin) All() []RouteInfo {
|
||||
return r.routes
|
||||
}
|
||||
|
||||
// ByDomain returns all routeinfos which registed to a specific domain
|
||||
// returns a slice, if nothing founds this slice has 0 len&cap
|
||||
func (r Plugin) ByDomain(domain string) []RouteInfo {
|
||||
var routesByDomain []RouteInfo
|
||||
rlen := len(r.routes)
|
||||
if domain == "localhost" || domain == "127.0.0.1" || domain == ":" {
|
||||
domain = ""
|
||||
}
|
||||
for i := 0; i < rlen; i++ {
|
||||
if r.routes[i].Domain == domain {
|
||||
routesByDomain = append(routesByDomain, r.routes[i])
|
||||
}
|
||||
}
|
||||
return routesByDomain
|
||||
}
|
||||
|
||||
// ByMethod returns all routeinfos by a http method
|
||||
// returns a slice, if nothing founds this slice has 0 len&cap
|
||||
func (r Plugin) ByMethod(method string) []RouteInfo {
|
||||
var routesByMethod []RouteInfo
|
||||
rlen := len(r.routes)
|
||||
method = strings.ToUpper(method)
|
||||
for i := 0; i < rlen; i++ {
|
||||
if r.routes[i].Method == method {
|
||||
routesByMethod = append(routesByMethod, r.routes[i])
|
||||
}
|
||||
}
|
||||
return routesByMethod
|
||||
}
|
||||
|
||||
// ByPath returns all routeinfos by a path
|
||||
// maybe one path is the same on GET and POST ( for example /login GET, /login POST)
|
||||
// because of that it returns a slice and not only one RouteInfo
|
||||
// returns a slice, if nothing founds this slice has 0 len&cap
|
||||
func (r Plugin) ByPath(path string) []RouteInfo {
|
||||
var routesByPath []RouteInfo
|
||||
rlen := len(r.routes)
|
||||
for i := 0; i < rlen; i++ {
|
||||
if r.routes[i].Path == path {
|
||||
routesByPath = append(routesByPath, r.routes[i])
|
||||
}
|
||||
}
|
||||
return routesByPath
|
||||
}
|
||||
|
||||
// ByDomainAndMethod returns all routeinfos registed to a specific domain and has specific http method
|
||||
// returns a slice, if nothing founds this slice has 0 len&cap
|
||||
func (r Plugin) ByDomainAndMethod(domain string, method string) []RouteInfo {
|
||||
var routesByDomainAndMethod []RouteInfo
|
||||
rlen := len(r.routes)
|
||||
method = strings.ToUpper(method)
|
||||
if domain == "localhost" || domain == "127.0.0.1" || domain == ":" {
|
||||
domain = ""
|
||||
}
|
||||
|
||||
for i := 0; i < rlen; i++ {
|
||||
if r.routes[i].Method == method && r.routes[i].Domain == domain {
|
||||
routesByDomainAndMethod = append(routesByDomainAndMethod, r.routes[i])
|
||||
}
|
||||
}
|
||||
return routesByDomainAndMethod
|
||||
}
|
||||
|
||||
// ByMethodAndPath returns a single *RouteInfo which has specific http method and path
|
||||
// returns only the first match
|
||||
// if nothing founds returns nil
|
||||
func (r Plugin) ByMethodAndPath(method string, path string) *RouteInfo {
|
||||
|
||||
rlen := len(r.routes)
|
||||
for i := 0; i < rlen; i++ {
|
||||
if r.routes[i].Method == method && r.routes[i].Path == path {
|
||||
return &r.routes[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
// RoutesInfo returns the Plugin, same as New()
|
||||
func RoutesInfo() *Plugin {
|
||||
return &Plugin{}
|
||||
}
|
||||
|
||||
// New returns the Plugin, same as RoutesInfo()
|
||||
func New() *Plugin {
|
||||
return &Plugin{}
|
||||
}
|
|
@ -55,7 +55,7 @@ func main(){
|
|||
ts = typescript.DefaultOptions()
|
||||
//
|
||||
|
||||
iris.Plugins().Add(typescript.New(ts)) //or with the default options just: typescript.New()
|
||||
iris.Plugins.Add(typescript.New(ts)) //or with the default options just: typescript.New()
|
||||
|
||||
iris.Get("/", func (ctx *iris.Context){})
|
||||
|
||||
|
|
|
@ -6,10 +6,10 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/iris-contrib/npm"
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/logger"
|
||||
"github.com/kataras/iris/npm"
|
||||
"github.com/kataras/iris/plugin/editor"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
@ -46,7 +46,7 @@ type (
|
|||
Plugin struct {
|
||||
options Options
|
||||
// taken from Activate
|
||||
pluginContainer iris.IPluginContainer
|
||||
pluginContainer iris.PluginContainer
|
||||
// taken at the PreListen
|
||||
logger *logger.Logger
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ func New(_opt ...Options) *Plugin {
|
|||
// implement the IPlugin & IPluginPreListen
|
||||
|
||||
// Activate ...
|
||||
func (t *Plugin) Activate(container iris.IPluginContainer) error {
|
||||
func (t *Plugin) Activate(container iris.PluginContainer) error {
|
||||
t.pluginContainer = container
|
||||
return nil
|
||||
}
|
||||
|
@ -122,8 +122,8 @@ func (t *Plugin) GetDescription() string {
|
|||
}
|
||||
|
||||
// PreListen ...
|
||||
func (t *Plugin) PreListen(s *iris.Iris) {
|
||||
t.logger = s.Logger()
|
||||
func (t *Plugin) PreListen(s *iris.Framework) {
|
||||
t.logger = s.Logger
|
||||
t.start()
|
||||
}
|
||||
|
||||
|
|
|
@ -152,15 +152,11 @@ func getPongoContext(templateData interface{}) pongo2.Context {
|
|||
return nil
|
||||
}
|
||||
|
||||
if v, isMap := templateData.(map[string]interface{}); isMap {
|
||||
return v
|
||||
}
|
||||
|
||||
if contextData, isPongoContext := templateData.(pongo2.Context); isPongoContext {
|
||||
return contextData
|
||||
}
|
||||
|
||||
return nil
|
||||
return templateData.(map[string]interface{})
|
||||
}
|
||||
|
||||
func (p *Engine) fromCache(relativeName string) *pongo2.Template {
|
||||
|
|
|
@ -132,11 +132,23 @@ func RegisterSharedFunc(name string, fn interface{}) {
|
|||
sharedFuncs[name] = fn
|
||||
}
|
||||
|
||||
// RegisterSharedFuncs registers functionalities that should be inherited from all supported template engines
|
||||
func RegisterSharedFuncs(theFuncs map[string]interface{}) {
|
||||
if sharedFuncs == nil || len(sharedFuncs) == 0 {
|
||||
sharedFuncs = theFuncs
|
||||
return
|
||||
}
|
||||
for k, v := range theFuncs {
|
||||
sharedFuncs[k] = v
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Render renders a template using the context's writer
|
||||
func (t *Template) Render(ctx context.IContext, name string, binding interface{}, layout ...string) (err error) {
|
||||
|
||||
if t == nil { // No engine was given but .Render was called
|
||||
ctx.WriteHTML(403, "<b> Iris </b> <br/> Templates are disabled via config.NoEngine, check your iris' configuration please.")
|
||||
ctx.HTML(403, "<b> Iris </b> <br/> Templates are disabled via config.NoEngine, check your iris' configuration please.")
|
||||
return fmt.Errorf("[IRIS TEMPLATES] Templates are disabled via config.NoEngine, check your iris' configuration please.\n")
|
||||
}
|
||||
|
||||
|
|
280
route.go
280
route.go
|
@ -1,280 +0,0 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
// IRoute is the interface which the Route should implements
|
||||
// it useful to have it as an interface because this interface is passed to the plugins
|
||||
IRoute interface {
|
||||
GetMethod() string
|
||||
GetDomain() string
|
||||
GetPath() string
|
||||
GetName() string
|
||||
// Name sets the name of the route
|
||||
Name(string) IRoute
|
||||
GetMiddleware() Middleware
|
||||
HasCors() bool
|
||||
ParsePath(...interface{}) string
|
||||
ParseURI(...interface{}) string
|
||||
}
|
||||
|
||||
// RouteNameFunc is returned to from route handle
|
||||
RouteNameFunc func(string) IRoute
|
||||
|
||||
// Route contains basic and temporary info about the route in order to be stored to the tree
|
||||
Route struct {
|
||||
method string
|
||||
domain string
|
||||
fullpath string
|
||||
// the name of the route, the default name is just the registed path.
|
||||
name string
|
||||
middleware Middleware
|
||||
// station
|
||||
station *Iris
|
||||
// this is used to convert /mypath/:aparam/:something to -> /mypath/%v/%v and /mypath/* -> mypath/%v
|
||||
// we use %v to escape from the conversions between strings,booleans and integers.
|
||||
// used inside custom html template func 'url'
|
||||
formattedPath string
|
||||
// formattedParts is just the formattedPath count, used to see if we have one path parameter then the url's function arguments will be passed as one string to the %v
|
||||
formattedParts int
|
||||
}
|
||||
)
|
||||
|
||||
var _ IRoute = &Route{}
|
||||
|
||||
// NewRoute creates, from a path string, and a slice of HandlerFunc
|
||||
func NewRoute(method string, registedPath string, middleware Middleware, station *Iris) *Route {
|
||||
domain := ""
|
||||
//dirdy but I'm not touching this again:P
|
||||
if registedPath[0] != SlashByte && strings.Contains(registedPath, ".") && (strings.IndexByte(registedPath, SlashByte) == -1 || strings.IndexByte(registedPath, SlashByte) > strings.IndexByte(registedPath, '.')) {
|
||||
//means that is a path with domain
|
||||
//we have to extract the domain
|
||||
|
||||
//find the first '/'
|
||||
firstSlashIndex := strings.IndexByte(registedPath, SlashByte)
|
||||
|
||||
//firt of all remove the first '/' if that exists and we have domain
|
||||
if firstSlashIndex == 0 {
|
||||
//e.g /admin.ideopod.com/hey
|
||||
//then just remove the first slash and re-execute the NewRoute and return it
|
||||
registedPath = registedPath[1:]
|
||||
return NewRoute(method, registedPath, middleware, station)
|
||||
}
|
||||
//if it's just the domain, then set it(registedPath) as the domain
|
||||
//and after set the registedPath to a slash '/' for the path part
|
||||
if firstSlashIndex == -1 {
|
||||
domain = registedPath
|
||||
registedPath = Slash
|
||||
} else {
|
||||
//we have a domain + path
|
||||
domain = registedPath[0:firstSlashIndex]
|
||||
registedPath = registedPath[len(domain):]
|
||||
}
|
||||
|
||||
}
|
||||
r := &Route{method: method, domain: domain, fullpath: registedPath, middleware: middleware, name: registedPath, formattedPath: registedPath, station: station}
|
||||
r.formatPath()
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Route) isWildcard() bool {
|
||||
return r.domain != r.station.server.Hostname() && r.domain == PrefixDynamicSubdomain
|
||||
}
|
||||
|
||||
func (r *Route) formatPath() {
|
||||
// we don't care about performance here, no runtime func.
|
||||
|
||||
n1Len := strings.Count(r.fullpath, ":")
|
||||
isMatchEverything := r.fullpath[len(r.fullpath)-1] == MatchEverythingByte
|
||||
if n1Len == 0 && !isMatchEverything {
|
||||
// its a static
|
||||
return
|
||||
}
|
||||
if n1Len == 0 && isMatchEverything {
|
||||
//if we have something like: /mypath/anything/* -> /mypatch/anything/%v
|
||||
r.formattedPath = r.fullpath[0:len(r.fullpath)-2] + "%v"
|
||||
r.formattedParts++
|
||||
return
|
||||
}
|
||||
|
||||
tempPath := r.fullpath
|
||||
|
||||
splittedN1 := strings.Split(r.fullpath, "/")
|
||||
|
||||
for _, v := range splittedN1 {
|
||||
if len(v) > 0 {
|
||||
if v[0] == ':' || v[0] == MatchEverythingByte {
|
||||
r.formattedParts++
|
||||
tempPath = strings.Replace(tempPath, v, "%v", -1) // n1Len, but let it we don't care about performance here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
r.formattedPath = tempPath
|
||||
}
|
||||
|
||||
// GetMethod returns the http method
|
||||
func (r Route) GetMethod() string {
|
||||
return r.method
|
||||
}
|
||||
|
||||
// GetDomain returns the registed domain which this route is ( if none, is "" which is means "localhost"/127.0.0.1)
|
||||
func (r Route) GetDomain() string {
|
||||
return r.domain
|
||||
}
|
||||
|
||||
// GetPath returns the full registed path
|
||||
func (r Route) GetPath() string {
|
||||
return r.fullpath
|
||||
}
|
||||
|
||||
// GetName returns the name of the route
|
||||
func (r Route) GetName() string {
|
||||
return r.name
|
||||
}
|
||||
|
||||
// Name sets the route's name
|
||||
func (r *Route) Name(newName string) IRoute {
|
||||
r.name = newName
|
||||
return r
|
||||
}
|
||||
|
||||
// GetMiddleware returns the chain of the []HandlerFunc registed to this Route
|
||||
func (r Route) GetMiddleware() Middleware {
|
||||
return r.middleware
|
||||
}
|
||||
|
||||
// HasCors check if middleware passsed to a route has cors
|
||||
func (r *Route) HasCors() bool {
|
||||
return RouteConflicts(r, "httpmethod")
|
||||
}
|
||||
|
||||
// ParsePath used to check arguments with the route's named parameters and return the correct url
|
||||
// if parse failed returns empty string
|
||||
func (r *Route) ParsePath(args ...interface{}) string {
|
||||
argsLen := len(args)
|
||||
|
||||
// we have named parameters but arguments not given
|
||||
if argsLen == 0 && r.formattedParts > 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// we have arguments but they are much more than the named parameters
|
||||
|
||||
// 1 check if we have /*, if yes then join all arguments to one as path and pass that as parameter
|
||||
if argsLen > r.formattedParts {
|
||||
if r.fullpath[len(r.fullpath)-1] == MatchEverythingByte {
|
||||
// we have to convert each argument to a string in this case
|
||||
|
||||
argsString := make([]string, argsLen, argsLen)
|
||||
|
||||
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:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameter := strings.Join(argsString, Slash)
|
||||
result := fmt.Sprintf(r.formattedPath, parameter)
|
||||
return result
|
||||
}
|
||||
// 2 if !1 return false
|
||||
return ""
|
||||
}
|
||||
|
||||
arguments := args[0:]
|
||||
|
||||
// check for arrays
|
||||
for i, v := range arguments {
|
||||
if arr, ok := v.([]string); ok {
|
||||
if len(arr) > 0 {
|
||||
interfaceArr := make([]interface{}, len(arr))
|
||||
for j, sv := range arr {
|
||||
interfaceArr[j] = sv
|
||||
}
|
||||
arguments[i] = interfaceArr[0]
|
||||
arguments = append(arguments, interfaceArr[1:]...)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf(r.formattedPath, arguments...)
|
||||
}
|
||||
|
||||
// ParseURI returns the subdomain+ host + ParsePath(...optional named parameters if route is dynamic)
|
||||
// returns an empty string if parse is failed
|
||||
func (r *Route) ParseURI(args ...interface{}) (uri string) {
|
||||
scheme := "http://"
|
||||
if r.station.server.IsSecure() {
|
||||
scheme = "https://"
|
||||
}
|
||||
|
||||
host := r.station.server.VirtualHost()
|
||||
arguments := args[0:]
|
||||
|
||||
// join arrays as arguments
|
||||
for i, v := range arguments {
|
||||
if arr, ok := v.([]string); ok {
|
||||
if len(arr) > 0 {
|
||||
interfaceArr := make([]interface{}, len(arr))
|
||||
for j, sv := range arr {
|
||||
interfaceArr[j] = sv
|
||||
}
|
||||
arguments[i] = interfaceArr[0]
|
||||
arguments = append(arguments, interfaceArr[1:]...)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// if it's dynamic subdomain then the first argument is the subdomain part
|
||||
if r.isWildcard() {
|
||||
if len(arguments) == 0 { // it's a wildcard subdomain but not arguments
|
||||
return
|
||||
}
|
||||
|
||||
if subdomain, ok := arguments[0].(string); ok {
|
||||
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
|
||||
}
|
||||
|
||||
arguments = arguments[1:]
|
||||
}
|
||||
|
||||
if parsedPath := r.ParsePath(arguments...); parsedPath != "" {
|
||||
uri = scheme + host + parsedPath
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// RouteConflicts checks for route's middleware conflicts
|
||||
func RouteConflicts(r *Route, with string) bool {
|
||||
for _, h := range r.middleware {
|
||||
if m, ok := h.(interface {
|
||||
Conflicts() string
|
||||
}); ok {
|
||||
if c := m.Conflicts(); c == with {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
308
router.go
308
router.go
|
@ -1,308 +0,0 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"net/http/pprof"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
// ParameterStartByte is very used on the node, it's just contains the byte for the ':' rune/char
|
||||
ParameterStartByte = byte(':')
|
||||
// SlashByte is just a byte of '/' rune/char
|
||||
SlashByte = byte('/')
|
||||
// Slash is just a string of "/"
|
||||
Slash = "/"
|
||||
// MatchEverythingByte is just a byte of '*" rune/char
|
||||
MatchEverythingByte = byte('*')
|
||||
// PrefixDynamicSubdomain is the prefix which dynamic subdomains are registed to, as virtual. Used internaly by Iris but good to know.
|
||||
PrefixDynamicSubdomain = "www.iris_subd0mAin.iris"
|
||||
|
||||
// HTTP Methods(1)
|
||||
|
||||
// MethodGet "GET"
|
||||
MethodGet = "GET"
|
||||
// MethodPost "POST"
|
||||
MethodPost = "POST"
|
||||
// MethodPut "PUT"
|
||||
MethodPut = "PUT"
|
||||
// MethodDelete "DELETE"
|
||||
MethodDelete = "DELETE"
|
||||
// MethodConnect "CONNECT"
|
||||
MethodConnect = "CONNECT"
|
||||
// MethodHead "HEAD"
|
||||
MethodHead = "HEAD"
|
||||
// MethodPatch "PATCH"
|
||||
MethodPatch = "PATCH"
|
||||
// MethodOptions "OPTIONS"
|
||||
MethodOptions = "OPTIONS"
|
||||
// MethodTrace "TRACE"
|
||||
MethodTrace = "TRACE"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// HTTP Methods(2)
|
||||
|
||||
// MethodConnectBytes []byte("CONNECT")
|
||||
MethodConnectBytes = []byte(MethodConnect)
|
||||
// AllMethods "GET", "POST", "PUT", "DELETE", "CONNECT", "HEAD", "PATCH", "OPTIONS", "TRACE"
|
||||
AllMethods = [...]string{"GET", "POST", "PUT", "DELETE", "CONNECT", "HEAD", "PATCH", "OPTIONS", "TRACE"}
|
||||
)
|
||||
|
||||
// router internal is the route serving service, one router per server
|
||||
type router struct {
|
||||
*GardenParty
|
||||
*HTTPErrorContainer
|
||||
station *Iris
|
||||
garden *Garden
|
||||
methodMatch func(m1, m2 string) bool
|
||||
getRequestPath func(*fasthttp.RequestCtx) []byte
|
||||
// routes useful information, this info can be used to make custom links inside templates
|
||||
// the route's information (can be) changed after its registration
|
||||
lookups []IRoute
|
||||
ServeRequest func(reqCtx *fasthttp.RequestCtx)
|
||||
// errorPool is responsible to get the Context to handle not found errors
|
||||
errorPool sync.Pool
|
||||
//it's true when optimize already ran
|
||||
optimized bool
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// methodMatchCorsFunc is sets the methodMatch when cors enabled (look router.optimize), it's allowing OPTIONS method to all other methods except GET
|
||||
func methodMatchCorsFunc(m1, reqMethod string) bool {
|
||||
return m1 == reqMethod || reqMethod == MethodOptions //(m1 != MethodGet && reqMethod == MethodOptions)
|
||||
}
|
||||
|
||||
// methodMatchFunc for normal method match
|
||||
func methodMatchFunc(m1, m2 string) bool {
|
||||
return m1 == m2
|
||||
}
|
||||
|
||||
func getRequestPathDefault(reqCtx *fasthttp.RequestCtx) []byte {
|
||||
// default to escape then
|
||||
return reqCtx.Path()
|
||||
}
|
||||
|
||||
// newRouter creates and returns an empty router
|
||||
func newRouter(station *Iris) *router {
|
||||
r := &router{
|
||||
station: station,
|
||||
garden: &Garden{},
|
||||
methodMatch: methodMatchFunc,
|
||||
getRequestPath: getRequestPathDefault,
|
||||
lookups: make([]IRoute, 0),
|
||||
HTTPErrorContainer: defaultHTTPErrors(),
|
||||
GardenParty: &GardenParty{relativePath: "/", station: station, root: true},
|
||||
errorPool: station.newContextPool()}
|
||||
|
||||
r.ServeRequest = r.serveFunc
|
||||
|
||||
return r
|
||||
|
||||
}
|
||||
|
||||
// addRoute is a middleware between router and garden
|
||||
// it just calls the garden's Plant method
|
||||
// is 'thread-safe'
|
||||
func (r *router) addRoute(route IRoute) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.lookups = append(r.lookups, route)
|
||||
r.garden.Plant(r.station, route)
|
||||
}
|
||||
|
||||
// RouteByName returns a route by its name,if not found then returns a route with empty path
|
||||
// Note that the searching is case-sensitive
|
||||
func (r *router) RouteByName(routeName string) IRoute {
|
||||
for _, route := range r.lookups {
|
||||
if route.GetName() == routeName {
|
||||
return route
|
||||
}
|
||||
}
|
||||
return &Route{}
|
||||
}
|
||||
|
||||
// UriOf returns the parsed URI of a route
|
||||
// receives two parameters
|
||||
// the first is the route's name (string)
|
||||
// the second is a variadic, if the route is dynamic (receives named parameters) then pass the value of these parameters here
|
||||
// overview of the result is: scheme(http or https if ListenTLS)/yourhost.com:PORT/profile/theusername/friends/theid
|
||||
//
|
||||
// example /profile/:username/friends/:friendId with name "profile" -> .UriOf("profile","kataras",8) will give http://127.0.0.1:8080/profile/kataras/friends/8
|
||||
func (r *router) UriOf(routeName string, args ...interface{}) (string, error) {
|
||||
route := r.RouteByName(routeName)
|
||||
// check if not found
|
||||
if route.GetMethod() == "" {
|
||||
return "", ErrRenderRouteNotFound.Format(routeName)
|
||||
}
|
||||
|
||||
return route.ParseURI(args...), nil
|
||||
}
|
||||
|
||||
//check if any tree has cors setted to true, means that cors middleware is added
|
||||
func (r *router) cors() (has bool) {
|
||||
r.garden.visitAll(func(i int, tree *tree) {
|
||||
if tree.cors {
|
||||
has = true
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// check if any tree has subdomains
|
||||
func (r *router) hosts() (has bool) {
|
||||
r.garden.visitAll(func(i int, tree *tree) {
|
||||
if tree.hosts {
|
||||
has = true
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// optimize runs once before listen, it checks if cors or hosts enabled and make the necessary changes to the Router itself
|
||||
func (r *router) optimize() {
|
||||
if r.optimized {
|
||||
return
|
||||
}
|
||||
|
||||
if r.cors() {
|
||||
r.methodMatch = methodMatchCorsFunc
|
||||
}
|
||||
|
||||
// For performance only,in order to not check at runtime for hosts and subdomains, I think it's better to do this:
|
||||
if r.hosts() {
|
||||
r.ServeRequest = r.serveDomainFunc
|
||||
}
|
||||
|
||||
//if PathEscape disabled, then take the raw URI
|
||||
if r.station.config.DisablePathEscape {
|
||||
r.getRequestPath = func(reqCtx *fasthttp.RequestCtx) []byte {
|
||||
// RequestURI fixes the https://github.com/kataras/iris/issues/135
|
||||
return reqCtx.RequestURI()
|
||||
}
|
||||
}
|
||||
|
||||
// set the debug profiling handlers if Profile enabled, before the server startup, not earlier
|
||||
if r.station.config.Profile && r.station.config.ProfilePath != "" {
|
||||
debugPath := r.station.config.ProfilePath
|
||||
|
||||
htmlMiddleware := func(ctx *Context) {
|
||||
ctx.SetContentType(ContentHTML + r.station.rest.CompiledCharset)
|
||||
ctx.Next()
|
||||
}
|
||||
|
||||
indexHandler := ToHandlerFunc(pprof.Index)
|
||||
cmdlineHandler := ToHandlerFunc(pprof.Cmdline)
|
||||
profileHandler := ToHandlerFunc(pprof.Profile)
|
||||
symbolHandler := ToHandlerFunc(pprof.Symbol)
|
||||
|
||||
goroutineHandler := ToHandlerFunc(pprof.Handler("goroutine"))
|
||||
heapHandler := ToHandlerFunc(pprof.Handler("heap"))
|
||||
threadcreateHandler := ToHandlerFunc(pprof.Handler("threadcreate"))
|
||||
debugBlockHandler := ToHandlerFunc(pprof.Handler("block"))
|
||||
|
||||
r.Get(debugPath+"/*action", htmlMiddleware, func(ctx *Context) {
|
||||
action := ctx.Param("action")
|
||||
if len(action) > 1 {
|
||||
if strings.Contains(action, "cmdline") {
|
||||
cmdlineHandler.Serve((ctx))
|
||||
} else if strings.Contains(action, "profile") {
|
||||
profileHandler.Serve(ctx)
|
||||
} else if strings.Contains(action, "symbol") {
|
||||
symbolHandler.Serve(ctx)
|
||||
} else if strings.Contains(action, "goroutine") {
|
||||
goroutineHandler.Serve(ctx)
|
||||
} else if strings.Contains(action, "heap") {
|
||||
heapHandler.Serve(ctx)
|
||||
} else if strings.Contains(action, "threadcreate") {
|
||||
threadcreateHandler.Serve(ctx)
|
||||
} else if strings.Contains(action, "debug/block") {
|
||||
debugBlockHandler.Serve(ctx)
|
||||
}
|
||||
} else {
|
||||
indexHandler.Serve(ctx)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
r.optimized = true
|
||||
}
|
||||
|
||||
// notFound internal method, it justs takes the context from pool ( in order to have the custom errors available) and procedure a Not Found 404 error
|
||||
// this is being called when no route was found used on the ServeRequest.
|
||||
func (r *router) notFound(reqCtx *fasthttp.RequestCtx) {
|
||||
ctx := r.errorPool.Get().(*Context)
|
||||
ctx.Reset(reqCtx)
|
||||
ctx.NotFound()
|
||||
r.errorPool.Put(ctx)
|
||||
}
|
||||
|
||||
//************************************************************************************
|
||||
// serveFunc & serveDomainFunc selected on router.optimize, which runs before station's listen
|
||||
// they are not used directly.
|
||||
//************************************************************************************
|
||||
|
||||
// serve finds and serves a route by it's request context
|
||||
// If no route found, it sends an http status 404
|
||||
func (r *router) serveFunc(reqCtx *fasthttp.RequestCtx) {
|
||||
method := utils.BytesToString(reqCtx.Method())
|
||||
tree := r.garden.first
|
||||
path := utils.BytesToString(r.getRequestPath(reqCtx))
|
||||
for tree != nil {
|
||||
if r.methodMatch(tree.method, method) {
|
||||
if !tree.serve(reqCtx, path) {
|
||||
r.notFound(reqCtx)
|
||||
}
|
||||
return
|
||||
}
|
||||
tree = tree.next
|
||||
}
|
||||
//not found, get the first's pool and use that to send a custom http error(if setted)
|
||||
|
||||
r.notFound(reqCtx)
|
||||
|
||||
}
|
||||
|
||||
// serveDomainFunc finds and serves a domain tree's route by it's request context
|
||||
// If no route found, it sends an http status 404
|
||||
func (r *router) serveDomainFunc(reqCtx *fasthttp.RequestCtx) {
|
||||
method := utils.BytesToString(reqCtx.Method())
|
||||
host := utils.BytesToString(reqCtx.Host())
|
||||
fulldomain := ""
|
||||
if strings.Count(host, ".") >= 2 && host != r.station.server.Host() {
|
||||
if portIdx := strings.Index(host, ":"); portIdx != -1 {
|
||||
fulldomain = host[0:portIdx]
|
||||
} else {
|
||||
fulldomain = host
|
||||
}
|
||||
}
|
||||
|
||||
path := utils.BytesToString(r.getRequestPath(reqCtx))
|
||||
tree := r.garden.first
|
||||
for tree != nil {
|
||||
if tree.hosts && tree.domain != "" && fulldomain != "" {
|
||||
if tree.domain == fulldomain { // it's a static subdomain
|
||||
path = fulldomain + path
|
||||
} else if strings.Index(tree.domain, PrefixDynamicSubdomain) != -1 { // it's a dynamic virtual subdomain
|
||||
path = PrefixDynamicSubdomain + path
|
||||
}
|
||||
}
|
||||
|
||||
if r.methodMatch(tree.method, method) {
|
||||
if tree.serve(reqCtx, path) {
|
||||
return
|
||||
}
|
||||
}
|
||||
tree = tree.next
|
||||
}
|
||||
|
||||
//not found, get the first's pool and use that to send a custom http error(if setted)
|
||||
r.notFound(reqCtx)
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
## Package information
|
||||
|
||||
I decide to split the whole server from the main iris package because these files don't depends on any of the iris' types.
|
||||
|
||||
|
||||
**That's it.**
|
|
@ -1,26 +0,0 @@
|
|||
package server
|
||||
|
||||
import "github.com/kataras/iris/errors"
|
||||
|
||||
var (
|
||||
// ErrServerPortAlreadyUsed returns an error with message: 'Server can't run, port is already used'
|
||||
ErrServerPortAlreadyUsed = errors.New("Server can't run, port is already used")
|
||||
// ErrServerAlreadyStarted returns an error with message: 'Server is already started and listening'
|
||||
ErrServerAlreadyStarted = errors.New("Server is already started and listening")
|
||||
// ErrServerOptionsMissing returns an error with message: 'You have to pass iris.ServerOptions'
|
||||
ErrServerOptionsMissing = errors.New("You have to pass iris.ServerOptions")
|
||||
// ErrServerTLSOptionsMissing returns an error with message: 'You have to set CertFile and KeyFile to iris.ServerOptions before ListenTLS'
|
||||
ErrServerTLSOptionsMissing = errors.New("You have to set CertFile and KeyFile to iris.ServerOptions before ListenTLS")
|
||||
// ErrServerIsClosed returns an error with message: 'Can't close the server, propably is already closed or never started'
|
||||
ErrServerIsClosed = errors.New("Can't close the server, propably is already closed or never started")
|
||||
// ErrServerUnknown returns an error with message: 'Unknown reason from Server, please report this as bug!'
|
||||
ErrServerUnknown = errors.New("Unknown reason from Server, please report this as bug!")
|
||||
// ErrParsedAddr returns an error with message: 'ListeningAddr error, for TCP and UDP, the syntax of ListeningAddr is host:port, like 127.0.0.1:8080.
|
||||
// If host is omitted, as in :8080, Listen listens on all available interfaces instead of just the interface with the given host address.
|
||||
// See Dial for more details about address syntax'
|
||||
ErrParsedAddr = errors.New("ListeningAddr error, for TCP and UDP, the syntax of ListeningAddr is host:port, like 127.0.0.1:8080. If host is omitted, as in :8080, Listen listens on all available interfaces instead of just the interface with the given host address. See Dial for more details about address syntax")
|
||||
// ErrServerRemoveUnix returns an error with message: 'Unexpected error when trying to remove unix socket file +filename: +specific error"'
|
||||
ErrServerRemoveUnix = errors.New("Unexpected error when trying to remove unix socket file. Addr: %s | Trace: %s")
|
||||
// ErrServerChmod returns an error with message: 'Cannot chmod +mode for +host:+specific error
|
||||
ErrServerChmod = errors.New("Cannot chmod %#o for %q: %s")
|
||||
)
|
248
server/server.go
248
server/server.go
|
@ -1,248 +0,0 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Server is the IServer's implementation, holds the fasthttp's Server, a net.Listener, the ServerOptions, and the handler
|
||||
// handler is registed at the Station/Iris level
|
||||
type Server struct {
|
||||
*fasthttp.Server
|
||||
listener net.Listener
|
||||
Config config.Server
|
||||
started bool
|
||||
tls bool
|
||||
handler fasthttp.RequestHandler
|
||||
}
|
||||
|
||||
// New returns a pointer to a Server object, and set it's options if any, nothing more
|
||||
func New(cfg ...config.Server) *Server {
|
||||
c := config.DefaultServer().Merge(cfg)
|
||||
s := &Server{Server: &fasthttp.Server{Name: config.ServerName}, Config: c}
|
||||
|
||||
s.Config.ListeningAddr = parseAddr(s.Config.ListeningAddr)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// SetHandler sets the handler in order to listen on new requests, this is done at the Station/Iris level
|
||||
func (s *Server) SetHandler(h fasthttp.RequestHandler) {
|
||||
s.handler = h
|
||||
if s.Server != nil {
|
||||
s.Server.Handler = s.handler
|
||||
}
|
||||
}
|
||||
|
||||
// Handler returns the fasthttp.RequestHandler which is registed to the Server
|
||||
func (s *Server) Handler() fasthttp.RequestHandler {
|
||||
return s.handler
|
||||
}
|
||||
|
||||
// IsListening returns true if server is listening/started, otherwise false
|
||||
func (s *Server) IsListening() bool {
|
||||
return s.started
|
||||
}
|
||||
|
||||
// IsSecure returns true if server uses TLS, otherwise false
|
||||
func (s *Server) IsSecure() bool {
|
||||
return s.tls
|
||||
}
|
||||
|
||||
// Listener returns the net.Listener which this server (is) listening to
|
||||
func (s *Server) Listener() net.Listener {
|
||||
return s.listener
|
||||
}
|
||||
|
||||
// Host returns the Listener().Addr().String(), if server is not listening it returns the config.ListeningAddr
|
||||
func (s *Server) Host() (host string) {
|
||||
if s.IsListening() {
|
||||
return s.Listener().Addr().String()
|
||||
} else {
|
||||
return s.Config.ListeningAddr
|
||||
}
|
||||
}
|
||||
|
||||
// VirtualHost returns the s.Config.ListeningAddr, if host provided else returns the Listener's (Host())
|
||||
//
|
||||
// Note: currently this is used only on iris/route.ParseURI.
|
||||
//
|
||||
func (s *Server) VirtualHost() (host string) {
|
||||
// we always have at least the :PORT because of parseAddr, so we just
|
||||
// check if we have anything before PORT
|
||||
a := s.Config.ListeningAddr
|
||||
if len(a[0:strings.IndexByte(a, ':')]) > 0 {
|
||||
return a
|
||||
} else {
|
||||
return s.Host()
|
||||
}
|
||||
}
|
||||
|
||||
// Hostname returns the hostname part only, if host == 0.0.0.0:8080 it will return the 0.0.0.0
|
||||
// if server is not listening it returns the config.ListeningAddr's hostname part
|
||||
func (s *Server) Hostname() (hostname string) {
|
||||
if s.IsListening() {
|
||||
fullhost := s.Listener().Addr().String()
|
||||
hostname = fullhost[0:strings.IndexByte(fullhost, ':')] // no the port
|
||||
} else {
|
||||
fullhost := s.Config.ListeningAddr
|
||||
if idx := strings.IndexByte(fullhost, ':'); idx > 1 { // at least after second char
|
||||
hostname = hostname[0:idx]
|
||||
} else {
|
||||
hostname = "0.0.0.0"
|
||||
}
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//Serve just serves a listener, it is a blocking action, plugin.PostListen is not fired here.
|
||||
func (s *Server) Serve(l net.Listener) error {
|
||||
s.listener = l
|
||||
return s.Server.Serve(l)
|
||||
}
|
||||
|
||||
// listen starts the process of listening to the new requests
|
||||
func (s *Server) listen() (err error) {
|
||||
|
||||
if s.started {
|
||||
err = ErrServerAlreadyStarted.Return()
|
||||
return
|
||||
}
|
||||
s.listener, err = net.Listen("tcp4", s.Config.ListeningAddr)
|
||||
|
||||
if err != nil {
|
||||
err = ErrServerPortAlreadyUsed.Return()
|
||||
return
|
||||
}
|
||||
|
||||
//Non-block way here because I want the plugin's PostListen ability...
|
||||
go s.Server.Serve(s.listener)
|
||||
|
||||
s.started = true
|
||||
s.tls = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// listenTLS starts the process of listening to the new requests using TLS, keyfile and certfile are given before this method fires
|
||||
func (s *Server) listenTLS() (err error) {
|
||||
|
||||
if s.started {
|
||||
err = ErrServerAlreadyStarted.Return()
|
||||
return
|
||||
}
|
||||
|
||||
if s.Config.CertFile == "" || s.Config.KeyFile == "" {
|
||||
err = ErrServerTLSOptionsMissing.Return()
|
||||
return
|
||||
}
|
||||
|
||||
s.listener, err = net.Listen("tcp4", s.Config.ListeningAddr)
|
||||
|
||||
if err != nil {
|
||||
err = ErrServerPortAlreadyUsed.Return()
|
||||
return
|
||||
}
|
||||
|
||||
go s.Server.ServeTLS(s.listener, s.Config.CertFile, s.Config.KeyFile)
|
||||
|
||||
s.started = true
|
||||
s.tls = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// listenUnix starts the process of listening to the new requests using a 'socket file', this works only on unix
|
||||
func (s *Server) listenUnix() (err error) {
|
||||
|
||||
if s.started {
|
||||
err = ErrServerAlreadyStarted.Return()
|
||||
return
|
||||
}
|
||||
|
||||
mode := s.Config.Mode
|
||||
|
||||
//this code is from fasthttp ListenAndServeUNIX, I extracted it because we need the tcp.Listener
|
||||
if errOs := os.Remove(s.Config.ListeningAddr); errOs != nil && !os.IsNotExist(errOs) {
|
||||
err = ErrServerRemoveUnix.Format(s.Config.ListeningAddr, errOs.Error())
|
||||
return
|
||||
}
|
||||
//
|
||||
s.listener, err = net.Listen("unix", s.Config.ListeningAddr)
|
||||
|
||||
if err != nil {
|
||||
err = ErrServerPortAlreadyUsed.Return()
|
||||
return
|
||||
}
|
||||
|
||||
if err = os.Chmod(s.Config.ListeningAddr, mode); err != nil {
|
||||
err = ErrServerChmod.Format(mode, s.Config.ListeningAddr, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
s.Server.Handler = s.handler
|
||||
go s.Server.Serve(s.listener)
|
||||
|
||||
s.started = true
|
||||
s.tls = false
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// OpenServer opens/starts/runs/listens (to) the server, listenTLS if Cert && Key is registed, listenUnix if Mode is registed, otherwise listen
|
||||
// instead of return an error this is panics on any server's error
|
||||
func (s *Server) OpenServer() (err error) {
|
||||
if s.Config.CertFile != "" && s.Config.KeyFile != "" {
|
||||
err = s.listenTLS()
|
||||
} else if s.Config.Mode > 0 {
|
||||
err = s.listenUnix()
|
||||
} else {
|
||||
err = s.listen()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CloseServer closes the server
|
||||
func (s *Server) CloseServer() error {
|
||||
|
||||
if !s.started {
|
||||
return ErrServerIsClosed.Return()
|
||||
}
|
||||
|
||||
if s.listener != nil {
|
||||
s.started = false
|
||||
return s.listener.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseAddr gets a slice of string and returns the address of which the Iris' server can listen
|
||||
func parseAddr(fullHostOrPort ...string) string {
|
||||
|
||||
if len(fullHostOrPort) > 1 {
|
||||
fullHostOrPort = fullHostOrPort[0:1]
|
||||
}
|
||||
addr := config.DefaultServerAddr // default address
|
||||
// if nothing passed, then use environment's port (if any) or just :8080
|
||||
if len(fullHostOrPort) == 0 {
|
||||
if envPort := os.Getenv("PORT"); len(envPort) > 0 {
|
||||
addr = ":" + envPort
|
||||
}
|
||||
|
||||
} else if len(fullHostOrPort) == 1 {
|
||||
addr = fullHostOrPort[0]
|
||||
if strings.IndexRune(addr, ':') == -1 {
|
||||
//: doesn't found on the given address, so maybe it's only a port
|
||||
addr = ":" + addr
|
||||
}
|
||||
}
|
||||
|
||||
return addr
|
||||
}
|
|
@ -21,7 +21,6 @@ var (
|
|||
func register() {
|
||||
// the actual work is here.
|
||||
Provider.NewStore = func(sessionId string, cookieLifeDuration time.Duration) store.IStore {
|
||||
//println("memory.go:49-> requesting new memory store with sessionid: " + sessionId)
|
||||
return &Store{sid: sessionId, lastAccessedTime: time.Now(), values: make(map[interface{}]interface{}, 0)}
|
||||
}
|
||||
sessions.Register(Provider)
|
||||
|
|
|
@ -4,7 +4,12 @@ import "github.com/kataras/iris/config"
|
|||
|
||||
// New creates & returns a new Manager and start its GC
|
||||
func New(cfg ...config.Sessions) *Manager {
|
||||
manager, err := newManager(config.DefaultSessions().Merge(cfg))
|
||||
c := config.DefaultSessions().Merge(cfg)
|
||||
// If provider is empty then return nil manager, means that the sessions are disabled
|
||||
if c.Provider == "" {
|
||||
return nil
|
||||
}
|
||||
manager, err := newManager(c)
|
||||
if err != nil {
|
||||
panic(err.Error()) // we have to panic here because we will start GC after and if provider is nil then many panics will come
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"github.com/gavv/httpexpect"
|
||||
"github.com/gavv/httpexpect/fasthttpexpect"
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
)
|
||||
|
||||
var notFoundMessage = "Iris custom message for 404 not found"
|
||||
|
@ -46,15 +45,7 @@ func TestCustomErrors(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
api.PreListen(config.Server{ListeningAddr: ""})
|
||||
|
||||
// create httpexpect instance that will call fasthtpp.RequestHandler directly
|
||||
e := httpexpect.WithConfig(httpexpect.Config{
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
Client: fasthttpexpect.NewBinder(api.ServeRequest),
|
||||
})
|
||||
// first register the custom errors
|
||||
|
||||
// register the custom errors
|
||||
api.OnError(404, func(ctx *iris.Context) {
|
||||
ctx.Write("%s", notFoundMessage)
|
||||
})
|
||||
|
@ -63,6 +54,12 @@ func TestCustomErrors(t *testing.T) {
|
|||
ctx.Write("%s", internalServerMessage)
|
||||
})
|
||||
|
||||
// create httpexpect instance that will call fasthtpp.RequestHandler directly
|
||||
e := httpexpect.WithConfig(httpexpect.Config{
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
Client: fasthttpexpect.NewBinder(api.NoListen().Handler),
|
||||
})
|
||||
|
||||
// run the tests
|
||||
for _, r := range routesCustomErrors {
|
||||
e.Request(r.Method, r.RequestPath).
|
||||
|
|
53
tests/party_test.go
Normal file
53
tests/party_test.go
Normal file
|
@ -0,0 +1,53 @@
|
|||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gavv/httpexpect"
|
||||
"github.com/gavv/httpexpect/fasthttpexpect"
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
func TestSimpleParty(t *testing.T) {
|
||||
h := func(c *iris.Context) { c.WriteString(c.HostString() + c.PathString()) }
|
||||
|
||||
/*
|
||||
// subdomain first, but this test will fail on your machine, so I just commend it, you can imagine what will be
|
||||
party2 := iris.Party("kataras.")
|
||||
{
|
||||
party2.Get("/", h)
|
||||
party2.Get("/path1", h)
|
||||
party2.Get("/path2", h)
|
||||
party2.Get("/namedpath/:param1/something/:param2", h)
|
||||
party2.Get("/namedpath/:param1/something/:param2/else", h)
|
||||
}*/
|
||||
|
||||
// simple
|
||||
party1 := iris.Party("/party1")
|
||||
{
|
||||
party1.Get("/", h)
|
||||
party1.Get("/path1", h)
|
||||
party1.Get("/path2", h)
|
||||
party1.Get("/namedpath/:param1/something/:param2", h)
|
||||
party1.Get("/namedpath/:param1/something/:param2/else", h)
|
||||
}
|
||||
|
||||
// create httpexpect instance that will call fasthtpp.RequestHandler directly
|
||||
e := httpexpect.WithConfig(httpexpect.Config{
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
Client: fasthttpexpect.NewBinder(iris.NoListen().Handler),
|
||||
})
|
||||
|
||||
request := func(reqPath string) {
|
||||
e.Request("GET", reqPath).
|
||||
Expect().
|
||||
Status(iris.StatusOK).Body().Equal(reqPath)
|
||||
}
|
||||
|
||||
// run the tests
|
||||
request("/party1/")
|
||||
request("/party1/path1")
|
||||
request("/party1/path2")
|
||||
request("/party1/namedpath/theparam1/something/theparam2")
|
||||
request("/party1/namedpath/theparam1/something/theparam2/else")
|
||||
}
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/gavv/httpexpect"
|
||||
"github.com/gavv/httpexpect/fasthttpexpect"
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/config"
|
||||
)
|
||||
|
||||
type param struct {
|
||||
|
@ -59,7 +58,6 @@ var routes = []route{
|
|||
|
||||
func TestRouter(t *testing.T) {
|
||||
api := iris.New()
|
||||
|
||||
for idx := range routes {
|
||||
r := routes[idx]
|
||||
if r.Register {
|
||||
|
@ -88,11 +86,10 @@ func TestRouter(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
api.PreListen(config.Server{ListeningAddr: ""})
|
||||
// create httpexpect instance that will call fasthtpp.RequestHandler directly
|
||||
e := httpexpect.WithConfig(httpexpect.Config{
|
||||
Reporter: httpexpect.NewAssertReporter(t),
|
||||
Client: fasthttpexpect.NewBinder(api.ServeRequest),
|
||||
Client: fasthttpexpect.NewBinder(api.NoListen().Handler),
|
||||
})
|
||||
|
||||
// run the tests (1)
|
||||
|
@ -107,15 +104,14 @@ func TestRouter(t *testing.T) {
|
|||
|
||||
func TestPathEscape(t *testing.T) {
|
||||
api := iris.New()
|
||||
|
||||
api.Get("/details/:name", func(ctx *iris.Context) {
|
||||
name := ctx.Param("name")
|
||||
highlight := ctx.URLParam("highlight")
|
||||
ctx.Text(iris.StatusOK, fmt.Sprintf("name=%s,highlight=%s", name, highlight))
|
||||
})
|
||||
|
||||
api.PreListen(config.Server{ListeningAddr: ""})
|
||||
api.PostListen()
|
||||
e := httpexpect.WithConfig(httpexpect.Config{Reporter: httpexpect.NewAssertReporter(t), Client: fasthttpexpect.NewBinder(api.ServeRequest)})
|
||||
e := httpexpect.WithConfig(httpexpect.Config{Reporter: httpexpect.NewAssertReporter(t), Client: fasthttpexpect.NewBinder(api.NoListen().Handler)})
|
||||
|
||||
e.Request("GET", "/details/Sakamoto desu ga?highlight=text").Expect().Status(iris.StatusOK).Body().Equal("name=Sakamoto desu ga,highlight=text")
|
||||
}
|
145
tree.go
145
tree.go
|
@ -1,145 +0,0 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
|
||||
"github.com/kataras/iris/utils"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
type (
|
||||
tree struct {
|
||||
station *Iris
|
||||
method string
|
||||
rootBranch *Branch
|
||||
domain string
|
||||
hosts bool //if domain != "" we set it directly on .Plant
|
||||
cors bool // if cross domain allow enabled
|
||||
pool sync.Pool
|
||||
next *tree
|
||||
}
|
||||
|
||||
// Garden is the main area which routes are planted/placed
|
||||
Garden struct {
|
||||
first *tree
|
||||
}
|
||||
)
|
||||
|
||||
// garden
|
||||
|
||||
func (g *Garden) visitAll(f func(i int, tr *tree)) {
|
||||
t := g.first
|
||||
i := 0
|
||||
for t != nil {
|
||||
|
||||
f(i, t)
|
||||
t = t.next
|
||||
}
|
||||
}
|
||||
|
||||
// visitAllBreak like visitAll but if true to the function then it breaks
|
||||
func (g *Garden) visitAllBreak(f func(i int, tr *tree) bool) {
|
||||
t := g.first
|
||||
i := 0
|
||||
for t != nil {
|
||||
|
||||
if f(i, t) {
|
||||
break
|
||||
}
|
||||
t = t.next
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Garden) last() (t *tree) {
|
||||
|
||||
t = g.first
|
||||
for t.next != nil {
|
||||
t = t.next
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getRootByMethodAndDomain returns the correct branch which it's method&domain is equal to the given method&domain, from a garden's tree
|
||||
// trees with no domain means that their domain==""
|
||||
func (g *Garden) getRootByMethodAndDomain(method string, domain string) (b *Branch) {
|
||||
g.visitAll(func(i int, t *tree) {
|
||||
if t.domain == domain && t.method == method {
|
||||
b = t.rootBranch
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Plant plants/adds a route to the garden
|
||||
func (g *Garden) Plant(station *Iris, _route IRoute) {
|
||||
method := _route.GetMethod()
|
||||
domain := _route.GetDomain()
|
||||
path := _route.GetPath()
|
||||
theRoot := g.getRootByMethodAndDomain(method, domain)
|
||||
if theRoot == nil {
|
||||
theRoot = new(Branch)
|
||||
theNewTree := newTree(station, method, theRoot, domain, len(domain) > 0, _route.HasCors())
|
||||
if g.first == nil {
|
||||
g.first = theNewTree
|
||||
} else {
|
||||
g.last().next = theNewTree
|
||||
}
|
||||
|
||||
}
|
||||
theRoot.AddBranch(domain+path, _route.GetMiddleware())
|
||||
}
|
||||
|
||||
// tree
|
||||
|
||||
func newTree(station *Iris, method string, theRoot *Branch, domain string, hosts bool, hasCors bool) *tree {
|
||||
t := &tree{station: station, method: method, rootBranch: theRoot, domain: domain, hosts: hosts, cors: hasCors, pool: station.newContextPool()}
|
||||
return t
|
||||
}
|
||||
|
||||
// serve serves the route
|
||||
func (_tree *tree) serve(reqCtx *fasthttp.RequestCtx, path string) bool {
|
||||
ctx := _tree.pool.Get().(*Context)
|
||||
ctx.Reset(reqCtx)
|
||||
middleware, params, mustRedirect := _tree.rootBranch.GetBranch(path, ctx.Params) // pass the parameters here for 0 allocation
|
||||
if middleware != nil {
|
||||
ctx.Params = params
|
||||
ctx.middleware = middleware
|
||||
//ctx.Request.Header.SetUserAgentBytes(DefaultUserAgent)
|
||||
ctx.Do()
|
||||
_tree.pool.Put(ctx)
|
||||
return true
|
||||
} else if mustRedirect && !_tree.station.config.DisablePathCorrection && !bytes.Equal(reqCtx.Method(), MethodConnectBytes) {
|
||||
|
||||
reqPath := path
|
||||
pathLen := len(reqPath)
|
||||
|
||||
if pathLen > 1 {
|
||||
|
||||
if reqPath[pathLen-1] == '/' {
|
||||
reqPath = reqPath[:pathLen-1] //remove the last /
|
||||
} else {
|
||||
//it has path prefix, it doesn't ends with / and it hasn't be found, then just add the slash
|
||||
reqPath = reqPath + "/"
|
||||
}
|
||||
|
||||
ctx.Request.URI().SetPath(reqPath)
|
||||
urlToRedirect := utils.BytesToString(ctx.Request.RequestURI())
|
||||
|
||||
ctx.Redirect(urlToRedirect, 301) // StatusMovedPermanently
|
||||
// RFC2616 recommends that a short note "SHOULD" be included in the
|
||||
// response because older user agents may not understand 301/307.
|
||||
// Shouldn't send the response for POST or HEAD; that leaves GET.
|
||||
if _tree.method == MethodGet {
|
||||
note := "<a href=\"" + utils.HTMLEscape(urlToRedirect) + "\">Moved Permanently</a>.\n"
|
||||
ctx.Write(note)
|
||||
}
|
||||
_tree.pool.Put(ctx)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
_tree.pool.Put(ctx)
|
||||
return false
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
## Package information
|
||||
|
||||
This package contains helpful functions that iris uses, you can use them to your project also!
|
||||
This package contains helpful functions that iris, internally, uses
|
||||
|
||||
|
||||
**That's it.**
|
||||
|
|
|
@ -20,6 +20,8 @@ type (
|
|||
Upgrade(context.IContext) error
|
||||
// OnConnection registers a callback which fires when a connection/client is connected to the server
|
||||
OnConnection(ConnectionFunc)
|
||||
// Config returns a pointer to server's configs
|
||||
Config() *config.Websocket
|
||||
}
|
||||
|
||||
// roomPayload is used as payload from the connection to the server
|
||||
|
@ -57,9 +59,10 @@ var _ Server = &server{}
|
|||
|
||||
// server implementation
|
||||
|
||||
func newServer(c config.Websocket) *server {
|
||||
// newServer creates a websocket server and returns it
|
||||
func newServer(c *config.Websocket) *server {
|
||||
s := &server{
|
||||
config: &c,
|
||||
config: c,
|
||||
put: make(chan *connection),
|
||||
free: make(chan *connection),
|
||||
connections: make(map[string]*connection),
|
||||
|
@ -72,10 +75,13 @@ func newServer(c config.Websocket) *server {
|
|||
|
||||
s.upgrader = websocket.New(s.handleConnection)
|
||||
go s.serve() // start the server automatically
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *server) Config() *config.Websocket {
|
||||
return s.config
|
||||
}
|
||||
|
||||
func (s *server) Upgrade(ctx context.IContext) error {
|
||||
return s.upgrader.Upgrade(ctx)
|
||||
}
|
||||
|
|
|
@ -9,9 +9,8 @@ import (
|
|||
// to avoid the import cycle to /kataras/iris. The ws package is used inside iris' station configuration
|
||||
// inside Iris' configuration like kataras/iris/sessions, kataras/iris/render/rest, kataras/iris/render/template, kataras/iris/server and so on.
|
||||
type irisStation interface {
|
||||
H_(string, string, func(context.IContext))
|
||||
StaticContent(string, string, []byte)
|
||||
Logger() *logger.Logger
|
||||
H_(string, string, func(context.IContext)) func(string)
|
||||
StaticContent(string, string, []byte) func(string)
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -22,16 +21,34 @@ type irisStation interface {
|
|||
// This is not usable for you, unless you need more than one websocket server,
|
||||
// because iris' station already has one which you can configure and start
|
||||
//
|
||||
func New(station irisStation, cfg ...config.Websocket) Server {
|
||||
c := config.DefaultWebsocket().Merge(cfg)
|
||||
// This is deprecated after rc-1, now we create the server and after register it
|
||||
// because I want to be able to call the Websocket via a property and no via func before iris.Listen.
|
||||
func New(station irisStation, c *config.Websocket, logger *logger.Logger) Server {
|
||||
if c.Endpoint == "" {
|
||||
station.Logger().Panicf("Websockets - config's Endpoint is empty, you have to set it in order to enable and start the websocket server!!. Refer to the docs if you can't figure out.")
|
||||
//station.Logger().Panicf("Websockets - config's Endpoint is empty, you have to set it in order to enable and start the websocket server!!. Refer to the docs if you can't figure out.")
|
||||
return nil
|
||||
}
|
||||
server := newServer(c)
|
||||
RegisterServer(station, server, logger)
|
||||
return server
|
||||
}
|
||||
|
||||
// NewServer creates a websocket server and returns it
|
||||
func NewServer(c *config.Websocket) Server {
|
||||
return newServer(c)
|
||||
}
|
||||
|
||||
// RegisterServer registers the handlers for the websocket server
|
||||
// it's a bridge between station and websocket server
|
||||
func RegisterServer(station irisStation, server Server, logger *logger.Logger) {
|
||||
c := server.Config()
|
||||
if c.Endpoint == "" {
|
||||
return
|
||||
}
|
||||
|
||||
websocketHandler := func(ctx context.IContext) {
|
||||
if err := server.Upgrade(ctx); err != nil {
|
||||
station.Logger().Panic(err)
|
||||
logger.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +59,7 @@ func New(station irisStation, cfg ...config.Websocket) Server {
|
|||
}
|
||||
|
||||
if err := server.Upgrade(ctx); err != nil {
|
||||
station.Logger().Panic(err)
|
||||
logger.Panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +68,6 @@ func New(station irisStation, cfg ...config.Websocket) Server {
|
|||
// serve the client side on domain:port/iris-ws.js
|
||||
station.StaticContent("/iris-ws.js", "application/json", clientSource)
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
var clientSource = []byte(`var stringMessageType = 0;
|
||||
|
|
Loading…
Reference in New Issue
Block a user