mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
omit errors received by the server via configuration 🍪 | requested by https://github.com/kataras/iris/issues/668
relative link: https://github.com/kataras/iris/issues/668 Former-commit-id: 6491abd68b74e18bf4ed0b32406e67597c9b55a9
This commit is contained in:
parent
ace439203d
commit
16ccb2edc4
76
HISTORY.md
76
HISTORY.md
|
@ -16,8 +16,82 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
|
|||
> Iris uses the [vendor directory](https://docs.google.com/document/d/1Bz5-UB7g2uPBdOx-rw5t9MxJwkfpx90cqG9AFL0JAYo) feature, so you get truly reproducible builds, as this method guards against upstream renames and deletes.
|
||||
|
||||
**How to upgrade**: Open your command-line and execute this command: `go get -u github.com/kataras/iris`.
|
||||
For further installation support, please click [here](http://support.iris-go.com/d/16-how-to-install-iris-web-framework).
|
||||
|
||||
# Th, 13 July 2017 | v8.0.1
|
||||
|
||||
Nothing tremendous at this minor version.
|
||||
|
||||
We've just added a configuration field in order to ignore errors received by the `Run` function, see below.
|
||||
|
||||
[Configuration#IgnoreServerErrors](https://github.com/kataras/iris/blob/master/configuration.go#L255)
|
||||
```go
|
||||
type Configuration struct {
|
||||
// [...]
|
||||
|
||||
// IgnoreServerErrors will cause to ignore the matched "errors"
|
||||
// from the main application's `Run` function.
|
||||
// This is a slice of string, not a slice of error
|
||||
// users can register these errors using yaml or toml configuration file
|
||||
// like the rest of the configuration fields.
|
||||
//
|
||||
// See `WithoutServerError(...)` function too.
|
||||
//
|
||||
// Defaults to an empty slice.
|
||||
IgnoreServerErrors []string `yaml:"IgnoreServerErrors" toml:"IgnoreServerErrors"`
|
||||
|
||||
// [...]
|
||||
}
|
||||
```
|
||||
[Configuration#WithoutServerError](https://github.com/kataras/iris/blob/master/configuration.go#L106)
|
||||
```go
|
||||
// WithoutServerError will cause to ignore the matched "errors"
|
||||
// from the main application's `Run` function.
|
||||
//
|
||||
// Usage:
|
||||
// err := app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
|
||||
// will return `nil` if the server's error was `http/iris#ErrServerClosed`.
|
||||
//
|
||||
// See `Configuration#IgnoreServerErrors []string` too.
|
||||
WithoutServerError(errors ...error) Configurator
|
||||
```
|
||||
|
||||
By default no error is being ignored, of course.
|
||||
|
||||
Example code:
|
||||
[_examples/http-listening/omit-server-errors](https://github.com/kataras/iris/tree/master/_examples/http-listening/omit-server-errors)
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
app.Get("/", func(ctx context.Context) {
|
||||
ctx.HTML("<h1>Hello World!/</h1>")
|
||||
})
|
||||
|
||||
err := app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
|
||||
if err != nil {
|
||||
// do something
|
||||
}
|
||||
// same as:
|
||||
// err := app.Run(iris.Addr(":8080"))
|
||||
// if err != nil && (err != iris.ErrServerClosed || err.Error() != iris.ErrServerClosed.Error()) {
|
||||
// [...]
|
||||
// }
|
||||
}
|
||||
```
|
||||
|
||||
At first we didn't want to implement something like that because it's ridiculous easy to do it manually but a second thought came to us,
|
||||
that many applications are based on configuration, therefore it would be nice to have something to ignore errors
|
||||
by simply string values that can be passed to the application's configuration via `toml` or `yaml` files too.
|
||||
|
||||
This feature has been implemented after a request of ignoring the `iris/http#ErrServerClosed` from the `Run` function:
|
||||
https://github.com/kataras/iris/issues/668
|
||||
|
||||
# Mo, 10 July 2017 | v8.0.0
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ These types of projects need heart and sacrifices to continue offer the best dev
|
|||
</a>
|
||||
|
||||
* [Installation](#-installation)
|
||||
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#mo-10-july-2017--v800)
|
||||
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#th-13-july-2017--v801)
|
||||
* [Learn](#-learn)
|
||||
* [HTTP Listening](_examples/#http-listening)
|
||||
* [Configuration](_examples/#configuration)
|
||||
|
@ -215,7 +215,7 @@ _iris_ does not force you to use any specific ORM. With support for the most pop
|
|||
|
||||
### 📌 Version
|
||||
|
||||
Current: **8.0.0**
|
||||
Current: **8.0.1**
|
||||
|
||||
Each new release is pushed to the master. It stays there until the next version. When a next version is released then the previous version goes to its own branch with `gopkg.in` as its import path (and its own vendor folder), in order to keep it working "for-ever".
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ It doesn't always contain the "best ways" but it does cover each important featu
|
|||
### HTTP Listening
|
||||
|
||||
- [Common, with address](http-listening/listen-addr/main.go)
|
||||
* [omit server errors](http-listening/listen-addr/omit-server-errors)
|
||||
- [UNIX socket file](http-listening/listen-unix/main.go)
|
||||
- [TLS](http-listening/listen-tls/main.go)
|
||||
- [Letsencrypt (Automatic Certifications)](http-listening/listen-letsencrypt/main.go)
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
// +build go1.9
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.Default()
|
||||
|
||||
// Method: GET
|
||||
// Resource: http://localhost:8080/
|
||||
app.Handle("GET", "/", func(ctx iris.Context) {
|
||||
ctx.HTML("<b>Hello world!</b>")
|
||||
})
|
||||
|
||||
// same as app.Handle("GET", "/ping", [...])
|
||||
// Method: GET
|
||||
// Resource: http://localhost:8080/ping
|
||||
app.Get("/ping", func(ctx iris.Context) {
|
||||
ctx.WriteString("pong")
|
||||
})
|
||||
|
||||
// Method: GET
|
||||
// Resource: http://localhost:8080/hello
|
||||
app.Get("/hello", func(ctx iris.Context) {
|
||||
ctx.JSON(iris.Map{"message": "Hello iris web framework."})
|
||||
})
|
||||
|
||||
// http://localhost:8080
|
||||
// http://localhost:8080/ping
|
||||
// http://localhost:8080/hello
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
|
@ -12,8 +12,6 @@ func main() {
|
|||
ctx.HTML("<h1>Hello World!/</h1>")
|
||||
})
|
||||
|
||||
if err := app.Run(iris.Addr(":8080")); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// http://localhost:8080
|
||||
app.Run(iris.Addr(":8080"))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/kataras/iris"
|
||||
"github.com/kataras/iris/context"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := iris.New()
|
||||
|
||||
app.Get("/", func(ctx context.Context) {
|
||||
ctx.HTML("<h1>Hello World!/</h1>")
|
||||
})
|
||||
|
||||
err := app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
|
||||
if err != nil {
|
||||
// do something
|
||||
}
|
||||
// same as:
|
||||
// err := app.Run(iris.Addr(":8080"))
|
||||
// if err != nil && (err != iris.ErrServerClosed || err.Error() != iris.ErrServerClosed.Error()) {
|
||||
// [...]
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
stdContext "context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func logger(app *iris.Application) *bytes.Buffer {
|
||||
buf := &bytes.Buffer{}
|
||||
app.Logger().Formatter = &logrus.TextFormatter{
|
||||
DisableColors: true,
|
||||
DisableSorting: true,
|
||||
DisableTimestamp: true,
|
||||
}
|
||||
|
||||
app.Logger().Out = buf
|
||||
|
||||
// disable the "Now running at...." in order to have a clean log of the error.
|
||||
// we could attach that on `Run` but better to keep things simple here.
|
||||
app.Configure(iris.WithoutStartupLog)
|
||||
return buf
|
||||
}
|
||||
|
||||
func TestListenAddr(t *testing.T) {
|
||||
app := iris.New()
|
||||
// we keep the logger running as well but in a controlled way.
|
||||
log := logger(app)
|
||||
|
||||
// close the server at 3-6 seconds
|
||||
go func() {
|
||||
time.Sleep(3 * time.Second)
|
||||
ctx, cancel := stdContext.WithTimeout(stdContext.TODO(), 3*time.Second)
|
||||
defer cancel()
|
||||
app.Shutdown(ctx)
|
||||
}()
|
||||
|
||||
err := app.Run(iris.Addr(":9829"))
|
||||
// in this case the error should be logged and return as well.
|
||||
if err != iris.ErrServerClosed {
|
||||
t.Fatalf("expecting err to be `iris.ErrServerClosed` but got: %v", err)
|
||||
}
|
||||
|
||||
// println(log.Bytes())
|
||||
// println(len(log.Bytes()))
|
||||
expected := fmt.Sprintln("level=error msg=\"" + iris.ErrServerClosed.Error() + "\" ")
|
||||
// println([]byte(expected))
|
||||
// println(len([]byte(expected)))
|
||||
|
||||
if got := log.String(); expected != got {
|
||||
t.Fatalf("expecting to log the:\n'%s'\ninstead of:\n'%s'", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListenAddrWithoutServerErr(t *testing.T) {
|
||||
app := iris.New()
|
||||
// we keep the logger running as well but in a controlled way.
|
||||
log := logger(app)
|
||||
|
||||
// close the server at 3-6 seconds
|
||||
go func() {
|
||||
time.Sleep(3 * time.Second)
|
||||
ctx, cancel := stdContext.WithTimeout(stdContext.TODO(), 3*time.Second)
|
||||
defer cancel()
|
||||
app.Shutdown(ctx)
|
||||
}()
|
||||
|
||||
// we disable the ErrServerClosed, so the error should be nil when server is closed by `app.Shutdown`.
|
||||
|
||||
// so in this case the iris/http.ErrServerClosed should be NOT logged and NOT return.
|
||||
err := app.Run(iris.Addr(":9827"), iris.WithoutServerError(iris.ErrServerClosed))
|
||||
if err != nil {
|
||||
t.Fatalf("expecting err to be nil but got: %v", err)
|
||||
}
|
||||
|
||||
if got := log.String(); got != "" {
|
||||
t.Fatalf("expecting to log nothing but logged: '%s'", got)
|
||||
}
|
||||
}
|
|
@ -95,6 +95,29 @@ type Configurator func(*Application)
|
|||
// variables for configurators don't need any receivers, functions
|
||||
// for them that need (helps code editors to recognise as variables without parenthesis completion).
|
||||
|
||||
// WithoutServerError will cause to ignore the matched "errors"
|
||||
// from the main application's `Run` function.
|
||||
//
|
||||
// Usage:
|
||||
// err := app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
|
||||
// will return `nil` if the server's error was `http/iris#ErrServerClosed`.
|
||||
//
|
||||
// See `Configuration#IgnoreServerErrors []string` too.
|
||||
func WithoutServerError(errors ...error) Configurator {
|
||||
return func(app *Application) {
|
||||
if len(errors) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
errorsAsString := make([]string, len(errors), len(errors))
|
||||
for i, e := range errors {
|
||||
errorsAsString[i] = e.Error()
|
||||
}
|
||||
|
||||
app.config.IgnoreServerErrors = append(app.config.IgnoreServerErrors, errorsAsString...)
|
||||
}
|
||||
}
|
||||
|
||||
// WithoutStartupLog turns off the information send, once, to the terminal when the main server is open.
|
||||
var WithoutStartupLog = func(app *Application) {
|
||||
app.config.DisableStartupLog = true
|
||||
|
@ -220,6 +243,17 @@ type Configuration struct {
|
|||
// It can be retrieved by the context if needed (i.e router for subdomains)
|
||||
vhost string
|
||||
|
||||
// IgnoreServerErrors will cause to ignore the matched "errors"
|
||||
// from the main application's `Run` function.
|
||||
// This is a slice of string, not a slice of error
|
||||
// users can register these errors using yaml or toml configuration file
|
||||
// like the rest of the configuration fields.
|
||||
//
|
||||
// See `WithoutServerError(...)` function too.
|
||||
//
|
||||
// Defaults to an empty slice.
|
||||
IgnoreServerErrors []string `yaml:"IgnoreServerErrors" toml:"IgnoreServerErrors"`
|
||||
|
||||
// DisableStartupLog if setted to true then it turns off the write banner on server startup.
|
||||
//
|
||||
// Defaults to false.
|
||||
|
@ -455,6 +489,10 @@ func WithConfiguration(c Configuration) Configurator {
|
|||
return func(app *Application) {
|
||||
main := app.config
|
||||
|
||||
if v := c.IgnoreServerErrors; len(v) > 0 {
|
||||
main.IgnoreServerErrors = append(main.IgnoreServerErrors, v...)
|
||||
}
|
||||
|
||||
if v := c.DisableStartupLog; v {
|
||||
main.DisableStartupLog = v
|
||||
}
|
||||
|
|
|
@ -37,6 +37,16 @@ func New(errMsg string) Error {
|
|||
}
|
||||
}
|
||||
|
||||
// NewFromErr same as `New` but pointer for nil checks without the need of the `Return()` function.
|
||||
func NewFromErr(err error) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errp := New(err.Error())
|
||||
return &errp
|
||||
}
|
||||
|
||||
// Equal returns true if "e" and "e2" are matched, by their IDs.
|
||||
// It will always returns true if the "e2" is a children of "e"
|
||||
// or the error messages are exactly the same, otherwise false.
|
||||
|
@ -123,6 +133,17 @@ func (e Error) With(err error) error {
|
|||
return e.Format(err.Error())
|
||||
}
|
||||
|
||||
// Ignore will ignore the "err" and return nil.
|
||||
func (e Error) Ignore(err error) error {
|
||||
if err == nil {
|
||||
return e
|
||||
}
|
||||
if e.Error() == err.Error() {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// Panic output the message and after panics.
|
||||
func (e Error) Panic() {
|
||||
_, fn, line, _ := runtime.Caller(1)
|
||||
|
|
|
@ -106,12 +106,12 @@ func (r *Reporter) Describe(format string, err error) {
|
|||
|
||||
// PrintStack prints all the errors to the given "printer".
|
||||
// Returns itself in order to be used as printer and return the full error in the same time.
|
||||
func (r Reporter) PrintStack(printer func(string, ...interface{})) error {
|
||||
func (r *Reporter) PrintStack(printer func(string, ...interface{})) error {
|
||||
return PrintAndReturnErrors(r, printer)
|
||||
}
|
||||
|
||||
// Stack returns the list of the errors in the stack.
|
||||
func (r Reporter) Stack() []Error {
|
||||
func (r *Reporter) Stack() []Error {
|
||||
return r.wrapper.Stack
|
||||
}
|
||||
|
||||
|
@ -127,12 +127,12 @@ func (r *Reporter) addStack(stack []Error) {
|
|||
}
|
||||
|
||||
// Error implements the error, returns the full error string.
|
||||
func (r Reporter) Error() string {
|
||||
func (r *Reporter) Error() string {
|
||||
return r.wrapper.Error()
|
||||
}
|
||||
|
||||
// Return returns nil if the error is empty, otherwise returns the full error.
|
||||
func (r Reporter) Return() error {
|
||||
func (r *Reporter) Return() error {
|
||||
if r.Error() == "" {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -27,6 +27,15 @@ type Supervisor struct {
|
|||
mu sync.Mutex
|
||||
|
||||
onServe []func(TaskHost)
|
||||
// IgnoreErrors should contains the errors that should be ignored
|
||||
// on both serve functions return statements and error handlers.
|
||||
//
|
||||
// i.e: http.ErrServerClosed.Error().
|
||||
//
|
||||
// Note that this will match the string value instead of the equality of the type's variables.
|
||||
//
|
||||
// Defaults to empty.
|
||||
IgnoredErrors []string
|
||||
onErr []func(error)
|
||||
onShutdown []func()
|
||||
}
|
||||
|
@ -103,17 +112,31 @@ func (su *Supervisor) RegisterOnError(cb func(error)) {
|
|||
su.mu.Unlock()
|
||||
}
|
||||
|
||||
func (su *Supervisor) notifyErr(err error) {
|
||||
// if err == http.ErrServerClosed {
|
||||
// su.notifyShutdown()
|
||||
// return
|
||||
// }
|
||||
func (su *Supervisor) validateErr(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
su.mu.Lock()
|
||||
defer su.mu.Unlock()
|
||||
|
||||
for _, e := range su.IgnoredErrors {
|
||||
if err.Error() == e {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (su *Supervisor) notifyErr(err error) {
|
||||
err = su.validateErr(err)
|
||||
if err != nil {
|
||||
su.mu.Lock()
|
||||
for _, f := range su.onErr {
|
||||
go f(err)
|
||||
}
|
||||
su.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterOnServe registers a function to call on
|
||||
|
@ -156,7 +179,7 @@ func (su *Supervisor) supervise(blockFunc func() error) error {
|
|||
}
|
||||
}
|
||||
|
||||
return err // start the server
|
||||
return su.validateErr(err)
|
||||
}
|
||||
|
||||
// Serve accepts incoming connections on the Listener l, creating a
|
||||
|
@ -171,7 +194,6 @@ func (su *Supervisor) supervise(blockFunc func() error) error {
|
|||
// Serve always returns a non-nil error. After Shutdown or Close, the
|
||||
// returned error is http.ErrServerClosed.
|
||||
func (su *Supervisor) Serve(l net.Listener) error {
|
||||
|
||||
return su.supervise(func() error { return su.Server.Serve(l) })
|
||||
}
|
||||
|
||||
|
|
11
iris.go
11
iris.go
|
@ -33,7 +33,7 @@ import (
|
|||
const (
|
||||
|
||||
// Version is the current version number of the Iris Web Framework.
|
||||
Version = "8.0.0"
|
||||
Version = "8.0.1"
|
||||
)
|
||||
|
||||
// HTTP status codes as registered with IANA.
|
||||
|
@ -361,6 +361,8 @@ func (app *Application) NewHost(srv *http.Server) *host.Supervisor {
|
|||
host.RegisterOnInterrupt(host.ShutdownOnInterrupt(su, shutdownTimeout))
|
||||
}
|
||||
|
||||
su.IgnoredErrors = append(su.IgnoredErrors, app.config.IgnoreServerErrors...)
|
||||
|
||||
app.Hosts = append(app.Hosts, su)
|
||||
|
||||
return su
|
||||
|
@ -512,7 +514,7 @@ func (app *Application) Build() error {
|
|||
// ErrServerClosed is returned by the Server's Serve, ServeTLS, ListenAndServe,
|
||||
// and ListenAndServeTLS methods after a call to Shutdown or Close.
|
||||
//
|
||||
// Conversion for the http.ErrServerClosed.
|
||||
// A shortcut for the `http#ErrServerClosed`.
|
||||
var ErrServerClosed = http.ErrServerClosed
|
||||
|
||||
// Run builds the framework and starts the desired `Runner` with or without configuration edits.
|
||||
|
@ -523,7 +525,9 @@ 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"))
|
||||
// Run(Raw(h.ListenAndServe), WithCharset("UTF-8"),
|
||||
// WithRemoteAddrHeader("CF-Connecting-IP"),
|
||||
// WithoutServerError(iris.ErrServerClosed))
|
||||
//
|
||||
// The Application can go online with any type of server or iris's host with the help of
|
||||
// the following runners:
|
||||
|
@ -536,7 +540,6 @@ func (app *Application) Run(serve Runner, withOrWithout ...Configurator) error {
|
|||
}
|
||||
|
||||
app.Configure(withOrWithout...)
|
||||
|
||||
// this will block until an error(unless supervisor's DeferFlow called from a Task).
|
||||
err := serve(app)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue
Block a user