mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
Add 'context.OnConnectionClose(callbackFn) bool' and 'context.OnClose(callbackFn)' and give a use case example. More on this path later on, stay tuned.
Former-commit-id: dc6580f072d076b8cb204a681e45905210981153
This commit is contained in:
parent
d98da25ffb
commit
5d9ded37c4
|
@ -6,6 +6,57 @@ This folder provides easy to understand code snippets on how to get started with
|
||||||
|
|
||||||
It doesn't always contain the "best ways" but it does cover each important feature that will make you so excited to GO with iris!
|
It doesn't always contain the "best ways" but it does cover each important feature that will make you so excited to GO with iris!
|
||||||
|
|
||||||
|
## Running the examples
|
||||||
|
|
||||||
|
1. Install the Go Programming Language, version 1.9+ from [here](https://golang.org/dl).
|
||||||
|
2. Install Iris: `go get -u github.com/kataras/iris`
|
||||||
|
3. Install any external packages that required by the examples
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>External packages</summary>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd _examples && go get ./...
|
||||||
|
# or
|
||||||
|
go get github.com/iris-contrib/middleware/...
|
||||||
|
go get github.com/betacraft/yaag/irisyaag
|
||||||
|
go get github.com/markbates/goth/...
|
||||||
|
go get github.com/getsentry/raven-go/...
|
||||||
|
go get github.com/casbin/casbin
|
||||||
|
go get github.com/markbates/goth/...
|
||||||
|
go get github.com/aws/aws-sdk-go/...
|
||||||
|
go get github.com/getsentry/raven-go/...
|
||||||
|
go get github.com/casbin/casbin
|
||||||
|
go get github.com/aws/aws-sdk-go/...
|
||||||
|
go get github.com/prometheus/client_golang/...
|
||||||
|
go get github.com/didip/tollbooth
|
||||||
|
go get github.com/valyala/quicktemplate
|
||||||
|
go get github.com/shiyanhui/hero
|
||||||
|
go get github.com/go-xorm/xorm
|
||||||
|
go get github.com/nfnt/resize
|
||||||
|
go get github.com/prometheus/client_golang/...
|
||||||
|
go get github.com/didip/tollbooth
|
||||||
|
go get github.com/valyala/quicktemplate
|
||||||
|
go get github.com/shiyanhui/hero
|
||||||
|
go get github.com/go-xorm/xorm
|
||||||
|
go get github.com/nfnt/resize
|
||||||
|
go get github.com/dgrijalva/jwt-go
|
||||||
|
go get github.com/newrelic/go-agent
|
||||||
|
go get github.com/valyala/tcplisten
|
||||||
|
go get github.com/kataras/bindata/cmd/bindata
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
And execute
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ cd $GOPATH/src/github.com/kataras/iris/_examples/overview
|
||||||
|
$ go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
> Test the examples by opening a terminal window and execute: `GOCACHE=off && cd _examples && go test -v ./...`
|
||||||
|
|
||||||
### Overview
|
### Overview
|
||||||
|
|
||||||
- [Hello world!](hello-world/main.go)
|
- [Hello world!](hello-world/main.go)
|
||||||
|
|
|
@ -54,7 +54,7 @@ func (r resource) loadFromBase(dir string) string {
|
||||||
result := string(b)
|
result := string(b)
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != "windows" {
|
||||||
result = strings.Replace(result, "\n", "\r\n", -1)
|
// result = strings.Replace(result, "\n", "\r\n", -1)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Code generated by bindata. DO NOT EDIT.
|
// Code generated by bindata. DO NOT EDIT.
|
||||||
// sources:
|
// sources:
|
||||||
// assets\css\bootstrap.min.css
|
// assets/css/bootstrap.min.css
|
||||||
// assets\favicon.ico
|
// assets/favicon.ico
|
||||||
// assets\js\jquery-2.1.1.js
|
// assets/js/jquery-2.1.1.js
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ func (fi gzipBindataFileInfo) Sys() interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _gzipBindataAssetscssbootstrapmincss = []byte(
|
var _gzipBindataAssetsCssBootstrapmincss = []byte(
|
||||||
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\xef\x8f\xe3\x38\x92\x20\xfa\xb9\x1a\xe8\xff\x41\x5b\x8d\x41\x57" +
|
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\xbd\xef\x8f\xe3\x38\x92\x20\xfa\xb9\x1a\xe8\xff\x41\x5b\x8d\x41\x57" +
|
||||||
"\x4d\x59\x2e\xf9\x77\xda\x89\xce\xb7\xfb\xe6\x0e\xb7\x03\xdc\xec\x97\x9b\x0f\x07\xf4\xf4\x7b\xa0\x25\xda\xd6\x94" +
|
"\x4d\x59\x2e\xf9\x77\xda\x89\xce\xb7\xfb\xe6\x0e\xb7\x03\xdc\xec\x97\x9b\x0f\x07\xf4\xf4\x7b\xa0\x25\xda\xd6\x94" +
|
||||||
"\x2c\x69\x24\x39\x2b\xb3\xfb\xf6\xfe\xf6\x83\xf8\x4b\x41\x32\x48\xd1\x4e\x77\xcf\xdc\x62\xb7\xee\x7a\x9c\x62\x44" +
|
"\x2c\x69\x24\x39\x2b\xb3\xfb\xf6\xfe\xf6\x83\xf8\x4b\x41\x32\x48\xd1\x4e\x77\xcf\xdc\x62\xb7\xee\x7a\x9c\x62\x44" +
|
||||||
|
@ -806,14 +806,14 @@ var _gzipBindataAssetscssbootstrapmincss = []byte(
|
||||||
"\x6f\x60\x83\x31\xfd\x46\x3e\xae\xb6\x0e\x28\xfe\xfd\xe4\x72\x0f\x83\x02\x88\x8b\x01\x82\xf5\x3c\x3e\x46\xfe\x4f" +
|
"\x6f\x60\x83\x31\xfd\x46\x3e\xae\xb6\x0e\x28\xfe\xfd\xe4\x72\x0f\x83\x02\x88\x8b\x01\x82\xf5\x3c\x3e\x46\xfe\x4f" +
|
||||||
"\x00\x00\x00\xff\xff\x47\x37\xb0\x07\xc9\x28\x02\x00")
|
"\x00\x00\x00\xff\xff\x47\x37\xb0\x07\xc9\x28\x02\x00")
|
||||||
|
|
||||||
func gzipBindataAssetscssbootstrapmincss() (*gzipAsset, error) {
|
func gzipBindataAssetsCssBootstrapmincss() (*gzipAsset, error) {
|
||||||
bytes := _gzipBindataAssetscssbootstrapmincss
|
bytes := _gzipBindataAssetsCssBootstrapmincss
|
||||||
info := gzipBindataFileInfo{
|
info := gzipBindataFileInfo{
|
||||||
name: "assets/css/bootstrap.min.css",
|
name: "assets/css/bootstrap.min.css",
|
||||||
size: 141513,
|
size: 141513,
|
||||||
md5checksum: "",
|
md5checksum: "",
|
||||||
mode: os.FileMode(438),
|
mode: os.FileMode(511),
|
||||||
modTime: time.Unix(1520997241, 0),
|
modTime: time.Unix(1521004692, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
a := &gzipAsset{bytes: bytes, info: info}
|
a := &gzipAsset{bytes: bytes, info: info}
|
||||||
|
@ -821,7 +821,7 @@ func gzipBindataAssetscssbootstrapmincss() (*gzipAsset, error) {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _gzipBindataAssetsfaviconico = []byte(
|
var _gzipBindataAssetsFaviconico = []byte(
|
||||||
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\x0b\x70\x55\xc7\x79\xde\x7b\xce\x45\x12\xb2\x90\xc4\xc3\xe6\x61" +
|
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x5a\x0b\x70\x55\xc7\x79\xde\x7b\xce\x45\x12\xb2\x90\xc4\xc3\xe6\x61" +
|
||||||
"\x3b\x90\xf8\x31\xc4\x19\x6c\x32\xe3\xc4\x34\xe3\xc6\x34\x6d\xed\xd4\x69\x62\x32\x49\x9a\xa6\x75\xea\x36\x33\xee" +
|
"\x3b\x90\xf8\x31\xc4\x19\x6c\x32\xe3\xc4\x34\xe3\xc6\x34\x6d\xed\xd4\x69\x62\x32\x49\x9a\xa6\x75\xea\x36\x33\xee" +
|
||||||
"\xd8\x9e\xb4\x75\xdc\x7a\xa6\xc5\x31\x02\xa6\xd3\x84\x47\x28\x6f\x3b\xc6\x3c\xcc\xeb\x9e\xbd\x08\x21\x04\x92\x28" +
|
"\xd8\x9e\xb4\x75\xdc\x7a\xa6\xc5\x31\x02\xa6\xd3\x84\x47\x28\x6f\x3b\xc6\x3c\xcc\xeb\x9e\xbd\x08\x21\x04\x92\x28" +
|
||||||
|
@ -967,14 +967,14 @@ var _gzipBindataAssetsfaviconico = []byte(
|
||||||
"\x0c\x24\x28\xcd\x41\x65\xd4\x2a\xc1\x2e\x17\x0e\x2a\x5b\xac\xb2\xc7\x2a\x4d\xab\x4c\xa1\xdf\xff\x0f\x00\x00\xff" +
|
"\x0c\x24\x28\xcd\x41\x65\xd4\x2a\xc1\x2e\x17\x0e\x2a\x5b\xac\xb2\xc7\x2a\x4d\xab\x4c\xa1\xdf\xff\x0f\x00\x00\xff" +
|
||||||
"\xff\xc6\xb9\x24\x2f\xee\x3a\x00\x00")
|
"\xff\xc6\xb9\x24\x2f\xee\x3a\x00\x00")
|
||||||
|
|
||||||
func gzipBindataAssetsfaviconico() (*gzipAsset, error) {
|
func gzipBindataAssetsFaviconico() (*gzipAsset, error) {
|
||||||
bytes := _gzipBindataAssetsfaviconico
|
bytes := _gzipBindataAssetsFaviconico
|
||||||
info := gzipBindataFileInfo{
|
info := gzipBindataFileInfo{
|
||||||
name: "assets/favicon.ico",
|
name: "assets/favicon.ico",
|
||||||
size: 15086,
|
size: 15086,
|
||||||
md5checksum: "",
|
md5checksum: "",
|
||||||
mode: os.FileMode(438),
|
mode: os.FileMode(511),
|
||||||
modTime: time.Unix(1520997241, 0),
|
modTime: time.Unix(1521004692, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
a := &gzipAsset{bytes: bytes, info: info}
|
a := &gzipAsset{bytes: bytes, info: info}
|
||||||
|
@ -982,7 +982,7 @@ func gzipBindataAssetsfaviconico() (*gzipAsset, error) {
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var _gzipBindataAssetsjsjquery211js = []byte(
|
var _gzipBindataAssetsJsJquery211js = []byte(
|
||||||
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\xfd\x7b\x77\xdb\x46\x96\x28\x8a\xff\x2d\xaf\xe5\xef\x50\xa2\x73\x64" +
|
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xcc\xfd\x7b\x77\xdb\x46\x96\x28\x8a\xff\x2d\xaf\xe5\xef\x50\xa2\x73\x64" +
|
||||||
"\xd0\xe2\x43\xb2\x93\x74\x42\x99\xd6\xcf\xb1\x9d\x1e\x9d\x5f\xec\xb8\x23\x67\x32\xf7\x4a\xca\xa4\x48\x14\xc5\xb2" +
|
"\xd0\xe2\x43\xb2\x93\x74\x42\x99\xd6\xcf\xb1\x9d\x1e\x9d\x5f\xec\xb8\x23\x67\x32\xf7\x4a\xca\xa4\x48\x14\xc5\xb2" +
|
||||||
"\x41\x80\x41\x81\x92\xd8\xa1\xfa\xb3\xdf\xb5\x1f\xf5\x02\x40\xd9\xee\x4e\xcf\x39\x3d\x6b\x62\x11\x28\xd4\x63\xd7" +
|
"\x41\x80\x41\x81\x92\xd8\xa1\xfa\xb3\xdf\xb5\x1f\xf5\x02\x40\xd9\xee\x4e\xcf\x39\x3d\x6b\x62\x11\x28\xd4\x63\xd7" +
|
||||||
|
@ -3634,14 +3634,14 @@ var _gzipBindataAssetsjsjquery211js = []byte(
|
||||||
"\xfa\x6a\x18\xef\xb5\xaa\xc6\xb4\x04\x30\x01\x3d\xd6\x92\x74\x16\xc4\xcc\x86\x81\x87\x9d\x94\xce\x89\xbb\xe5\xdc" +
|
"\xfa\x6a\x18\xef\xb5\xaa\xc6\xb4\x04\x30\x01\x3d\xd6\x92\x74\x16\xc4\xcc\x86\x81\x87\x9d\x94\xce\x89\xbb\xe5\xdc" +
|
||||||
"\x66\xfb\xff\x03\x00\x00\xff\xff\x1d\x30\x27\xdd\x1d\xea\x03\x00")
|
"\x66\xfb\xff\x03\x00\x00\xff\xff\x1d\x30\x27\xdd\x1d\xea\x03\x00")
|
||||||
|
|
||||||
func gzipBindataAssetsjsjquery211js() (*gzipAsset, error) {
|
func gzipBindataAssetsJsJquery211js() (*gzipAsset, error) {
|
||||||
bytes := _gzipBindataAssetsjsjquery211js
|
bytes := _gzipBindataAssetsJsJquery211js
|
||||||
info := gzipBindataFileInfo{
|
info := gzipBindataFileInfo{
|
||||||
name: "assets/js/jquery-2.1.1.js",
|
name: "assets/js/jquery-2.1.1.js",
|
||||||
size: 256541,
|
size: 256541,
|
||||||
md5checksum: "",
|
md5checksum: "",
|
||||||
mode: os.FileMode(438),
|
mode: os.FileMode(511),
|
||||||
modTime: time.Unix(1520997241, 0),
|
modTime: time.Unix(1521004692, 0),
|
||||||
}
|
}
|
||||||
|
|
||||||
a := &gzipAsset{bytes: bytes, info: info}
|
a := &gzipAsset{bytes: bytes, info: info}
|
||||||
|
@ -3706,9 +3706,9 @@ func GzipAssetNames() []string {
|
||||||
// _gzipbindata is a table, holding each asset generator, mapped to its name.
|
// _gzipbindata is a table, holding each asset generator, mapped to its name.
|
||||||
//
|
//
|
||||||
var _gzipbindata = map[string]func() (*gzipAsset, error){
|
var _gzipbindata = map[string]func() (*gzipAsset, error){
|
||||||
"assets/css/bootstrap.min.css": gzipBindataAssetscssbootstrapmincss,
|
"assets/css/bootstrap.min.css": gzipBindataAssetsCssBootstrapmincss,
|
||||||
"assets/favicon.ico": gzipBindataAssetsfaviconico,
|
"assets/favicon.ico": gzipBindataAssetsFaviconico,
|
||||||
"assets/js/jquery-2.1.1.js": gzipBindataAssetsjsjquery211js,
|
"assets/js/jquery-2.1.1.js": gzipBindataAssetsJsJquery211js,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3764,11 +3764,11 @@ type gzipBintree struct {
|
||||||
var _gzipbintree = &gzipBintree{Func: nil, Children: map[string]*gzipBintree{
|
var _gzipbintree = &gzipBintree{Func: nil, Children: map[string]*gzipBintree{
|
||||||
"assets": {Func: nil, Children: map[string]*gzipBintree{
|
"assets": {Func: nil, Children: map[string]*gzipBintree{
|
||||||
"css": {Func: nil, Children: map[string]*gzipBintree{
|
"css": {Func: nil, Children: map[string]*gzipBintree{
|
||||||
"bootstrap.min.css": {Func: gzipBindataAssetscssbootstrapmincss, Children: map[string]*gzipBintree{}},
|
"bootstrap.min.css": {Func: gzipBindataAssetsCssBootstrapmincss, Children: map[string]*gzipBintree{}},
|
||||||
}},
|
}},
|
||||||
"favicon.ico": {Func: gzipBindataAssetsfaviconico, Children: map[string]*gzipBintree{}},
|
"favicon.ico": {Func: gzipBindataAssetsFaviconico, Children: map[string]*gzipBintree{}},
|
||||||
"js": {Func: nil, Children: map[string]*gzipBintree{
|
"js": {Func: nil, Children: map[string]*gzipBintree{
|
||||||
"jquery-2.1.1.js": {Func: gzipBindataAssetsjsjquery211js, Children: map[string]*gzipBintree{}},
|
"jquery-2.1.1.js": {Func: gzipBindataAssetsJsJquery211js, Children: map[string]*gzipBintree{}},
|
||||||
}},
|
}},
|
||||||
}},
|
}},
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (r resource) loadFromBase(dir string) string {
|
||||||
result := string(b)
|
result := string(b)
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != "windows" {
|
||||||
result = strings.Replace(result, "\n", "\r\n", -1)
|
// result = strings.Replace(result, "\n", "\r\n", -1)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -98,8 +98,11 @@ func TestEmbeddingGzipFilesIntoApp(t *testing.T) {
|
||||||
}
|
}
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
reader.WriteTo(buf)
|
reader.WriteTo(buf)
|
||||||
if rawContents != buf.String() {
|
if expected, got := rawContents, buf.String(); expected != got {
|
||||||
t.Fatalf("[%d] of '%s': expected body:\n%s but got:\n%s", i, url, rawContents, buf.String())
|
// t.Fatalf("[%d] of '%s': expected body:\n%s but got:\n%s", i, url, expected, got)
|
||||||
|
// let's reduce the output here...
|
||||||
|
// they are big files, no need to check for length here.
|
||||||
|
t.Fatalf("[%d] %s, expected body to look like: '%s...%s' but got '%s...%s'", i, url, expected[:40], expected[len(rawContents)-40:], got[:40], got[len(got)-40:])
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ func (r resource) loadFromBase(dir string) string {
|
||||||
}
|
}
|
||||||
result := string(b)
|
result := string(b)
|
||||||
if runtime.GOOS != "windows" {
|
if runtime.GOOS != "windows" {
|
||||||
result = strings.Replace(result, "\n", "\r\n", -1)
|
// result = strings.Replace(result, "\n", "\r\n", -1)
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,9 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/kataras/golog"
|
"github.com/kataras/golog"
|
||||||
|
|
||||||
"github.com/kataras/iris"
|
"github.com/kataras/iris"
|
||||||
// Note:
|
// Note:
|
||||||
// For some reason the latest vscode-go language extension does not provide enough intelligence (parameters documentation and go to definition features)
|
// For some reason the latest vscode-go language extension does not provide enough intelligence (parameters documentation and go to definition features)
|
||||||
|
@ -80,10 +78,10 @@ func (b *Broker) listen() {
|
||||||
|
|
||||||
func (b *Broker) ServeHTTP(ctx context.Context) {
|
func (b *Broker) ServeHTTP(ctx context.Context) {
|
||||||
// Make sure that the writer supports flushing.
|
// Make sure that the writer supports flushing.
|
||||||
//
|
|
||||||
flusher, ok := ctx.ResponseWriter().(http.Flusher)
|
flusher, ok := ctx.ResponseWriter().Flusher()
|
||||||
if !ok {
|
if !ok {
|
||||||
ctx.StatusCode(iris.StatusInternalServerError)
|
ctx.StatusCode(iris.StatusHTTPVersionNotSupported)
|
||||||
ctx.WriteString("Streaming unsupported!")
|
ctx.WriteString("Streaming unsupported!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -102,19 +100,27 @@ func (b *Broker) ServeHTTP(ctx context.Context) {
|
||||||
// Signal the broker that we have a new connection.
|
// Signal the broker that we have a new connection.
|
||||||
b.newClients <- messageChan
|
b.newClients <- messageChan
|
||||||
|
|
||||||
// Remove this client from the map of connected clients
|
// Listen to connection close or when the entire request handler chain exits and un-register messageChan.
|
||||||
// when this handler exits.
|
// using the `ctx.ResponseWriter().CloseNotifier()` and `defer` for this single handler of the route:
|
||||||
defer func() {
|
/*
|
||||||
b.closingClients <- messageChan
|
notifier, ok := ctx.ResponseWriter().CloseNotifier()
|
||||||
}()
|
if ok {
|
||||||
|
go func() {
|
||||||
|
<-notifier.CloseNotify()
|
||||||
|
b.closingClients <- messageChan
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// Listen to connection close and un-register messageChan.
|
defer func() {
|
||||||
notify := ctx.ResponseWriter().(http.CloseNotifier).CloseNotify()
|
b.closingClients <- messageChan
|
||||||
|
}()
|
||||||
go func() {
|
*/
|
||||||
<-notify
|
// or by using the `ctx.OnClose`, which will take care all of the above for you:
|
||||||
|
ctx.OnClose(func() {
|
||||||
|
// Remove this client from the map of connected clients
|
||||||
|
// when this handler exits.
|
||||||
b.closingClients <- messageChan
|
b.closingClients <- messageChan
|
||||||
}()
|
})
|
||||||
|
|
||||||
// block waiting for messages broadcast on this connection's messageChan.
|
// block waiting for messages broadcast on this connection's messageChan.
|
||||||
for {
|
for {
|
||||||
|
@ -164,5 +170,5 @@ func main() {
|
||||||
// TIP: If you make use of it inside a web frontend application
|
// TIP: If you make use of it inside a web frontend application
|
||||||
// then checkout the "optional.sse.js.html" to use the javascript's API for SSE,
|
// then checkout the "optional.sse.js.html" to use the javascript's API for SSE,
|
||||||
// it will also remove the browser's "loading" indicator while receiving those event messages.
|
// it will also remove the browser's "loading" indicator while receiving those event messages.
|
||||||
app.Run(iris.Addr(":8080"))
|
app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// +build go1.11beta2
|
// +build go1.11beta2
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -340,6 +340,32 @@ type Context interface {
|
||||||
// IsStopped checks and returns true if the current position of the Context is 255,
|
// IsStopped checks and returns true if the current position of the Context is 255,
|
||||||
// means that the StopExecution() was called.
|
// means that the StopExecution() was called.
|
||||||
IsStopped() bool
|
IsStopped() bool
|
||||||
|
// OnConnectionClose registers the "cb" function which will fire (on its own goroutine, no need to be registered goroutine by the end-dev)
|
||||||
|
// when the underlying connection has gone away.
|
||||||
|
//
|
||||||
|
// This mechanism can be used to cancel long operations on the server
|
||||||
|
// if the client has disconnected before the response is ready.
|
||||||
|
//
|
||||||
|
// It depends on the `http#CloseNotify`.
|
||||||
|
// CloseNotify may wait to notify until Request.Body has been
|
||||||
|
// fully read.
|
||||||
|
//
|
||||||
|
// After the main Handler has returned, there is no guarantee
|
||||||
|
// that the channel receives a value.
|
||||||
|
//
|
||||||
|
// Finally, it reports whether the protocol supports pipelines (HTTP/1.1 with pipelines disabled is not supported).
|
||||||
|
// The "cb" will not fire for sure if the output value is false.
|
||||||
|
//
|
||||||
|
// Note that you can register only one callback for the entire request handler chain/per route.
|
||||||
|
//
|
||||||
|
// Look the `ResponseWriter#CloseNotifier` for more.
|
||||||
|
OnConnectionClose(fnGoroutine func()) bool
|
||||||
|
// OnClose registers the callback function "cb" to the underline connection closing event using the `Context#OnConnectionClose`
|
||||||
|
// and also in the end of the request handler using the `ResponseWriter#SetBeforeFlush`.
|
||||||
|
// Note that you can register only one callback for the entire request handler chain/per route.
|
||||||
|
//
|
||||||
|
// Look the `Context#OnConnectionClose` and `ResponseWriter#SetBeforeFlush` for more.
|
||||||
|
OnClose(cb func())
|
||||||
|
|
||||||
// +------------------------------------------------------------+
|
// +------------------------------------------------------------+
|
||||||
// | Current "user/request" storage |
|
// | Current "user/request" storage |
|
||||||
|
@ -1355,6 +1381,76 @@ func (ctx *context) IsStopped() bool {
|
||||||
return ctx.currentHandlerIndex == stopExecutionIndex
|
return ctx.currentHandlerIndex == stopExecutionIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnConnectionClose registers the "cb" function which will fire (on its own goroutine, no need to be registered goroutine by the end-dev)
|
||||||
|
// when the underlying connection has gone away.
|
||||||
|
//
|
||||||
|
// This mechanism can be used to cancel long operations on the server
|
||||||
|
// if the client has disconnected before the response is ready.
|
||||||
|
//
|
||||||
|
// It depends on the `http#CloseNotify`.
|
||||||
|
// CloseNotify may wait to notify until Request.Body has been
|
||||||
|
// fully read.
|
||||||
|
//
|
||||||
|
// After the main Handler has returned, there is no guarantee
|
||||||
|
// that the channel receives a value.
|
||||||
|
//
|
||||||
|
// Finally, it reports whether the protocol supports pipelines (HTTP/1.1 with pipelines disabled is not supported).
|
||||||
|
// The "cb" will not fire for sure if the output value is false.
|
||||||
|
//
|
||||||
|
// Note that you can register only one callback for the entire request handler chain/per route.
|
||||||
|
//
|
||||||
|
// Look the `ResponseWriter#CloseNotifier` for more.
|
||||||
|
func (ctx *context) OnConnectionClose(cb func()) bool {
|
||||||
|
// Note that `ctx.ResponseWriter().CloseNotify()` can already do the same
|
||||||
|
// but it returns a channel which will never fire if it the protocol version is not compatible,
|
||||||
|
// here we don't want to allocate an empty channel, just skip it.
|
||||||
|
notifier, ok := ctx.writer.CloseNotifier()
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
notify := notifier.CloseNotify()
|
||||||
|
go func() {
|
||||||
|
<-notify
|
||||||
|
if cb != nil {
|
||||||
|
cb()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnClose registers the callback function "cb" to the underline connection closing event using the `Context#OnConnectionClose`
|
||||||
|
// and also in the end of the request handler using the `ResponseWriter#SetBeforeFlush`.
|
||||||
|
// Note that you can register only one callback for the entire request handler chain/per route.
|
||||||
|
//
|
||||||
|
// Look the `Context#OnConnectionClose` and `ResponseWriter#SetBeforeFlush` for more.
|
||||||
|
func (ctx *context) OnClose(cb func()) {
|
||||||
|
if cb == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the on underline connection close handler first.
|
||||||
|
ctx.OnConnectionClose(cb)
|
||||||
|
|
||||||
|
// Author's notes:
|
||||||
|
// This is fired on `ctx.ResponseWriter().FlushResponse()` which is fired by the framework automatically, internally, on the end of request handler(s),
|
||||||
|
// it is not fired on the underline streaming function of the writer: `ctx.ResponseWriter().Flush()` (which can be fired more than one if streaming is supported by the client).
|
||||||
|
// The `FlushResponse` is called only once, so add the "cb" here, no need to add done request handlers each time `OnClose` is called by the end-dev.
|
||||||
|
//
|
||||||
|
// Don't allow more than one because we don't allow that on `OnConnectionClose` too:
|
||||||
|
// old := ctx.writer.GetBeforeFlush()
|
||||||
|
// if old != nil {
|
||||||
|
// ctx.writer.SetBeforeFlush(func() {
|
||||||
|
// old()
|
||||||
|
// cb()
|
||||||
|
// })
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
ctx.writer.SetBeforeFlush(cb)
|
||||||
|
}
|
||||||
|
|
||||||
// +------------------------------------------------------------+
|
// +------------------------------------------------------------+
|
||||||
// | Current "user/request" storage |
|
// | Current "user/request" storage |
|
||||||
// | and share information between the handlers - Values(). |
|
// | and share information between the handlers - Values(). |
|
||||||
|
|
|
@ -83,6 +83,21 @@ type ResponseWriter interface {
|
||||||
|
|
||||||
// WiteTo writes a response writer (temp: status code, headers and body) to another response writer
|
// WiteTo writes a response writer (temp: status code, headers and body) to another response writer
|
||||||
WriteTo(ResponseWriter)
|
WriteTo(ResponseWriter)
|
||||||
|
|
||||||
|
// Flusher indicates if `Flush` is supported by the client.
|
||||||
|
//
|
||||||
|
// The default HTTP/1.x and HTTP/2 ResponseWriter implementations
|
||||||
|
// support Flusher, but ResponseWriter wrappers may not. Handlers
|
||||||
|
// should always test for this ability at runtime.
|
||||||
|
//
|
||||||
|
// Note that even for ResponseWriters that support Flush,
|
||||||
|
// if the client is connected through an HTTP proxy,
|
||||||
|
// the buffered data may not reach the client until the response
|
||||||
|
// completes.
|
||||||
|
Flusher() (http.Flusher, bool)
|
||||||
|
|
||||||
|
// CloseNotifier indicates if the protocol supports the underline connection closure notification.
|
||||||
|
CloseNotifier() (http.CloseNotifier, bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// +------------------------------------------------------------+
|
// +------------------------------------------------------------+
|
||||||
|
@ -296,21 +311,25 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
return nil, nil, errors.New("hijack is not supported by this ResponseWriter")
|
return nil, nil, errors.New("hijack is not supported by this ResponseWriter")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flusher indicates if `Flush` is supported by the client.
|
||||||
|
//
|
||||||
|
// The default HTTP/1.x and HTTP/2 ResponseWriter implementations
|
||||||
|
// support Flusher, but ResponseWriter wrappers may not. Handlers
|
||||||
|
// should always test for this ability at runtime.
|
||||||
|
//
|
||||||
|
// Note that even for ResponseWriters that support Flush,
|
||||||
|
// if the client is connected through an HTTP proxy,
|
||||||
|
// the buffered data may not reach the client until the response
|
||||||
|
// completes.
|
||||||
|
func (w *responseWriter) Flusher() (http.Flusher, bool) {
|
||||||
|
flusher, canFlush := w.ResponseWriter.(http.Flusher)
|
||||||
|
return flusher, canFlush
|
||||||
|
}
|
||||||
|
|
||||||
// Flush sends any buffered data to the client.
|
// Flush sends any buffered data to the client.
|
||||||
func (w *responseWriter) Flush() {
|
func (w *responseWriter) Flush() {
|
||||||
// The Flusher interface is implemented by ResponseWriters that allow
|
if flusher, ok := w.Flusher(); ok {
|
||||||
// an HTTP handler to flush buffered data to the client.
|
flusher.Flush()
|
||||||
//
|
|
||||||
// The default HTTP/1.x and HTTP/2 ResponseWriter implementations
|
|
||||||
// support Flusher, but ResponseWriter wrappers may not. Handlers
|
|
||||||
// should always test for this ability at runtime.
|
|
||||||
//
|
|
||||||
// Note that even for ResponseWriters that support Flush,
|
|
||||||
// if the client is connected through an HTTP proxy,
|
|
||||||
// the buffered data may not reach the client until the response
|
|
||||||
// completes.
|
|
||||||
if fl, isFlusher := w.ResponseWriter.(http.Flusher); isFlusher {
|
|
||||||
fl.Flush()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,6 +368,12 @@ func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
|
||||||
return ErrPushNotSupported
|
return ErrPushNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CloseNotifier indicates if the protocol supports the underline connection closure notification.
|
||||||
|
func (w *responseWriter) CloseNotifier() (http.CloseNotifier, bool) {
|
||||||
|
notifier, supportsCloseNotify := w.ResponseWriter.(http.CloseNotifier)
|
||||||
|
return notifier, supportsCloseNotify
|
||||||
|
}
|
||||||
|
|
||||||
// CloseNotify returns a channel that receives at most a
|
// CloseNotify returns a channel that receives at most a
|
||||||
// single value (true) when the client connection has gone
|
// single value (true) when the client connection has gone
|
||||||
// away.
|
// away.
|
||||||
|
@ -368,9 +393,10 @@ func (w *responseWriter) Push(target string, opts *http.PushOptions) error {
|
||||||
// is a problem, use HTTP/2 or only use CloseNotify on methods
|
// is a problem, use HTTP/2 or only use CloseNotify on methods
|
||||||
// such as POST.
|
// such as POST.
|
||||||
func (w *responseWriter) CloseNotify() <-chan bool {
|
func (w *responseWriter) CloseNotify() <-chan bool {
|
||||||
if notifier, supportsCloseNotify := w.ResponseWriter.(http.CloseNotifier); supportsCloseNotify {
|
if notifier, ok := w.CloseNotifier(); ok {
|
||||||
return notifier.CloseNotify()
|
return notifier.CloseNotify()
|
||||||
}
|
}
|
||||||
|
|
||||||
ch := make(chan bool, 1)
|
ch := make(chan bool, 1)
|
||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user