1
0
mirror of https://github.com/labstack/echo.git synced 2025-02-15 13:53:06 +02:00

Add Go 1.16 to CI and drop 1.12 specific code (#1850)

* Correct incorrect years in CHANGELOG.md
* CI tests with last 4 versions. Remove 1.12 and below specific code
* Rename proxy test
This commit is contained in:
Martti T 2021-04-16 12:38:12 +03:00 committed by GitHub
parent bb7f2223bb
commit a4ab482b60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 156 additions and 243 deletions

View File

@ -25,7 +25,9 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
go: [1.12, 1.13, 1.14, 1.15]
# Each major Go release is supported until there are two newer major releases. https://golang.org/doc/devel/release.html#policy
# Echo tests with last four major releases
go: [1.13, 1.14, 1.15, 1.16]
name: ${{ matrix.os }} @ Go ${{ matrix.go }}
runs-on: ${{ matrix.os }}
steps:
@ -59,7 +61,7 @@ jobs:
go test -race --coverprofile=coverage.coverprofile --covermode=atomic ./...
- name: Upload coverage to Codecov
if: success() && matrix.go == 1.15 && matrix.os == 'ubuntu-latest'
if: success() && matrix.go == 1.16 && matrix.os == 'ubuntu-latest'
uses: codecov/codecov-action@v1
with:
token:
@ -69,7 +71,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
go: [1.15]
go: [1.16]
name: Benchmark comparison ${{ matrix.os }} @ Go ${{ matrix.go }}
runs-on: ${{ matrix.os }}
steps:

View File

@ -1,6 +1,6 @@
# Changelog
## v4.2.2 - 2020-04-07
## v4.2.2 - 2021-04-07
**Fixes**
@ -10,7 +10,7 @@
* Fix panic in redirect middleware on short host name (#1813)
* Fix timeout middleware docs (#1836)
## v4.2.1 - 2020-03-08
## v4.2.1 - 2021-03-08
**Important notes**
@ -32,7 +32,7 @@ A performance regression has been fixed, even bringing better performance than b
This release was made possible by our **contributors**:
aldas, clwluvw, lammel, Le0tk0k, maciej-jezierski, rkilingr, stffabi, withshubh
## v4.2.0 - 2020-02-11
## v4.2.0 - 2021-02-11
**Important notes**

View File

@ -1,28 +0,0 @@
// +build go1.13
package echo
import (
"errors"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
func TestHTTPError_Unwrap(t *testing.T) {
t.Run("non-internal", func(t *testing.T) {
err := NewHTTPError(http.StatusBadRequest, map[string]interface{}{
"code": 12,
})
assert.Nil(t, errors.Unwrap(err))
})
t.Run("internal", func(t *testing.T) {
err := NewHTTPError(http.StatusBadRequest, map[string]interface{}{
"code": 12,
})
err.SetInternal(errors.New("internal error"))
assert.Equal(t, "internal error", errors.Unwrap(err).Error())
})
}

View File

@ -957,6 +957,23 @@ func TestHTTPError(t *testing.T) {
})
}
func TestHTTPError_Unwrap(t *testing.T) {
t.Run("non-internal", func(t *testing.T) {
err := NewHTTPError(http.StatusBadRequest, map[string]interface{}{
"code": 12,
})
assert.Nil(t, errors.Unwrap(err))
})
t.Run("internal", func(t *testing.T) {
err := NewHTTPError(http.StatusBadRequest, map[string]interface{}{
"code": 12,
})
err.SetInternal(errors.New("internal error"))
assert.Equal(t, "internal error", errors.Unwrap(err).Error())
})
}
func TestDefaultHTTPErrorHandler(t *testing.T) {
e := New()
e.Debug = true

View File

@ -110,7 +110,7 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
if config.CookieMaxAge == 0 {
config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge
}
if config.CookieSameSite == SameSiteNoneMode {
if config.CookieSameSite == http.SameSiteNoneMode {
config.CookieSecure = true
}

View File

@ -1,12 +0,0 @@
// +build go1.13
package middleware
import (
"net/http"
)
const (
// SameSiteNoneMode required to be redefined for Go 1.12 support (see #1524)
SameSiteNoneMode http.SameSite = http.SameSiteNoneMode
)

View File

@ -1,12 +0,0 @@
// +build !go1.13
package middleware
import (
"net/http"
)
const (
// SameSiteNoneMode required to be redefined for Go 1.12 support (see #1524)
SameSiteNoneMode http.SameSite = 4
)

View File

@ -1,33 +0,0 @@
// +build go1.13
package middleware
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
)
// Test for SameSiteModeNone moved to separate file for Go 1.12 support
func TestCSRFWithSameSiteModeNone(t *testing.T) {
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
csrf := CSRFWithConfig(CSRFConfig{
CookieSameSite: SameSiteNoneMode,
})
h := csrf(func(c echo.Context) error {
return c.String(http.StatusOK, "test")
})
r := h(c)
assert.NoError(t, r)
assert.Regexp(t, "SameSite=None", rec.Header()["Set-Cookie"])
assert.Regexp(t, "Secure", rec.Header()["Set-Cookie"])
}

View File

@ -138,3 +138,23 @@ func TestCSRFWithSameSiteDefaultMode(t *testing.T) {
fmt.Println(rec.Header()["Set-Cookie"])
assert.NotRegexp(t, "SameSite=", rec.Header()["Set-Cookie"])
}
func TestCSRFWithSameSiteModeNone(t *testing.T) {
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
csrf := CSRFWithConfig(CSRFConfig{
CookieSameSite: http.SameSiteNoneMode,
})
h := csrf(func(c echo.Context) error {
return c.String(http.StatusOK, "test")
})
r := h(c)
assert.NoError(t, r)
assert.Regexp(t, "SameSite=None", rec.Header()["Set-Cookie"])
assert.Regexp(t, "Secure", rec.Header()["Set-Cookie"])
}

View File

@ -1,13 +1,16 @@
package middleware
import (
"context"
"fmt"
"io"
"math/rand"
"net"
"net/http"
"net/http/httputil"
"net/url"
"regexp"
"strings"
"sync"
"sync/atomic"
"time"
@ -264,3 +267,37 @@ func ProxyWithConfig(config ProxyConfig) echo.MiddlewareFunc {
}
}
}
// StatusCodeContextCanceled is a custom HTTP status code for situations
// where a client unexpectedly closed the connection to the server.
// As there is no standard error code for "client closed connection", but
// various well-known HTTP clients and server implement this HTTP code we use
// 499 too instead of the more problematic 5xx, which does not allow to detect this situation
const StatusCodeContextCanceled = 499
func proxyHTTP(tgt *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler {
proxy := httputil.NewSingleHostReverseProxy(tgt.URL)
proxy.ErrorHandler = func(resp http.ResponseWriter, req *http.Request, err error) {
desc := tgt.URL.String()
if tgt.Name != "" {
desc = fmt.Sprintf("%s(%s)", tgt.Name, tgt.URL.String())
}
// If the client canceled the request (usually by closing the connection), we can report a
// client error (4xx) instead of a server error (5xx) to correctly identify the situation.
// The Go standard library (at of late 2020) wraps the exported, standard
// context.Canceled error with unexported garbage value requiring a substring check, see
// https://github.com/golang/go/blob/6965b01ea248cabb70c3749fd218b36089a21efb/src/net/net.go#L416-L430
if err == context.Canceled || strings.Contains(err.Error(), "operation was canceled") {
httpError := echo.NewHTTPError(StatusCodeContextCanceled, fmt.Sprintf("client closed connection: %v", err))
httpError.Internal = err
c.Set("_error", httpError)
} else {
httpError := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("remote %s unreachable, could not forward: %v", desc, err))
httpError.Internal = err
c.Set("_error", httpError)
}
}
proxy.Transport = config.Transport
proxy.ModifyResponse = config.ModifyResponse
return proxy
}

View File

@ -1,47 +0,0 @@
// +build go1.11
package middleware
import (
"context"
"fmt"
"net/http"
"net/http/httputil"
"strings"
"github.com/labstack/echo/v4"
)
// StatusCodeContextCanceled is a custom HTTP status code for situations
// where a client unexpectedly closed the connection to the server.
// As there is no standard error code for "client closed connection", but
// various well-known HTTP clients and server implement this HTTP code we use
// 499 too instead of the more problematic 5xx, which does not allow to detect this situation
const StatusCodeContextCanceled = 499
func proxyHTTP(tgt *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler {
proxy := httputil.NewSingleHostReverseProxy(tgt.URL)
proxy.ErrorHandler = func(resp http.ResponseWriter, req *http.Request, err error) {
desc := tgt.URL.String()
if tgt.Name != "" {
desc = fmt.Sprintf("%s(%s)", tgt.Name, tgt.URL.String())
}
// If the client canceled the request (usually by closing the connection), we can report a
// client error (4xx) instead of a server error (5xx) to correctly identify the situation.
// The Go standard library (at of late 2020) wraps the exported, standard
// context.Canceled error with unexported garbage value requiring a substring check, see
// https://github.com/golang/go/blob/6965b01ea248cabb70c3749fd218b36089a21efb/src/net/net.go#L416-L430
if err == context.Canceled || strings.Contains(err.Error(), "operation was canceled") {
httpError := echo.NewHTTPError(StatusCodeContextCanceled, fmt.Sprintf("client closed connection: %v", err))
httpError.Internal = err
c.Set("_error", httpError)
} else {
httpError := echo.NewHTTPError(http.StatusBadGateway, fmt.Sprintf("remote %s unreachable, could not forward: %v", desc, err))
httpError.Internal = err
c.Set("_error", httpError)
}
}
proxy.Transport = config.Transport
proxy.ModifyResponse = config.ModifyResponse
return proxy
}

View File

@ -1,14 +0,0 @@
// +build !go1.11
package middleware
import (
"net/http"
"net/http/httputil"
"github.com/labstack/echo/v4"
)
func proxyHTTP(t *ProxyTarget, c echo.Context, config ProxyConfig) http.Handler {
return httputil.NewSingleHostReverseProxy(t.URL)
}

View File

@ -1,86 +0,0 @@
// +build go1.11
package middleware
import (
"context"
"net/http"
"net/http/httptest"
"net/url"
"sync"
"testing"
"time"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
)
func TestProxy_1_11(t *testing.T) {
// Setup
url1, _ := url.Parse("http://127.0.0.1:27121")
url2, _ := url.Parse("http://127.0.0.1:27122")
targets := []*ProxyTarget{
{
Name: "target 1",
URL: url1,
},
{
Name: "target 2",
URL: url2,
},
}
rb := NewRandomBalancer(nil)
// must add targets:
for _, target := range targets {
assert.True(t, rb.AddTarget(target))
}
// must ignore duplicates:
for _, target := range targets {
assert.False(t, rb.AddTarget(target))
}
// Random
e := echo.New()
e.Use(Proxy(rb))
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
// Remote unreachable
rec = httptest.NewRecorder()
req.URL.Path = "/api/users"
e.ServeHTTP(rec, req)
assert.Equal(t, "/api/users", req.URL.Path)
assert.Equal(t, http.StatusBadGateway, rec.Code)
}
func TestClientCancelConnectionResultsHTTPCode499(t *testing.T) {
var timeoutStop sync.WaitGroup
timeoutStop.Add(1)
HTTPTarget := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
timeoutStop.Wait() // wait until we have canceled the request
w.WriteHeader(http.StatusOK)
}))
defer HTTPTarget.Close()
targetURL, _ := url.Parse(HTTPTarget.URL)
target := &ProxyTarget{
Name: "target",
URL: targetURL,
}
rb := NewRandomBalancer(nil)
assert.True(t, rb.AddTarget(target))
e := echo.New()
e.Use(Proxy(rb))
rec := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/", nil)
ctx, cancel := context.WithCancel(req.Context())
req = req.WithContext(ctx)
go func() {
time.Sleep(10 * time.Millisecond)
cancel()
}()
e.ServeHTTP(rec, req)
timeoutStop.Done()
assert.Equal(t, 499, rec.Code)
}

