1
0
mirror of https://github.com/kataras/iris.git synced 2025-12-19 19:07:06 +00:00

(#1554) Add support for all common compressions (write and read)

- Remove the context.Context interface and export the *context, the iris.Context now points to the pointer\nSupport compression and rate limiting in the FileServer\nBit of code organisation


Former-commit-id: ad1c61bf968059510c6be9e7f2cceec7da70ba17
This commit is contained in:
Gerasimos (Makis) Maropoulos
2020-07-10 23:21:09 +03:00
parent 645da2b2ef
commit 0f113dfcda
112 changed files with 2119 additions and 3390 deletions

View File

@@ -1,15 +1,10 @@
package iris
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
@@ -20,6 +15,7 @@ import (
"github.com/BurntSushi/toml"
"github.com/kataras/sitemap"
"github.com/kataras/tunnel"
"gopkg.in/yaml.v3"
)
@@ -567,222 +563,14 @@ var WithTunneling = func(app *Application) {
app.config.Tunneling = conf
}
// Tunnel is the Tunnels field of the TunnelingConfiguration structure.
type Tunnel struct {
// Name is the only one required field,
// it is used to create and close tunnels, e.g. "MyApp".
// If this field is not empty then ngrok tunnels will be created
// when the iris app is up and running.
Name string `json:"name" yaml:"Name" toml:"Name"`
// Addr is basically optionally as it will be set through
// Iris built-in Runners, however, if `iris.Raw` is used
// then this field should be set of form 'hostname:port'
// because framework cannot be aware
// of the address you used to run the server on this custom runner.
Addr string `json:"addr,omitempty" yaml:"Addr" toml:"Addr"`
}
// TunnelingConfiguration contains configuration
// for the optional tunneling through ngrok feature.
// Note that the ngrok should be already installed at the host machine.
type TunnelingConfiguration struct {
// AuthToken field is optionally and can be used
// to authenticate the ngrok access.
// ngrok authtoken <YOUR_AUTHTOKEN>
AuthToken string `json:"authToken,omitempty" yaml:"AuthToken" toml:"AuthToken"`
// No...
// Config is optionally and can be used
// to load ngrok configuration from file system path.
//
// If you don't specify a location for a configuration file,
// ngrok tries to read one from the default location $HOME/.ngrok2/ngrok.yml.
// The configuration file is optional; no error is emitted if that path does not exist.
// Config string `json:"config,omitempty" yaml:"Config" toml:"Config"`
// Bin is the system binary path of the ngrok executable file.
// If it's empty then the framework will try to find it through system env variables.
Bin string `json:"bin,omitempty" yaml:"Bin" toml:"Bin"`
// WebUIAddr is the web interface address of an already-running ngrok instance.
// Iris will try to fetch the default web interface address(http://127.0.0.1:4040)
// to determinate if a ngrok instance is running before try to start it manually.
// However if a custom web interface address is used,
// this field must be set e.g. http://127.0.0.1:5050.
WebInterface string `json:"webInterface,omitempty" yaml:"WebInterface" toml:"WebInterface"`
// Region is optionally, can be used to set the region which defaults to "us".
// Available values are:
// "us" for United States
// "eu" for Europe
// "ap" for Asia/Pacific
// "au" for Australia
// "sa" for South America
// "jp" forJapan
// "in" for India
Region string `json:"region,omitempty" yaml:"Region" toml:"Region"`
// Tunnels the collection of the tunnels.
// One tunnel per Iris Host per Application, usually you only need one.
Tunnels []Tunnel `json:"tunnels" yaml:"Tunnels" toml:"Tunnels"`
}
func (tc *TunnelingConfiguration) isEnabled() bool {
return tc != nil && len(tc.Tunnels) > 0
}
func (tc *TunnelingConfiguration) isNgrokRunning() bool {
resp, err := http.Get(tc.WebInterface)
if err != nil {
return false
}
resp.Body.Close()
return true
}
// https://ngrok.com/docs
type ngrokTunnel struct {
Name string `json:"name"`
Addr string `json:"addr"`
Proto string `json:"proto"`
Auth string `json:"auth"`
BindTLS bool `json:"bind_tls"`
}
func (tc TunnelingConfiguration) startTunnel(t Tunnel, publicAddr *string) error {
tunnelAPIRequest := ngrokTunnel{
Name: t.Name,
Addr: t.Addr,
Proto: "http",
BindTLS: true,
}
if !tc.isNgrokRunning() {
ngrokBin := "ngrok" // environment binary.
if tc.Bin == "" {
_, err := exec.LookPath(ngrokBin)
if err != nil {
ngrokEnvVar, found := os.LookupEnv("NGROK")
if !found {
return fmt.Errorf(`"ngrok" executable not found, please install it from: https://ngrok.com/download`)
}
ngrokBin = ngrokEnvVar
}
} else {
ngrokBin = tc.Bin
}
if tc.AuthToken != "" {
cmd := exec.Command(ngrokBin, "authtoken", tc.AuthToken)
err := cmd.Run()
if err != nil {
return err
}
}
// start -none, start without tunnels.
// and finally the -log stdout logs to the stdout otherwise the pipe will never be able to read from, spent a lot of time on this lol.
cmd := exec.Command(ngrokBin, "start", "-none", "-log", "stdout")
// if tc.Config != "" {
// cmd.Args = append(cmd.Args, []string{"-config", tc.Config}...)
// }
if tc.Region != "" {
cmd.Args = append(cmd.Args, []string{"-region", tc.Region}...)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
return err
}
if err = cmd.Start(); err != nil {
return err
}
p := make([]byte, 256)
okText := []byte("client session established")
for {
n, err := stdout.Read(p)
if err != nil {
return err
}
// we need this one:
// msg="client session established"
// note that this will block if something terrible happens
// but ngrok's errors are strong so the error is easy to be resolved without any logs.
if bytes.Contains(p[:n], okText) {
break
}
}
}
return tc.createTunnel(tunnelAPIRequest, publicAddr)
}
func (tc TunnelingConfiguration) stopTunnel(t Tunnel) error {
url := fmt.Sprintf("%s/api/tunnels/%s", tc.WebInterface, t.Name)
req, err := http.NewRequest(http.MethodDelete, url, nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != StatusNoContent {
return fmt.Errorf("stop return an unexpected status code: %d", resp.StatusCode)
}
return nil
}
func (tc TunnelingConfiguration) createTunnel(tunnelAPIRequest ngrokTunnel, publicAddr *string) error {
url := fmt.Sprintf("%s/api/tunnels", tc.WebInterface)
requestData, err := json.Marshal(tunnelAPIRequest)
if err != nil {
return err
}
resp, err := http.Post(url, context.ContentJSONHeaderValue, bytes.NewBuffer(requestData))
if err != nil {
return err
}
defer resp.Body.Close()
type publicAddrOrErrResp struct {
PublicAddr string `json:"public_url"`
Details struct {
ErrorText string `json:"err"` // when can't bind more addresses, status code was successful.
} `json:"details"`
ErrMsg string `json:"msg"` // when ngrok is not yet ready, status code was unsuccessful.
}
var apiResponse publicAddrOrErrResp
err = json.NewDecoder(resp.Body).Decode(&apiResponse)
if err != nil {
return err
}
if errText := apiResponse.ErrMsg; errText != "" {
return errors.New(errText)
}
if errText := apiResponse.Details.ErrorText; errText != "" {
return errors.New(errText)
}
*publicAddr = apiResponse.PublicAddr
return nil
}
type (
// TunnelingConfiguration contains configuration
// for the optional tunneling through ngrok feature.
// Note that the ngrok should be already installed at the host machine.
TunnelingConfiguration = tunnel.Configuration
// Tunnel is the Tunnels field of the TunnelingConfiguration structure.
Tunnel = tunnel.Tunnel
)
// Configuration holds the necessary settings for an Iris Application instance.
// All fields are optionally, the default values will work for a common web application.
@@ -904,7 +692,7 @@ type Configuration struct {
// Defaults to false.
DisableAutoFireStatusCode bool `json:"disableAutoFireStatusCode,omitempty" yaml:"DisableAutoFireStatusCode" toml:"DisableAutoFireStatusCode"`
// ResetOnFireErrorCode if true then any previously response body or headers through
// response recorder or gzip writer will be ignored and the router
// response recorder will be ignored and the router
// will fire the registered (or default) HTTP error handler instead.
// See `core/router/handler#FireErrorCode` and `Context.EndRequest` for more details.
//
@@ -1210,7 +998,7 @@ func WithConfiguration(c Configuration) Configurator {
main.SocketSharding = v
}
if c.Tunneling.isEnabled() {
if len(c.Tunneling.Tunnels) > 0 {
main.Tunneling = c.Tunneling
}