mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 02:31:04 +01:00
Update to version 8.5.5 | Read HISTORY.md
Former-commit-id: dced7d472edabbab4f80c76051f13261928a8dea
This commit is contained in:
parent
666bcacf20
commit
15feaf0237
|
@ -10,7 +10,7 @@ go:
|
|||
# - tip
|
||||
go_import_path: github.com/kataras/iris
|
||||
install:
|
||||
- go get ./... # for iris-contrib/httpexpect, kataras/golog, boltdb/bolt(sessiondb, optional)
|
||||
- go get ./... # for iris-contrib/httpexpect, kataras/golog, kataras/signal
|
||||
script:
|
||||
- go test -v -cover ./...
|
||||
after_script:
|
||||
|
|
13
HISTORY.md
13
HISTORY.md
|
@ -17,6 +17,19 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
|
|||
|
||||
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris` or let the automatic updater do that for you.
|
||||
|
||||
# Tu, 02 November 2017 | v8.5.5
|
||||
|
||||
- fix [audio/mpeg3 does not appear to be a valid registered mime type#798](https://github.com/kataras/iris/issues/798]) reported by @kryptodev,
|
||||
- improve the updater's performance and moved that into the framework itself,
|
||||
- ask for authentication only when a new version is released.
|
||||
- sessiondb's `.Async` functions do nothing now, all session databases(back-end persistence storage) should run in-sync, @speedwheel helped to find a misbehavior because of that setting,
|
||||
- we are on [opencollective](http://opencollective.com/iris) and sponsored by [codesponsor](https://codesponsor.io/) now.
|
||||
|
||||
_TODO;_
|
||||
|
||||
- give the ability to customize the mvc path-method-and path parameters mapping,
|
||||
- make a github bot which will post the monthly usage and even earnings statistics in a public github markdown file, hope that users will love that type of transparency we will introduce here.
|
||||
|
||||
# Th, 26 October 2017 | v8.5.4
|
||||
|
||||
This version is part of the [releases](https://github.com/kataras/iris/releases).
|
||||
|
|
18
README.md
18
README.md
|
@ -1,5 +1,9 @@
|
|||
# ![Logo created by @santoshanand](logo_white_35_24.png) Iris [![build status](https://img.shields.io/travis/kataras/iris/master.svg?style=flat-square)](https://travis-ci.org/kataras/iris)[![report card](https://img.shields.io/badge/report%20card-a%2B-ff3333.svg?style=flat-square)](http://goreportcard.com/report/kataras/iris)[![github closed issues](https://img.shields.io/github/issues-closed-raw/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/issues?q=is%3Aissue+is%3Aclosed)[![release](https://img.shields.io/github/release/kataras/iris.svg?style=flat-square)](https://github.com/kataras/iris/releases)[![view examples](https://img.shields.io/badge/learn%20by-examples-0077b3.svg?style=flat-square)](https://github.com/kataras/iris/tree/master/_examples)[![chat](https://img.shields.io/badge/community-%20chat-00BCD4.svg?style=flat-square)](https://kataras.rocket.chat/channel/iris)[![CLA assistant](https://cla-assistant.io/readme/badge/kataras/iris?style=flat-square)](https://cla-assistant.io/kataras/iris)
|
||||
|
||||
<a target='_blank' rel='nofollow' href='https://app.codesponsor.io/link/Qw6E1MTHvUJW6BtwUUf9qwsy/kataras/iris'>
|
||||
<img alt='Sponsor' width='888' height='68' src='https://app.codesponsor.io/embed/Qw6E1MTHvUJW6BtwUUf9qwsy/kataras/iris.svg' />
|
||||
</a>
|
||||
|
||||
Iris is a fast, simple and efficient web framework for Go.
|
||||
|
||||
Iris provides a beautifully expressive and easy to use foundation for your next website, API, or distributed app.
|
||||
|
@ -16,6 +20,8 @@ Learn what [others say about Iris](https://www.youtube.com/watch?v=jGx0LkuUs4A)
|
|||
_Updated at: [Friday, 29 September 2017](_benchmarks)_
|
||||
</details>
|
||||
|
||||
## Built with ♥️
|
||||
|
||||
We have no doubt you will able to find other web frameworks written in Go
|
||||
and even put up a real fight to learn and use them for quite some time but
|
||||
make no mistake, sooner or later you will be using Iris, not because of the ergonomic, high-performant solution that it provides but its well-documented unique features, as these will transform you to a real rockstar geek.
|
||||
|
@ -27,24 +33,16 @@ you can find online.
|
|||
|
||||
Iris may have reached version 8, but we're not stopping there. We have many feature ideas on our board that we're anxious to add and other innovative web development solutions that we're planning to build into Iris.
|
||||
|
||||
## Built with ♥️
|
||||
|
||||
<a target='_blank' rel='nofollow' href='https://app.codesponsor.io/link/Qw6E1MTHvUJW6BtwUUf9qwsy/kataras/iris'>
|
||||
<img alt='Sponsor' width='700' height='60' src='https://app.codesponsor.io/embed/Qw6E1MTHvUJW6BtwUUf9qwsy/kataras/iris.svg' />
|
||||
</a>
|
||||
|
||||
Accelerated by [KeyCDN](https://www.keycdn.com/), a simple, fast and reliable CDN.
|
||||
|
||||
We are developing this project using the best code editor for Golang; [Visual Studio Code](https://code.visualstudio.com/) supported by [Microsoft](https://www.microsoft.com).
|
||||
|
||||
Iris was built on top of the the [net/http](https://golang.org/pkg/net/http/) package, we own many thanks to [Brad Fitzpatrick](https://github.com/bradfitz) for that.
|
||||
|
||||
If you're coming from [nodejs](https://nodejs.org) world, Iris is the [expressjs](https://github.com/expressjs/express) equivalent for Gophers.
|
||||
|
||||
## Table Of Content
|
||||
|
||||
* [Installation](#installation)
|
||||
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#th-26-october-2017--v854)
|
||||
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#tu-02-november-2017--v855)
|
||||
* [Getting started](#getting-started)
|
||||
* [Learn](_examples/)
|
||||
* [MVC (Model View Controller)](_examples/#mvc) **NEW**
|
||||
|
@ -80,7 +78,7 @@ If you're coming from [nodejs](https://nodejs.org) world, Iris is the [expressjs
|
|||
|
||||
## Installation
|
||||
|
||||
The only requirement is the [Go Programming Language](https://golang.org/dl/), at least version 1.9
|
||||
The only requirement is the [Go Programming Language](https://golang.org/dl/)
|
||||
|
||||
```sh
|
||||
$ go get -u github.com/kataras/iris
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
8.5.4:https://github.com/kataras/iris/blob/master/HISTORY.md#th-26-october-2017--v854
|
||||
8.5.5:https://github.com/kataras/iris/blob/master/HISTORY.md#tu-02-november-2017--v855
|
|
@ -137,6 +137,9 @@ var WithoutInterruptHandler = func(app *Application) {
|
|||
}
|
||||
|
||||
// WithoutVersionChecker will disable the version checker and updater.
|
||||
// The Iris server will be not
|
||||
// receive automatic updates if you pass this
|
||||
// to the `Run` function. Use it only while you're ready for Production environment.
|
||||
var WithoutVersionChecker = func(app *Application) {
|
||||
app.config.DisableVersionChecker = true
|
||||
}
|
||||
|
@ -268,23 +271,23 @@ type Configuration struct {
|
|||
// Example: https://github.com/kataras/iris/tree/master/_examples/http-listening/listen-addr/omit-server-errors
|
||||
//
|
||||
// Defaults to an empty slice.
|
||||
IgnoreServerErrors []string `yaml:"IgnoreServerErrors" toml:"IgnoreServerErrors"`
|
||||
IgnoreServerErrors []string `json:"ignoreServerErrors,omitempty" yaml:"IgnoreServerErrors" toml:"IgnoreServerErrors"`
|
||||
|
||||
// DisableStartupLog if setted to true then it turns off the write banner on server startup.
|
||||
//
|
||||
// Defaults to false.
|
||||
DisableStartupLog bool `yaml:"DisableStartupLog" toml:"DisableStartupLog"`
|
||||
DisableStartupLog bool `json:"disableStartupLog,omitempty" yaml:"DisableStartupLog" toml:"DisableStartupLog"`
|
||||
// DisableInterruptHandler if setted to true then it disables the automatic graceful server shutdown
|
||||
// when control/cmd+C pressed.
|
||||
// Turn this to true if you're planning to handle this by your own via a custom host.Task.
|
||||
//
|
||||
// Defaults to false.
|
||||
DisableInterruptHandler bool `yaml:"DisableInterruptHandler" toml:"DisableInterruptHandler"`
|
||||
DisableInterruptHandler bool `json:"disableInterruptHandler,omitempty" yaml:"DisableInterruptHandler" toml:"DisableInterruptHandler"`
|
||||
|
||||
// DisableVersionChecker if true then process will be not be notified for any available updates.
|
||||
//
|
||||
// Defaults to false.
|
||||
DisableVersionChecker bool `yaml:"DisableVersionChecker" toml:"DisableVersionChecker"`
|
||||
DisableVersionChecker bool `json:"disableVersionChecker,omitempty" yaml:"DisableVersionChecker" toml:"DisableVersionChecker"`
|
||||
|
||||
// DisablePathCorrection corrects and redirects the requested path to the registered path
|
||||
// for example, if /home/ path is requested but no handler for this Route found,
|
||||
|
@ -292,7 +295,7 @@ type Configuration struct {
|
|||
// (permant)redirects the client to the correct path /home
|
||||
//
|
||||
// Defaults to false.
|
||||
DisablePathCorrection bool `yaml:"DisablePathCorrection" toml:"DisablePathCorrection"`
|
||||
DisablePathCorrection bool `json:"disablePathCorrection,omitempty" yaml:"DisablePathCorrection" toml:"DisablePathCorrection"`
|
||||
|
||||
// EnablePathEscape when is true then its escapes the path, the named parameters (if any).
|
||||
// Change to false it if you want something like this https://github.com/kataras/iris/issues/135 to work
|
||||
|
@ -305,17 +308,17 @@ type Configuration struct {
|
|||
// projectName, _ := url.QueryUnescape(c.Param("project").
|
||||
//
|
||||
// Defaults to false.
|
||||
EnablePathEscape bool `yaml:"EnablePathEscape" toml:"EnablePathEscape"`
|
||||
EnablePathEscape bool `json:"enablePathEscape,omitempty" yaml:"EnablePathEscape" toml:"EnablePathEscape"`
|
||||
|
||||
// EnableOptimization when this field is true
|
||||
// then the application tries to optimize for the best performance where is possible.
|
||||
//
|
||||
// Defaults to false.
|
||||
EnableOptimizations bool `yaml:"EnableOptimizations" toml:"EnableOptimizations"`
|
||||
EnableOptimizations bool `json:"enableOptimizations,omitempty" yaml:"EnableOptimizations" toml:"EnableOptimizations"`
|
||||
// FireMethodNotAllowed if it's true router checks for StatusMethodNotAllowed(405) and
|
||||
// fires the 405 error instead of 404
|
||||
// Defaults to false.
|
||||
FireMethodNotAllowed bool `yaml:"FireMethodNotAllowed" toml:"FireMethodNotAllowed"`
|
||||
FireMethodNotAllowed bool `json:"fireMethodNotAllowed,omitempty" yaml:"FireMethodNotAllowed" toml:"FireMethodNotAllowed"`
|
||||
|
||||
// DisableBodyConsumptionOnUnmarshal manages the reading behavior of the context's body readers/binders.
|
||||
// If setted to true then it
|
||||
|
@ -325,7 +328,7 @@ type Configuration struct {
|
|||
// if this field setted to true then a new buffer will be created to read from and the request body.
|
||||
// The body will not be changed and existing data before the
|
||||
// context.UnmarshalBody/ReadJSON/ReadXML will be not consumed.
|
||||
DisableBodyConsumptionOnUnmarshal bool `yaml:"DisableBodyConsumptionOnUnmarshal" toml:"DisableBodyConsumptionOnUnmarshal"`
|
||||
DisableBodyConsumptionOnUnmarshal bool `json:"disableBodyConsumptionOnUnmarshal,omitempty" yaml:"DisableBodyConsumptionOnUnmarshal" toml:"DisableBodyConsumptionOnUnmarshal"`
|
||||
|
||||
// DisableAutoFireStatusCode if true then it turns off the http error status code handler automatic execution
|
||||
// from "context.StatusCode(>=400)" and instead app should manually call the "context.FireStatusCode(>=400)".
|
||||
|
@ -338,16 +341,16 @@ type Configuration struct {
|
|||
// HTTP Custom error handlers are being registered via app.OnErrorCode(code, handler)".
|
||||
//
|
||||
// Defaults to false.
|
||||
DisableAutoFireStatusCode bool `yaml:"DisableAutoFireStatusCode" toml:"DisableAutoFireStatusCode"`
|
||||
DisableAutoFireStatusCode bool `json:"disableAutoFireStatusCode,omitempty" yaml:"DisableAutoFireStatusCode" toml:"DisableAutoFireStatusCode"`
|
||||
|
||||
// TimeFormat time format for any kind of datetime parsing
|
||||
// Defaults to "Mon, 02 Jan 2006 15:04:05 GMT".
|
||||
TimeFormat string `yaml:"TimeFormat" toml:"TimeFormat"`
|
||||
TimeFormat string `json:"timeFormat,omitempty" yaml:"TimeFormat" toml:"TimeFormat"`
|
||||
|
||||
// Charset character encoding for various rendering
|
||||
// used for templates and the rest of the responses
|
||||
// Defaults to "UTF-8".
|
||||
Charset string `yaml:"Charset" toml:"Charset"`
|
||||
Charset string `json:"charset,omitempty" yaml:"Charset" toml:"Charset"`
|
||||
|
||||
// +----------------------------------------------------+
|
||||
// | Context's keys for values used on various featuers |
|
||||
|
@ -359,11 +362,11 @@ type Configuration struct {
|
|||
// currently we have only one: https://github.com/kataras/iris/tree/master/middleware/i18n.
|
||||
//
|
||||
// Defaults to "iris.translate" and "iris.language"
|
||||
TranslateFunctionContextKey string `yaml:"TranslateFunctionContextKey" toml:"TranslateFunctionContextKey"`
|
||||
TranslateFunctionContextKey string `json:"translateFunctionContextKey,omitempty" yaml:"TranslateFunctionContextKey" toml:"TranslateFunctionContextKey"`
|
||||
// TranslateLanguageContextKey used for i18n.
|
||||
//
|
||||
// Defaults to "iris.language"
|
||||
TranslateLanguageContextKey string `yaml:"TranslateLanguageContextKey" toml:"TranslateLanguageContextKey"`
|
||||
TranslateLanguageContextKey string `json:"translateLanguageContextKey,omitempty" yaml:"TranslateLanguageContextKey" toml:"TranslateLanguageContextKey"`
|
||||
|
||||
// GetViewLayoutContextKey is the key of the context's user values' key
|
||||
// which is being used to set the template
|
||||
|
@ -371,13 +374,13 @@ type Configuration struct {
|
|||
// Overrides the parent's or the configuration's.
|
||||
//
|
||||
// Defaults to "iris.ViewLayout"
|
||||
ViewLayoutContextKey string `yaml:"ViewLayoutContextKey" toml:"ViewLayoutContextKey"`
|
||||
ViewLayoutContextKey string `json:"viewLayoutContextKey,omitempty" yaml:"ViewLayoutContextKey" toml:"ViewLayoutContextKey"`
|
||||
// GetViewDataContextKey is the key of the context's user values' key
|
||||
// which is being used to set the template
|
||||
// binding data from a middleware or the main handler.
|
||||
//
|
||||
// Defaults to "iris.viewData"
|
||||
ViewDataContextKey string `yaml:"ViewDataContextKey" toml:"ViewDataContextKey"`
|
||||
ViewDataContextKey string `json:"viewDataContextKey,omitempty" yaml:"ViewDataContextKey" toml:"ViewDataContextKey"`
|
||||
// RemoteAddrHeaders returns the allowed request headers names
|
||||
// that can be valid to parse the client's IP based on.
|
||||
//
|
||||
|
@ -387,13 +390,13 @@ type Configuration struct {
|
|||
// "CF-Connecting-IP": false
|
||||
//
|
||||
// Look `context.RemoteAddr()` for more.
|
||||
RemoteAddrHeaders map[string]bool `yaml:"RemoteAddrHeaders" toml:"RemoteAddrHeaders"`
|
||||
RemoteAddrHeaders map[string]bool `json:"remoteAddrHeaders,omitempty" yaml:"RemoteAddrHeaders" toml:"RemoteAddrHeaders"`
|
||||
|
||||
// Other are the custom, dynamic options, can be empty.
|
||||
// This field used only by you to set any app's options you want
|
||||
// or by custom adaptors, it's a way to simple communicate between your adaptors (if any)
|
||||
// Defaults to a non-nil empty map.
|
||||
Other map[string]interface{} `yaml:"Other" toml:"Other"`
|
||||
Other map[string]interface{} `json:"other,omitempty" yaml:"Other" toml:"Other"`
|
||||
}
|
||||
|
||||
var _ context.ConfigurationReadOnly = &Configuration{}
|
||||
|
|
|
@ -125,7 +125,7 @@ func (r RequestParams) GetInt64(key string) (int64, error) {
|
|||
|
||||
// GetFloat64 returns a path parameter's value based as float64 on its route's dynamic path key.
|
||||
func (r RequestParams) GetFloat64(key string) (float64, error) {
|
||||
return strconv.ParseFloat(r.Get(key), 64)
|
||||
return r.store.GetFloat64(key)
|
||||
}
|
||||
|
||||
// GetBool returns the path parameter's value as bool, based on its key.
|
||||
|
|
76
core/host/interrupt.go
Normal file
76
core/host/interrupt.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
package host
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// RegisterOnInterrupt registers a global function to call when CTRL+C/CMD+C pressed or a unix kill command received.
|
||||
func RegisterOnInterrupt(cb func()) {
|
||||
Interrupt.Register(cb)
|
||||
}
|
||||
|
||||
// Interrupt watches the os.Signals for interruption signals
|
||||
// and fires the callbacks when those happens.
|
||||
// A call of its `FireNow` manually will fire and reset the registered interrupt handlers.
|
||||
var Interrupt = new(interruptListener)
|
||||
|
||||
type interruptListener struct {
|
||||
mu sync.Mutex
|
||||
once sync.Once
|
||||
// onInterrupt contains a list of the functions that should be called when CTRL+C/CMD+C or
|
||||
// a unix kill command received.
|
||||
onInterrupt []func()
|
||||
}
|
||||
|
||||
// Register registers a global function to call when CTRL+C/CMD+C pressed or a unix kill command received.
|
||||
func (i *interruptListener) Register(cb func()) {
|
||||
if cb == nil {
|
||||
return
|
||||
}
|
||||
|
||||
i.listenOnce()
|
||||
i.mu.Lock()
|
||||
i.onInterrupt = append(i.onInterrupt, cb)
|
||||
i.mu.Unlock()
|
||||
}
|
||||
|
||||
// FireNow can be called more than one times from a Consumer in order to
|
||||
// execute all interrupt handlers manually.
|
||||
func (i *interruptListener) FireNow() {
|
||||
i.mu.Lock()
|
||||
for _, f := range i.onInterrupt {
|
||||
f()
|
||||
}
|
||||
i.onInterrupt = i.onInterrupt[0:0]
|
||||
i.mu.Unlock()
|
||||
}
|
||||
|
||||
// listenOnce fires a goroutine which calls the interrupt handlers when CTRL+C/CMD+C and e.t.c.
|
||||
// If `FireNow` called before then it does nothing when interrupt signal received,
|
||||
// so it's safe to be used side by side with `FireNow`.
|
||||
//
|
||||
// Btw this `listenOnce` is called automatically on first register, it's useless for outsiders.
|
||||
func (i *interruptListener) listenOnce() {
|
||||
i.once.Do(func() { go i.notifyAndFire() })
|
||||
}
|
||||
|
||||
func (i *interruptListener) notifyAndFire() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch,
|
||||
// kill -SIGINT XXXX or Ctrl+c
|
||||
os.Interrupt,
|
||||
syscall.SIGINT, // register that too, it should be ok
|
||||
// os.Kill is equivalent with the syscall.SIGKILL
|
||||
os.Kill,
|
||||
syscall.SIGKILL, // register that too, it should be ok
|
||||
// kill -SIGTERM XXXX
|
||||
syscall.SIGTERM,
|
||||
)
|
||||
select {
|
||||
case <-ch:
|
||||
i.FireNow()
|
||||
}
|
||||
}
|
|
@ -190,8 +190,6 @@ func (su *Supervisor) supervise(blockFunc func() error) error {
|
|||
|
||||
su.notifyServe(host)
|
||||
|
||||
tryStartInterruptNotifier()
|
||||
|
||||
err := blockFunc()
|
||||
su.notifyErr(err)
|
||||
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
package host
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// package-level interrupt notifier and event firing.
|
||||
|
||||
type world struct {
|
||||
mu sync.Mutex
|
||||
// onInterrupt contains a list of the functions that should be called when CTRL+C/CMD+C or
|
||||
// a unix kill command received.
|
||||
onInterrupt []func()
|
||||
}
|
||||
|
||||
var w = &world{}
|
||||
|
||||
// RegisterOnInterrupt registers a global function to call when CTRL+C/CMD+C pressed or a unix kill command received.
|
||||
func RegisterOnInterrupt(cb func()) {
|
||||
w.mu.Lock()
|
||||
w.onInterrupt = append(w.onInterrupt, cb)
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func notifyInterrupt() {
|
||||
w.mu.Lock()
|
||||
for _, f := range w.onInterrupt {
|
||||
f()
|
||||
}
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func tryStartInterruptNotifier() {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if len(w.onInterrupt) > 0 {
|
||||
// this can't be moved to the task interrupt's `Run` function
|
||||
// because it will not catch more than one ctrl/cmd+c, so
|
||||
// we do it here. These tasks are canceled already too.
|
||||
go func() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch,
|
||||
// kill -SIGINT XXXX or Ctrl+c
|
||||
os.Interrupt,
|
||||
syscall.SIGINT, // register that too, it should be ok
|
||||
// os.Kill is equivalent with the syscall.SIGKILL
|
||||
os.Kill,
|
||||
syscall.SIGKILL, // register that too, it should be ok
|
||||
// kill -SIGTERM XXXX
|
||||
syscall.SIGTERM,
|
||||
)
|
||||
select {
|
||||
case <-ch:
|
||||
notifyInterrupt()
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
93
core/maintenance/authenticate.go
Normal file
93
core/maintenance/authenticate.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package maintenance
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
|
||||
"github.com/kataras/iris/core/maintenance/client"
|
||||
"github.com/kataras/iris/core/maintenance/encoding"
|
||||
|
||||
"github.com/kataras/survey"
|
||||
)
|
||||
|
||||
// question describes the question which will be used
|
||||
// for the survey in order to authenticate the local iris.
|
||||
type question struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func hasInternetConnection() (bool, bool) {
|
||||
r, err := client.PostForm("", nil)
|
||||
if err != nil {
|
||||
// no internet connection
|
||||
return false, false
|
||||
}
|
||||
defer r.Body.Close()
|
||||
return true, r.StatusCode == 204
|
||||
}
|
||||
|
||||
func ask() bool {
|
||||
qs := fetchQuestions()
|
||||
var lastResponseUnsed string
|
||||
for _, q := range qs {
|
||||
survey.AskOne(&survey.Input{Message: q.Message}, &lastResponseUnsed, validate(q))
|
||||
}
|
||||
|
||||
return lastResponseUnsed != ""
|
||||
}
|
||||
|
||||
// fetchQuestions returns a list of questions
|
||||
// fetched by the authority server.
|
||||
func fetchQuestions() (qs []question) {
|
||||
r, err := client.PostForm("/survey/ask", nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
if err := encoding.UnmarshalBody(r.Body, &qs, json.Unmarshal); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func validate(q question) survey.Validator {
|
||||
return func(answer interface{}) error {
|
||||
if err := survey.Required(answer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ans, ok := answer.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("bug: expected string but got %v", answer)
|
||||
}
|
||||
data := url.Values{
|
||||
"q": []string{q.Message},
|
||||
"ans": []string{ans},
|
||||
"current_version": []string{Version},
|
||||
}
|
||||
|
||||
r, err := client.PostForm("/survey/submit", data)
|
||||
if err != nil {
|
||||
// error from server-side, allow.
|
||||
return nil
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if r.StatusCode == 200 {
|
||||
// read the whole thing, it has nothing.
|
||||
io.Copy(ioutil.Discard, r.Body)
|
||||
return nil // pass, no any errors.
|
||||
}
|
||||
// now, if invalid;
|
||||
got, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
errMsg := string(got)
|
||||
return fmt.Errorf(errMsg)
|
||||
}
|
||||
}
|
41
core/maintenance/client/client.go
Normal file
41
core/maintenance/client/client.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
)
|
||||
|
||||
const host = "http://live.iris-go.com"
|
||||
|
||||
// PostForm performs the PostForm with a secure client.
|
||||
func PostForm(p string, data url.Values) (*http.Response, error) {
|
||||
client := netutil.Client(25 * time.Second)
|
||||
|
||||
if len(data) == 0 {
|
||||
data = make(url.Values, 1)
|
||||
}
|
||||
data.Set("X-Auth", a)
|
||||
|
||||
u := host + p
|
||||
r, err := client.PostForm(u, data)
|
||||
return r, err
|
||||
}
|
||||
|
||||
var a string
|
||||
|
||||
func init() {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err == nil {
|
||||
for _, f := range interfaces {
|
||||
if f.Flags&net.FlagUp != 0 && bytes.Compare(f.HardwareAddr, nil) != 0 {
|
||||
a = f.HardwareAddr.String()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
33
core/maintenance/encoding/unmarshaler.go
Normal file
33
core/maintenance/encoding/unmarshaler.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package encoding
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// UnmarshalerFunc is the Unmarshaler compatible type.
|
||||
//
|
||||
// See 'unmarshalBody' for more.
|
||||
type UnmarshalerFunc func(data []byte, v interface{}) error
|
||||
|
||||
// UnmarshalBody reads the request's body and binds it to a value or pointer of any type.
|
||||
func UnmarshalBody(body io.Reader, v interface{}, unmarshaler UnmarshalerFunc) error {
|
||||
if body == nil {
|
||||
return errors.New("unmarshal: empty body")
|
||||
}
|
||||
|
||||
rawData, err := ioutil.ReadAll(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check if v is already a pointer, if yes then pass as it's
|
||||
if reflect.TypeOf(v).Kind() == reflect.Ptr {
|
||||
return unmarshaler(rawData, v)
|
||||
}
|
||||
// finally, if the v doesn't contains a self-body decoder and it's not a pointer
|
||||
// use the custom unmarshaler to bind the body
|
||||
return unmarshaler(rawData, &v)
|
||||
}
|
6
core/maintenance/maintenance.go
Normal file
6
core/maintenance/maintenance.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package maintenance
|
||||
|
||||
// Start starts the maintenance process.
|
||||
func Start() {
|
||||
CheckForUpdates()
|
||||
}
|
83
core/maintenance/version.go
Normal file
83
core/maintenance/version.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package maintenance
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/kataras/iris/core/maintenance/version"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
"github.com/kataras/survey"
|
||||
)
|
||||
|
||||
const (
|
||||
// Version is the string representation of the current local Iris Web Framework version.
|
||||
Version = "8.5.5"
|
||||
)
|
||||
|
||||
// CheckForUpdates checks for any available updates
|
||||
// and asks for the user if want to update now or not.
|
||||
func CheckForUpdates() {
|
||||
v := version.Acquire()
|
||||
updateAvailale := v.Compare(Version) == version.Smaller
|
||||
|
||||
if updateAvailale {
|
||||
has, ft := hasInternetConnection()
|
||||
canUpdate := (has && ft && ask()) || !has || !ft
|
||||
if canUpdate {
|
||||
installVersion(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func installVersion(v version.Version) {
|
||||
// on help? when asking for installing the new update
|
||||
// and when answering "No".
|
||||
ignoreUpdatesMsg := "Would you like to ignore future updates? Disable the version checker via:\napp.Run(..., iris.WithoutVersionChecker)"
|
||||
|
||||
// if update available ask for update action.
|
||||
shouldUpdateNowMsg :=
|
||||
fmt.Sprintf("A new version is available online[%s < %s].\nRelease notes: %s.\nUpdate now?",
|
||||
Version, v.String(),
|
||||
v.ChangelogURL)
|
||||
|
||||
var confirmUpdate bool
|
||||
survey.AskOne(&survey.Confirm{
|
||||
Message: shouldUpdateNowMsg,
|
||||
Help: ignoreUpdatesMsg,
|
||||
}, &confirmUpdate, nil)
|
||||
|
||||
// run the updater last, so the user can star the repo and at the same time
|
||||
// the app will update her/his local iris.
|
||||
if confirmUpdate { // it's true only when update was available and user typed "yes".
|
||||
repo := "github.com/kataras/iris/..."
|
||||
cmd := exec.Command("go", "get", "-u", "-v", repo)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stdout
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
golog.Warnf("unexpected message while trying to go get,\nif you edited the original source code then you've to remove the whole $GOPATH/src/github.com/kataras folder and execute `go get -u github.com/kataras/iris/...` manually\n%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
golog.Infof("Update process finished.\nManual rebuild and restart is required to apply the changes...\n")
|
||||
return
|
||||
}
|
||||
|
||||
// if update was available but chosen not to update then just continue...
|
||||
}
|
||||
|
||||
/* Author's note:
|
||||
We could use github webhooks to automatic notify for updates
|
||||
when a new update is pushed to the repository
|
||||
even when server is already started and running but this would expose
|
||||
a route which dev may don't know about, so let it for now but if
|
||||
they ask it then I should add an optional configuration field
|
||||
to "live/realtime update" and implement the idea (which is already implemented in the iris-go server).
|
||||
*/
|
||||
|
||||
/* Author's note:
|
||||
The old remote endpoint for version checker is still available on the server for backwards
|
||||
compatibility with older clients, it will stay there for a long period of time.
|
||||
*/
|
59
core/maintenance/version/fetch.go
Normal file
59
core/maintenance/version/fetch.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
)
|
||||
|
||||
const (
|
||||
versionURL = "https://raw.githubusercontent.com/kataras/iris/master/VERSION"
|
||||
)
|
||||
|
||||
func fetch() (*version.Version, string) {
|
||||
client := netutil.Client(time.Duration(30 * time.Second))
|
||||
|
||||
r, err := client.Get(versionURL)
|
||||
if err != nil {
|
||||
golog.Debugf("err: %v\n", err)
|
||||
return nil, ""
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if r.StatusCode >= 400 {
|
||||
golog.Debugf("Internet connection is missing, updater is unable to fetch the latest Iris version\n", err)
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
|
||||
if len(b) == 0 || err != nil {
|
||||
golog.Debugf("err: %v\n", err)
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
var (
|
||||
fetchedVersion = string(b)
|
||||
changelogURL string
|
||||
)
|
||||
// Example output:
|
||||
// Version(8.5.5)
|
||||
// 8.5.5:https://github.com/kataras/iris/blob/master/HISTORY.md#tu-02-november-2017--v855
|
||||
if idx := strings.IndexByte(fetchedVersion, ':'); idx > 0 {
|
||||
changelogURL = fetchedVersion[idx+1:]
|
||||
fetchedVersion = fetchedVersion[0:idx]
|
||||
}
|
||||
|
||||
latestVersion, err := version.NewVersion(fetchedVersion)
|
||||
if err != nil {
|
||||
golog.Debugf("while fetching and parsing latest version from github: %v\n", err)
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
return latestVersion, changelogURL
|
||||
}
|
64
core/maintenance/version/version.go
Normal file
64
core/maintenance/version/version.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
)
|
||||
|
||||
// Version is a version wrapper which
|
||||
// contains some additional customized properties.
|
||||
type Version struct {
|
||||
version.Version
|
||||
WrittenAt time.Time
|
||||
ChangelogURL string
|
||||
}
|
||||
|
||||
// Result is the compare result type.
|
||||
// Available types are Invalid, Smaller, Equal or Larger.
|
||||
type Result int32
|
||||
|
||||
const (
|
||||
// Smaller when the compared version is smaller than the latest one.
|
||||
Smaller Result = -1
|
||||
// Equal when the compared version is equal with the latest one.
|
||||
Equal Result = 0
|
||||
// Larger when the compared version is larger than the latest one.
|
||||
Larger Result = 1
|
||||
// Invalid means that an error occurred when comparing the versions.
|
||||
Invalid Result = -2
|
||||
)
|
||||
|
||||
// Compare compares the "versionStr" with the latest Iris version,
|
||||
// opossite to the version package
|
||||
// it returns the result of the "versionStr" not the "v" itself.
|
||||
func (v *Version) Compare(versionStr string) Result {
|
||||
if len(v.Version.String()) == 0 {
|
||||
// if version not refreshed, by an internet connection lose,
|
||||
// then return Invalid.
|
||||
return Invalid
|
||||
}
|
||||
|
||||
other, err := version.NewVersion(versionStr)
|
||||
if err != nil {
|
||||
return Invalid
|
||||
}
|
||||
return Result(other.Compare(&v.Version))
|
||||
}
|
||||
|
||||
// Acquire returns the latest version info wrapper.
|
||||
// It calls the fetch.
|
||||
func Acquire() (v Version) {
|
||||
newVersion, changelogURL := fetch()
|
||||
if newVersion == nil { // if github was down then don't panic, just set it as the smallest version.
|
||||
newVersion, _ = version.NewVersion("0.0.1")
|
||||
}
|
||||
|
||||
v = Version{
|
||||
Version: *newVersion,
|
||||
WrittenAt: time.Now(),
|
||||
ChangelogURL: changelogURL,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
|
@ -50,6 +51,40 @@ const (
|
|||
ParamTypePath
|
||||
)
|
||||
|
||||
// Not because for a single reason
|
||||
// a string may be a
|
||||
// ParamTypeString or a ParamTypeFile
|
||||
// or a ParamTypePath or ParamTypeAlphabetical.
|
||||
//
|
||||
// func ParamTypeFromStd(k reflect.Kind) ParamType {
|
||||
|
||||
// Kind returns the std kind of this param type.
|
||||
func (pt ParamType) Kind() reflect.Kind {
|
||||
switch pt {
|
||||
case ParamTypeAlphabetical:
|
||||
fallthrough
|
||||
case ParamTypeFile:
|
||||
fallthrough
|
||||
case ParamTypePath:
|
||||
fallthrough
|
||||
case ParamTypeString:
|
||||
return reflect.String
|
||||
case ParamTypeInt:
|
||||
return reflect.Int
|
||||
case ParamTypeLong:
|
||||
return reflect.Int64
|
||||
case ParamTypeBoolean:
|
||||
return reflect.Bool
|
||||
}
|
||||
return reflect.Invalid // 0
|
||||
}
|
||||
|
||||
// Assignable returns true if the "k" standard type
|
||||
// is assignabled to this ParamType.
|
||||
func (pt ParamType) Assignable(k reflect.Kind) bool {
|
||||
return pt.Kind() == k
|
||||
}
|
||||
|
||||
var paramTypes = map[string]ParamType{
|
||||
"string": ParamTypeString,
|
||||
"int": ParamTypeInt,
|
||||
|
|
|
@ -235,7 +235,7 @@ var types = map[string]string{
|
|||
".mov": "video/quicktime",
|
||||
".movie": "video/x-sgi-movie",
|
||||
".mp2": "audio/mpeg",
|
||||
".mp3": "audio/mpeg3",
|
||||
".mp3": "audio/mpeg",
|
||||
".mp4": "video/mp4",
|
||||
".mpa": "audio/mpeg",
|
||||
".mpc": "application/x-project",
|
||||
|
|
|
@ -102,7 +102,7 @@ func (nodes *Nodes) add(routeName, path string, paramNames []string, handlers co
|
|||
// set the wildcard param name to the root and its children.
|
||||
wildcardIdx := strings.IndexByte(path, '*')
|
||||
wildcardParamName := ""
|
||||
if wildcardIdx > 0 && len(paramNames) == 0 {
|
||||
if wildcardIdx > 0 && len(paramNames) == 0 { // 27 Oct comment: && len(paramNames) == 0 {
|
||||
wildcardParamName = path[wildcardIdx+1:]
|
||||
path = path[0:wildcardIdx-1] + "/" // replace *paramName with single slash
|
||||
|
||||
|
@ -217,8 +217,10 @@ loop:
|
|||
*nodes = append(*nodes, n)
|
||||
return
|
||||
}
|
||||
// println("4. nodes.Add path: " + path[len(n.s):])
|
||||
err = n.childrenNodes.add(routeName, path[len(n.s):], paramNames, handlers, false)
|
||||
|
||||
pathToAdd := path[len(n.s):]
|
||||
// println("4. nodes.Add path: " + pathToAdd)
|
||||
err = n.childrenNodes.add(routeName, pathToAdd, paramNames, handlers, false)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -231,10 +233,19 @@ loop:
|
|||
}
|
||||
n.paramNames = paramNames
|
||||
n.handlers = handlers
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// START
|
||||
// Author's note:
|
||||
// 27 Oct 2017; fixes s|i|l+static+p
|
||||
// without breaking the current tests.
|
||||
if wildcardIdx > 0 {
|
||||
wildcardParamName = path[wildcardIdx+1:]
|
||||
path = path[0:wildcardIdx-1] + "/"
|
||||
}
|
||||
// END
|
||||
|
||||
n := &node{
|
||||
s: path,
|
||||
routeName: routeName,
|
||||
|
|
6
doc.go
6
doc.go
|
@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
|
|||
|
||||
Current Version
|
||||
|
||||
8.5.4
|
||||
8.5.5
|
||||
|
||||
Installation
|
||||
|
||||
|
@ -333,7 +333,7 @@ Example Code:
|
|||
|
||||
h := app.NewHost(&http.Server{Addr:":8080"})
|
||||
h.RegisterOnShutdown(func(){
|
||||
println("server was closed!")
|
||||
println("terminate")
|
||||
})
|
||||
|
||||
app.Run(iris.Raw(h.ListenAndServe))
|
||||
|
@ -398,7 +398,7 @@ Example Code:
|
|||
//
|
||||
// we register a shutdown "event" callback
|
||||
su.RegisterOnShutdown(func() {
|
||||
println("server is closed")
|
||||
println("terminate")
|
||||
})
|
||||
// su.RegisterOnError
|
||||
// su.RegisterOnServe
|
||||
|
|
81
faq.md
81
faq.md
|
@ -1,81 +0,0 @@
|
|||
# FAQ
|
||||
|
||||
## How to upgrade
|
||||
|
||||
```sh
|
||||
go get -u github.com/kataras/iris
|
||||
```
|
||||
|
||||
## Learning
|
||||
|
||||
More than 50 practical examples, tutorials and articles at:
|
||||
|
||||
- https://github.com/kataras/iris/tree/master/_examples
|
||||
- https://github.com/iris-contrib/examples
|
||||
- https://iris-go.com/v8/recipe
|
||||
- https://docs.iris-go.com (in-progress)
|
||||
- https://godoc.org/github.com/kataras/iris
|
||||
|
||||
> [Stay tuned](https://github.com/kataras/iris/stargazers), community prepares even more tutorials.
|
||||
|
||||
Want to help and join to the greatest community? Describe your skills and push your own sections at: https://github.com/kataras/build-a-better-web-together/issues/new
|
||||
|
||||
### common errors that new gophers may meet
|
||||
|
||||
#### type aliases
|
||||
|
||||
| build error | reason | solution |
|
||||
| -----------|--------|--------|
|
||||
| `undefined iris.Context` | caused of using the **optional type alias** `iris.Context` instead of the `context.Context` when building with Go 1.8 | import the original package `github.com/kataras/iris/context` and declare as `func(context.Context){})` **or** download and install the [latest go version](https://golang.org/dl) _recommended_ |
|
||||
|
||||
Type alias is a new feature, introduced at Go version 1.9, so if you want to use Iris' type aliases you have to build using the latest Go version. Nothing really changes for your application if you use type alias or not, Iris' type aliases helps you to omit import statements -- to reduce lines of code, nothing more.
|
||||
|
||||
**Details...**
|
||||
|
||||
Go version 1.9 introduced the [type alias](https://golang.org/doc/go1.9#language) feature.
|
||||
|
||||
Iris uses the `type alias` feature to help you writing less code by omitting some package imports. The examples and documentation are written using Go 1.9 as well.
|
||||
|
||||
If you build your Go app with Go 1.9 you can, optionally, use all Iris web framework's features by importing one single package, the `github.com/kataras/iris`.
|
||||
|
||||
Available type aliases;
|
||||
|
||||
| Go 1.8 | Go 1.8 usage | Go 1.9 usage (optionally) |
|
||||
| -----------|--------|--------|
|
||||
| `import "github.com/kataras/iris/context"` | `func(context.Context) {}`, `context.Handler`, `context.Map` | `func(iris.Context) {}`, `iris.Handler`, `iris.Map` |
|
||||
| `import "github.com/kataras/iris/mvc"` | `type MyController struct { mvc.Controller }` , `mvc.SessionController` | `type MyController struct { iris.Controller }`, `iris.SessionController` |
|
||||
| `import "github.com/kataras/iris/core/router"` | `app.PartyFunc("/users", func(p router.Party) {})` | `app.PartyFunc("/users", func(p iris.Party) {})` |
|
||||
| `import "github.com/kataras/iris/core/host"` | `app.ConfigureHost(func(s *host.Supervisor) {})` | `app.ConfigureHost(func(s *iris.Supervisor) {})` |
|
||||
|
||||
You can find all type aliases and their original package import statements at the [./context.go file](context.go).
|
||||
|
||||
> Remember; this doesn't mean that you have to use those type aliases, you can still import the original packages as you did with Go version 1.8, it's up to you.
|
||||
|
||||
## Active development mode
|
||||
|
||||
Iris may have reached version 8, but we're not stopping there. We have many feature ideas on our board that we're anxious to add and other innovative web development solutions that we're planning to build into Iris.
|
||||
|
||||
## Can I find a job if I learn how to use Iris?
|
||||
|
||||
Yes, not only because you will learn Golang in the same time, but there are some positions
|
||||
open for Iris-specific developers the time we speak.
|
||||
|
||||
- https://glints.id/opportunities/jobs/5553
|
||||
|
||||
## Can Iris be used in production after Dubai purchase?
|
||||
|
||||
Yes, now more than ever.
|
||||
|
||||
https://github.com/kataras/iris/issues/711
|
||||
|
||||
## Do we have a community Chat?
|
||||
|
||||
Yes, https://kataras.rocket.chat/channel/iris.
|
||||
|
||||
https://github.com/kataras/iris/issues/646
|
||||
|
||||
## How this open-source project still active and shine?
|
||||
|
||||
By normal people like you, who help us by donating small or larger amounts of money.
|
||||
|
||||
Help this project to continue deliver awesome and unique features with the higher code quality as possible by donating any amount via [PayPal](https://www.paypal.me/kataras)!
|
|
@ -84,7 +84,8 @@ func New(t *testing.T, app *iris.Application, setters ...OptionSetter) *httpexpe
|
|||
setter.Set(conf)
|
||||
}
|
||||
|
||||
// disable the logger
|
||||
// set the logger or disable it (default) and disable the updater (for any case).
|
||||
app.Configure(iris.WithoutVersionChecker)
|
||||
app.Logger().SetLevel(conf.LogLevel)
|
||||
app.Build()
|
||||
|
||||
|
|
14
iris.go
14
iris.go
|
@ -17,6 +17,7 @@ import (
|
|||
// core packages, needed to build the application
|
||||
"github.com/kataras/iris/core/errors"
|
||||
"github.com/kataras/iris/core/host"
|
||||
"github.com/kataras/iris/core/maintenance"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
"github.com/kataras/iris/core/router"
|
||||
// handlerconv conversions
|
||||
|
@ -26,13 +27,14 @@ import (
|
|||
// view
|
||||
"github.com/kataras/iris/view"
|
||||
// middleware used in Default method
|
||||
|
||||
requestLogger "github.com/kataras/iris/middleware/logger"
|
||||
"github.com/kataras/iris/middleware/recover"
|
||||
)
|
||||
|
||||
const (
|
||||
var (
|
||||
// Version is the current version number of the Iris Web Framework.
|
||||
Version = "8.5.4"
|
||||
Version = maintenance.Version
|
||||
)
|
||||
|
||||
// HTTP status codes as registered with IANA.
|
||||
|
@ -663,9 +665,7 @@ var ErrServerClosed = http.ErrServerClosed
|
|||
// then create a new host and run it manually by `go NewHost(*http.Server).Serve/ListenAndServe` etc...
|
||||
// or use an already created host:
|
||||
// h := NewHost(*http.Server)
|
||||
// Run(Raw(h.ListenAndServe), WithCharset("UTF-8"),
|
||||
// WithRemoteAddrHeader("CF-Connecting-IP"),
|
||||
// WithoutServerError(iris.ErrServerClosed))
|
||||
// Run(Raw(h.ListenAndServe), WithCharset("UTF-8"), WithRemoteAddrHeader("CF-Connecting-IP"))
|
||||
//
|
||||
// The Application can go online with any type of server or iris's host with the help of
|
||||
// the following runners:
|
||||
|
@ -680,8 +680,8 @@ func (app *Application) Run(serve Runner, withOrWithout ...Configurator) error {
|
|||
app.Configure(withOrWithout...)
|
||||
app.logger.Debugf("Application: running using %d host(s)", len(app.Hosts)+1)
|
||||
|
||||
if !app.config.DisableVersionChecker && app.logger.Level != golog.DisableLevel {
|
||||
go CheckVersion()
|
||||
if !app.config.DisableVersionChecker {
|
||||
go maintenance.Start()
|
||||
}
|
||||
|
||||
// this will block until an error(unless supervisor's DeferFlow called from a Task).
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
// DefaultFileMode used as the default database's "fileMode"
|
||||
// for creating the sessions directory path, opening and write the session file.
|
||||
var (
|
||||
DefaultFileMode = 0666
|
||||
DefaultFileMode = 0755
|
||||
)
|
||||
|
||||
// Database the badger(key-value file-based) session storage.
|
||||
|
@ -23,7 +23,6 @@ type Database struct {
|
|||
// it's initialized at `New` or `NewFromDB`.
|
||||
// Can be used to get stats.
|
||||
Service *badger.DB
|
||||
async bool
|
||||
}
|
||||
|
||||
// New creates and returns a new badger(key-value file-based) storage
|
||||
|
@ -109,10 +108,9 @@ func (db *Database) Cleanup() (err error) {
|
|||
return rep.Return()
|
||||
}
|
||||
|
||||
// Async if true passed then it will use different
|
||||
// go routines to update the badger(key-value file-based) storage.
|
||||
// Async is DEPRECATED
|
||||
// if it was true then it could use different to update the back-end storage, now it does nothing.
|
||||
func (db *Database) Async(useGoRoutines bool) *Database {
|
||||
db.async = useGoRoutines
|
||||
return db
|
||||
}
|
||||
|
||||
|
@ -146,12 +144,8 @@ func (db *Database) Load(sid string) (storeDB sessions.RemoteStore) {
|
|||
|
||||
// Sync syncs the database with the session's (memory) store.
|
||||
func (db *Database) Sync(p sessions.SyncPayload) {
|
||||
if db.async {
|
||||
go db.sync(p)
|
||||
} else {
|
||||
db.sync(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *Database) sync(p sessions.SyncPayload) {
|
||||
bsid := []byte(p.SessionID)
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
// for creating the sessions directory path, opening and write
|
||||
// the session boltdb(file-based) storage.
|
||||
var (
|
||||
DefaultFileMode = 0666
|
||||
DefaultFileMode = 0755
|
||||
)
|
||||
|
||||
// Database the BoltDB(file-based) session storage.
|
||||
|
@ -27,7 +27,6 @@ type Database struct {
|
|||
// it's initialized at `New` or `NewFromDB`.
|
||||
// Can be used to get stats.
|
||||
Service *bolt.DB
|
||||
async bool
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -115,10 +114,9 @@ func (db *Database) Cleanup() error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Async if true passed then it will use different
|
||||
// go routines to update the BoltDB(file-based) storage.
|
||||
// Async is DEPRECATED
|
||||
// if it was true then it could use different to update the back-end storage, now it does nothing.
|
||||
func (db *Database) Async(useGoRoutines bool) *Database {
|
||||
db.async = useGoRoutines
|
||||
return db
|
||||
}
|
||||
|
||||
|
@ -151,12 +149,8 @@ func (db *Database) Load(sid string) (storeDB sessions.RemoteStore) {
|
|||
|
||||
// Sync syncs the database with the session's (memory) store.
|
||||
func (db *Database) Sync(p sessions.SyncPayload) {
|
||||
if db.async {
|
||||
go db.sync(p)
|
||||
} else {
|
||||
db.sync(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *Database) sync(p sessions.SyncPayload) {
|
||||
bsid := []byte(p.SessionID)
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
// DefaultFileMode used as the default database's "fileMode"
|
||||
// for creating the sessions directory path, opening and write the session file.
|
||||
var (
|
||||
DefaultFileMode = 0666
|
||||
DefaultFileMode = 0755
|
||||
)
|
||||
|
||||
// Database is the basic file-storage session database.
|
||||
|
@ -28,12 +28,7 @@ var (
|
|||
// Remember: sessions are not a storage for large data, everywhere: on any platform on any programming language.
|
||||
type Database struct {
|
||||
dir string
|
||||
fileMode os.FileMode // defaults to 0666 if missing.
|
||||
// if true then it will use go routines to:
|
||||
// append or re-write a file
|
||||
// create a file
|
||||
// remove a file
|
||||
async bool
|
||||
fileMode os.FileMode // defaults to DefaultFileMode if missing.
|
||||
}
|
||||
|
||||
// New creates and returns a new file-storage database instance based on the "directoryPath".
|
||||
|
@ -77,20 +72,15 @@ func (db *Database) Cleanup() error {
|
|||
|
||||
// FileMode for creating the sessions directory path, opening and write the session file.
|
||||
//
|
||||
// Defaults to 0666.
|
||||
// Defaults to 0755.
|
||||
func (db *Database) FileMode(fileMode uint32) *Database {
|
||||
db.fileMode = os.FileMode(fileMode)
|
||||
return db
|
||||
}
|
||||
|
||||
// Async if true passed then it will use go routines to:
|
||||
// append or re-write a file
|
||||
// create a file
|
||||
// remove a file.
|
||||
//
|
||||
// Defaults to false.
|
||||
// Async is DEPRECATED
|
||||
// if it was true then it could use different to update the back-end storage, now it does nothing.
|
||||
func (db *Database) Async(useGoRoutines bool) *Database {
|
||||
db.async = useGoRoutines
|
||||
return db
|
||||
}
|
||||
|
||||
|
@ -137,12 +127,8 @@ func (db *Database) load(fileName string) (storeDB sessions.RemoteStore, loadErr
|
|||
|
||||
// Sync syncs the database.
|
||||
func (db *Database) Sync(p sessions.SyncPayload) {
|
||||
if db.async {
|
||||
go db.sync(p)
|
||||
} else {
|
||||
db.sync(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *Database) sync(p sessions.SyncPayload) {
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ type Database struct {
|
|||
// it's initialized at `New` or `NewFromDB`.
|
||||
// Can be used to get stats.
|
||||
Service *leveldb.DB
|
||||
async bool
|
||||
}
|
||||
|
||||
// New creates and returns a new LevelDB(file-based) storage
|
||||
|
@ -98,10 +97,9 @@ func (db *Database) Cleanup() error {
|
|||
return iter.Error()
|
||||
}
|
||||
|
||||
// Async if true passed then it will use different
|
||||
// go routines to update the LevelDB(file-based) storage.
|
||||
// Async is DEPRECATED
|
||||
// if it was true then it could use different to update the back-end storage, now it does nothing.
|
||||
func (db *Database) Async(useGoRoutines bool) *Database {
|
||||
db.async = useGoRoutines
|
||||
return db
|
||||
}
|
||||
|
||||
|
@ -140,12 +138,8 @@ func (db *Database) Load(sid string) (storeDB sessions.RemoteStore) {
|
|||
|
||||
// Sync syncs the database with the session's (memory) store.
|
||||
func (db *Database) Sync(p sessions.SyncPayload) {
|
||||
if db.async {
|
||||
go db.sync(p)
|
||||
} else {
|
||||
db.sync(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *Database) sync(p sessions.SyncPayload) {
|
||||
bsid := []byte(p.SessionID)
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
// Database the redis back-end session database for the sessions.
|
||||
type Database struct {
|
||||
redis *service.Service
|
||||
async bool
|
||||
}
|
||||
|
||||
// New returns a new redis database.
|
||||
|
@ -27,10 +26,9 @@ func (db *Database) Config() *service.Config {
|
|||
return db.redis.Config
|
||||
}
|
||||
|
||||
// Async if true passed then it will use different
|
||||
// go routines to update the redis storage.
|
||||
// Async is DEPRECATED
|
||||
// if it was true then it could use different to update the back-end storage, now it does nothing.
|
||||
func (db *Database) Async(useGoRoutines bool) *Database {
|
||||
db.async = useGoRoutines
|
||||
return db
|
||||
}
|
||||
|
||||
|
@ -70,12 +68,8 @@ func (db *Database) Load(sid string) (storeDB sessions.RemoteStore) {
|
|||
|
||||
// Sync syncs the database.
|
||||
func (db *Database) Sync(p sessions.SyncPayload) {
|
||||
if db.async {
|
||||
go db.sync(p)
|
||||
} else {
|
||||
db.sync(p)
|
||||
}
|
||||
}
|
||||
|
||||
func (db *Database) sync(p sessions.SyncPayload) {
|
||||
if p.Action == sessions.ActionDestroy {
|
||||
|
|
151
version.go
151
version.go
|
@ -1,151 +0,0 @@
|
|||
package iris
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/golog"
|
||||
"github.com/kataras/iris/core/netutil"
|
||||
"github.com/kataras/survey"
|
||||
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
)
|
||||
|
||||
var checkVersionOnce = sync.Once{}
|
||||
|
||||
// CheckVersion checks for any available updates.
|
||||
func CheckVersion() {
|
||||
checkVersionOnce.Do(func() {
|
||||
checkVersion()
|
||||
})
|
||||
}
|
||||
|
||||
type versionInfo struct {
|
||||
Version string `json:"version"`
|
||||
ChangelogURL string `json:"changelog_url"`
|
||||
UpdateAvailable bool `json:"update_available"`
|
||||
FirstTime bool `json:"first_time"`
|
||||
}
|
||||
|
||||
func checkVersion() {
|
||||
client := netutil.Client(25 * time.Second)
|
||||
r, err := client.PostForm("https://iris-go.com/version", url.Values{"current_version": {Version}})
|
||||
|
||||
if err != nil {
|
||||
golog.Debugf("%v", err)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
if r.StatusCode >= 400 {
|
||||
return
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
|
||||
if len(b) == 0 || err != nil {
|
||||
golog.Debugf("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
v := new(versionInfo)
|
||||
if err := json.Unmarshal(b, v); err != nil {
|
||||
golog.Debugf("error while unmarshal the response body: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var qs []*survey.Question
|
||||
|
||||
// on help? when asking for installing the new update
|
||||
// and when answering "No".
|
||||
ignoreUpdatesMsg := "Would you like to ignore future updates? Disable the version checker via:\napp.Run(..., iris.WithoutVersionChecker)"
|
||||
|
||||
if v.UpdateAvailable {
|
||||
// if update available ask for update action.
|
||||
shouldUpdateNowMsg :=
|
||||
fmt.Sprintf("A new version is available online[%s > %s].\nRelease notes: %s.\nUpdate now?",
|
||||
v.Version, Version,
|
||||
v.ChangelogURL)
|
||||
|
||||
qs = append(qs, &survey.Question{
|
||||
Name: "shouldUpdateNow",
|
||||
Prompt: &survey.Confirm{
|
||||
Message: shouldUpdateNowMsg,
|
||||
Help: ignoreUpdatesMsg,
|
||||
},
|
||||
Validate: survey.Required,
|
||||
})
|
||||
}
|
||||
|
||||
// firs time and update available is not relative because if no update often server will decide when to ask this,
|
||||
// so separate the actions and if statements here.
|
||||
if v.FirstTime {
|
||||
// if first time that this server was updated then ask if enjoying the framework.
|
||||
qs = append(qs, &survey.Question{
|
||||
Name: "enjoyingIris",
|
||||
Prompt: &survey.Confirm{
|
||||
Message: "Enjoying Iris Framework?",
|
||||
Help: "yes or no",
|
||||
},
|
||||
Validate: survey.Required,
|
||||
})
|
||||
}
|
||||
|
||||
// Ask if should update(if available) and enjoying iris(if first time) in the same survey.
|
||||
ans := struct {
|
||||
ShouldUpdateNow bool `survey:"shouldUpdateNow"`
|
||||
EnjoyingIris bool `survey:"enjoyingIris"`
|
||||
}{}
|
||||
|
||||
survey.Ask(qs, &ans)
|
||||
|
||||
if ans.EnjoyingIris {
|
||||
// if the answer to the previous survey about enjoying the framework
|
||||
// was positive then do the survey (currently only one question and its action).
|
||||
qs2 := []*survey.Question{
|
||||
{
|
||||
Name: "starNow",
|
||||
Prompt: &survey.Confirm{
|
||||
Message: "Would you mind giving us a star on GitHub? It really helps us out! Thanks for your support:)",
|
||||
Help: "Its free so let's do that, type 'y'",
|
||||
},
|
||||
Validate: survey.Required,
|
||||
},
|
||||
/* any future questions should be here, at this second survey. */
|
||||
}
|
||||
ans2 := struct {
|
||||
StarNow bool `survey:"starNow"`
|
||||
}{}
|
||||
survey.Ask(qs2, &ans2)
|
||||
if ans2.StarNow {
|
||||
starRepo := "https://github.com/kataras/iris/stargazers"
|
||||
if err := open.Run(starRepo); err != nil {
|
||||
golog.Warnf("tried to open the browser for you but failed, please give us a star at: %s\n", starRepo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// run the updater last, so the user can star the repo and at the same time
|
||||
// the app will update her/his local iris.
|
||||
if ans.ShouldUpdateNow { // it's true only when update was available and user typed "yes".
|
||||
repo := "github.com/kataras/iris/..."
|
||||
cmd := exec.Command("go", "get", "-u", "-v", repo)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stdout
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
golog.Warnf("unexpected message while trying to go get,\nif you edited the original source code then you've to remove the whole $GOPATH/src/github.com/kataras folder and execute `go get -u github.com/kataras/iris/...` manually\n%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
golog.Infof("Update process finished.\nManual rebuild and restart is required to apply the changes...\n")
|
||||
} else if v.UpdateAvailable { // if update was available but choosen not to update.
|
||||
golog.Infof(ignoreUpdatesMsg)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user