Update to 8.2.2 | Google reCAPTCHA. Read HISTORY.md

Former-commit-id: dc8ee74fa33b2fbc41659c9097955afb152d5a6f
This commit is contained in:
kataras 2017-08-10 15:21:42 +03:00
parent be8295eb50
commit 4da5cd47f1
9 changed files with 218 additions and 73 deletions

View File

@ -18,6 +18,26 @@ 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`.
# Th, 10 August 2017 | v8.2.2
No API Changes.
- Implement [Google reCAPTCHA](middleware/recaptcha) middleware
- Fix [kataras/golog](https://github.com/kataras/golog) prints with colors on windows server 2012 while it shouldn't because its command line tool does not support 256bit colors
- Improve the updater by a custom self-updated back-end version checker, can be disabled by:
```go
app.Run(iris.Addr(":8080"), iris.WithoutVersionChecker)
```
Or
```go
app.Configure(iris.WithoutVersionChecker)
```
Or
```go
app.Configure(iris.WithConfiguration(iris.Configuration{DisableVersionChecker:true}))
```
# Tu, 08 August 2017 | v8.2.1

View File

@ -20,7 +20,7 @@ Iris is a fast, simple and efficient micro web framework for Go. It provides a b
### 📑 Table of contents
* [Installation](#-installation)
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#tu-08-august-2017--v821)
* [Latest changes](https://github.com/kataras/iris/blob/master/HISTORY.md#th-10-august-2017--v822)
* [Learn](#-learn)
* [HTTP Listening](_examples/#http-listening)
* [Configuration](_examples/#configuration)
@ -319,7 +319,7 @@ Thank You for your trust!
### 📌 Version
Current: **8.2.1**
Current: **8.2.2**
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".

View File

@ -1 +1 @@
8.2.1:https://github.com/kataras/iris/blob/master/HISTORY.md#tu-08-august-2017--v821
8.2.2:https://github.com/kataras/iris/blob/master/HISTORY.md#th-10-august-2017--v822

View File

@ -136,9 +136,9 @@ var WithoutInterruptHandler = func(app *Application) {
app.config.DisableInterruptHandler = true
}
// WithoutVersionCheck will disable the version checker and updater.
var WithoutVersionCheck = func(app *Application) {
app.config.DisableVersionCheck = true
// WithoutVersionChecker will disable the version checker and updater.
var WithoutVersionChecker = func(app *Application) {
app.config.DisableVersionChecker = true
}
// WithoutPathCorrection disables the PathCorrection setting.
@ -281,10 +281,10 @@ type Configuration struct {
// Defaults to false.
DisableInterruptHandler bool `yaml:"DisableInterruptHandler" toml:"DisableInterruptHandler"`
// DisableVersionCheck if true then process will be not be notified for any available updates.
// DisableVersionChecker if true then process will be not be notified for any available updates.
//
// Defaults to false.
DisableVersionCheck bool `yaml:"DisableVersionCheck" toml:"DisableVersionCheck"`
DisableVersionChecker bool `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,
@ -607,7 +607,7 @@ func DefaultConfiguration() Configuration {
return Configuration{
DisableStartupLog: false,
DisableInterruptHandler: false,
DisableVersionCheck: false,
DisableVersionChecker: false,
DisablePathCorrection: false,
EnablePathEscape: false,
FireMethodNotAllowed: false,

33
core/netutil/client.go Normal file
View File

@ -0,0 +1,33 @@
package netutil
import (
"net"
"net/http"
"time"
"github.com/kataras/golog"
)
// Client returns a new http.Client using
// the "timeout" for open connection and read-write operations.
func Client(timeout time.Duration) *http.Client {
transport := http.Transport{
Dial: func(network string, addr string) (net.Conn, error) {
conn, err := net.DialTimeout(network, addr, timeout)
if err != nil {
golog.Debugf("%v", err)
return nil, err
}
if err = conn.SetDeadline(time.Now().Add(timeout)); err != nil {
golog.Debugf("%v", err)
}
return conn, err
},
}
client := &http.Client{
Transport: &transport,
}
return client
}

2
doc.go
View File

@ -35,7 +35,7 @@ Source code and other details for the project are available at GitHub:
Current Version
8.2.1
8.2.2
Installation

View File

@ -32,7 +32,7 @@ import (
const (
// Version is the current version number of the Iris Web Framework.
Version = "8.2.1"
Version = "8.2.2"
)
// HTTP status codes as registered with IANA.
@ -648,7 +648,7 @@ 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.DisableVersionCheck && app.logger.Printer.IsTerminal {
if !app.config.DisableVersionChecker && app.logger.Printer.IsTerminal {
go CheckVersion()
}

View File

@ -0,0 +1,127 @@
package recaptcha
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/url"
"time"
"github.com/kataras/iris/context"
"github.com/kataras/iris/core/netutil"
)
const (
responseFormValue = "g-recaptcha-response"
apiURL = "https://www.google.com/recaptcha/api/siteverify"
)
// Response is the google's recaptcha response as JSON.
type Response struct {
ChallengeTS time.Time `json:"challenge_ts"`
Hostname string `json:"hostname"`
ErrorCodes []string `json:"error-codes"`
Success bool `json:"success"`
}
var client = netutil.Client(time.Duration(10 * time.Second))
// New accepts the google's recaptcha secret key and returns
// a middleware that verifies the request by sending a response to the google's API(V2-latest).
// Secret key can be obtained by https://www.google.com/recaptcha.
//
// Used for communication between your site and Google. Be sure to keep it a secret.
//
// Use `SiteVerify` to verify a request inside another handler if needed.
func New(secret string) context.Handler {
return func(ctx context.Context) {
if SiteFerify(ctx, secret).Success {
ctx.Next()
}
}
}
// SiteFerify accepts context and the secret key(https://www.google.com/recaptcha)
// and returns the google's recaptcha response, if `response.Success` is true
// then validation passed.
//
// Use `New` for middleware use instead.
func SiteFerify(ctx context.Context, secret string) (response Response) {
generatedResponseID := ctx.FormValue("g-recaptcha-response")
if generatedResponseID == "" {
response.ErrorCodes = append(response.ErrorCodes,
"form value[g-recaptcha-response] is empty")
return
}
r, err := client.PostForm(apiURL,
url.Values{
"secret": {secret},
"response": {generatedResponseID},
// optional: let's no track our users "remoteip": {ctx.RemoteAddr()},
},
)
if err != nil {
response.ErrorCodes = append(response.ErrorCodes, err.Error())
return
}
body, err := ioutil.ReadAll(r.Body)
r.Body.Close()
if err != nil {
response.ErrorCodes = append(response.ErrorCodes, err.Error())
return
}
err = json.Unmarshal(body, &response)
if err != nil {
response.ErrorCodes = append(response.ErrorCodes, err.Error())
return
}
return response
}
var recaptchaForm = `<form action="%s" method="POST">
<script src="https://www.google.com/recaptcha/api.js"></script>
<div class="g-recaptcha" data-sitekey="%s"></div>
<input type="submit" name="button" value="OK">
</form>`
// GetFormHTML can be used on pages where empty form
// is enough to verify that the client is not a "robot".
// i.e: GetFormHTML("public_key", "/contact")
// will return form tag which imports the google API script,
// with a simple submit button where redirects to the
// "postActionRelativePath".
//
// The "postActionRelativePath" MUST use the `SiteVerify` or
// followed by the `New()`'s context.Handler (with the secret key this time)
// in order to validate if the recaptcha verified.
//
// The majority of applications will use a custom form,
// this function is here for ridiculous simple use cases.
//
// Example Code:
//
// Method: "POST" | Path: "/contact"
// func postContact(ctx context.Context) {
// // [...]
// response := recaptcha.SiteFerify(ctx, recaptchaSecret)
//
// if response.Success {
// // [your action here, i.e sendEmail(...)]
// }
//
// ctx.JSON(response)
// }
//
// Method: "GET" | Path: "/contact"
// func getContact(ctx context.Context) {
// // render the recaptcha form
// ctx.HTML(recaptcha.GetFormHTML(recaptchaPublic, "/contact"))
// }
func GetFormHTML(dataSiteKey string, postActionRelativePath string) string {
return fmt.Sprintf(recaptchaForm, postActionRelativePath, dataSiteKey)
}

View File

@ -2,21 +2,21 @@ package iris
import (
"bufio"
"encoding/json"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"os/exec"
"strings"
"sync"
"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"
versionURL = "http://iris-go.com/version"
updateCmd = "go get -u -f -v github.com/kataras/iris"
)
@ -29,28 +29,16 @@ func CheckVersion() {
})
}
type versionInfo struct {
Version string `json:"version"`
ChangelogURL string `json:"changelog_url"`
UpdateAvailable bool `json:"update_available"`
}
func checkVersion() {
client := netutil.Client(time.Duration(15 * time.Second))
r, err := client.PostForm(versionURL, url.Values{"current_version": {Version}})
// open connection and read/write timeouts
timeout := time.Duration(10 * time.Second)
transport := http.Transport{
Dial: func(network string, addr string) (net.Conn, error) {
conn, err := net.DialTimeout(network, addr, timeout)
if err != nil {
golog.Debugf("%v", err)
return nil, err
}
conn.SetDeadline(time.Now().Add(timeout)) // skip error
return conn, nil
},
}
client := http.Client{
Transport: &transport,
}
r, err := client.Get(versionURL)
if err != nil {
golog.Debugf("%v", err)
return
@ -68,53 +56,29 @@ func checkVersion() {
return
}
var (
fetchedVersion = string(b)
changelogURL string
)
// 8.2.1:https://github.com/kataras/iris/blob/master/HISTORY.md#tu-08-august-2017--v821
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 parsing latest version: %v", err)
v := new(versionInfo)
if err := json.Unmarshal(b, v); err != nil {
golog.Debugf("error while unmarshal the response body: %v", err)
return
}
currentVersion, err := version.NewVersion(Version)
if err != nil {
golog.Debugf("while parsing current version: %v", err)
if !v.UpdateAvailable {
return
}
if currentVersion.GreaterThan(latestVersion) {
golog.Debugf("current version is greater than latest version, report as bug")
return
format := "A more recent version has been found[%s > %s].\n"
if v.ChangelogURL != "" {
format += "Release notes: %s\n"
}
if currentVersion.Equal(latestVersion) {
return
}
format += "Update now?[%s]: "
// currentVersion.LessThan(latestVersion)
updaterYesInput := [...]string{"y", "yes"}
var updaterYesInput = [...]string{"y", "yes"}
text := "A more recent version has been found[%s > %s].\n"
if changelogURL != "" {
text += "Release notes: %s\n"
}
text += "Update now?[%s]: "
golog.Warnf(text,
latestVersion.String(), currentVersion.String(),
changelogURL,
golog.Warnf(format, v.Version, Version,
v.ChangelogURL,
updaterYesInput[0]+"/n")
silent := false
@ -126,6 +90,7 @@ func checkVersion() {
if !silent {
if sc.Scan() {
inputText := sc.Text()
for _, s := range updaterYesInput {
if inputText == s {
shouldUpdate = true
@ -146,6 +111,6 @@ func checkVersion() {
return
}
golog.Infof("Update process finished, current version: %s.\nManual restart is required to apply the changes.\n", latestVersion.String())
golog.Infof("Update process finished.\nManual restart is required to apply the changes.\n")
}
}