View File

@ -2,6 +2,7 @@ package middleware
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"net"
@ -9,7 +10,9 @@ import (
"net/http/httptest"
"net/url"
"regexp"
"sync"
"testing"
"time"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
@ -302,3 +305,73 @@ func TestProxyRewriteRegex(t *testing.T) {
})
}
}
func TestProxyError(t *testing.T) {
// Setup
url1, _ := url.Parse("http://127.0.0.1:27121")
url2, _ := url.Parse("http://127.0.0.1:27122")
targets := []*ProxyTarget{
{
Name: "target 1",
URL: url1,
},
{
Name: "target 2",
URL: url2,
},
}
rb := NewRandomBalancer(nil)
// must add targets:
for _, target := range targets {
assert.True(t, rb.AddTarget(target))
}
// must ignore duplicates:
for _, target := range targets {
assert.False(t, rb.AddTarget(target))
}
// Random
e := echo.New()
e.Use(Proxy(rb))
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
// Remote unreachable
rec = httptest.NewRecorder()
req.URL.Path = "/api/users"
e.ServeHTTP(rec, req)
assert.Equal(t, "/api/users", req.URL.Path)
assert.Equal(t, http.StatusBadGateway, rec.Code)
}
func TestClientCancelConnectionResultsHTTPCode499(t *testing.T) {
var timeoutStop sync.WaitGroup
timeoutStop.Add(1)
HTTPTarget := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
timeoutStop.Wait() // wait until we have canceled the request
w.WriteHeader(http.StatusOK)
}))
defer HTTPTarget.Close()
targetURL, _ := url.Parse(HTTPTarget.URL)
target := &ProxyTarget{
Name: "target",
URL: targetURL,
}
rb := NewRandomBalancer(nil)
assert.True(t, rb.AddTarget(target))
e := echo.New()
e.Use(Proxy(rb))
rec := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/", nil)
ctx, cancel := context.WithCancel(req.Context())
req = req.WithContext(ctx)
go func() {
time.Sleep(10 * time.Millisecond)
cancel()
}()
e.ServeHTTP(rec, req)
timeoutStop.Done()
assert.Equal(t, 499, rec.Code)
}

View File

@ -1,5 +1,3 @@
// +build go1.13
package middleware
import (

View File

@ -1,5 +1,3 @@
// +build go1.13
package middleware
import (