Repository: labstack/echo
Pull request #1489 (2020-01-27 07:14:54)

Support HTTP/2 h2c mode (cleartext)
I've added a function "StartH2cServer" for HTTP/2 with h2c mode.
I'd like to run a HTTP/2 echo server on a secure private network, so I don't need a TLS.
However, echo requires a TLS to run a HTTP/2 server, so I've added the function using a h2c library.
Please review my small changes.

codecov[bot]: # [Codecov](https://codecov.io/gh/labstack/echo/pull/1489?src=pr&el=h1) Report
> Merging [#1489](https://codecov.io/gh/labstack/echo/pull/1489?src=pr&el=desc) into [master](https://codecov.io/gh/labstack/echo/commit/712b5e46c539096fd204535e61c568e455f4c604?src=pr&el=desc) will **decrease** coverage by `0.06%`.
> The diff coverage is `76.47%`.

[![Impacted file tree graph](https://codecov.io/gh/labstack/echo/pull/1489/graphs/tree.svg?width=650&token=vqnT76l79i&height=150&src=pr)](https://codecov.io/gh/labstack/echo/pull/1489?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1489      +/-   ##
==========================================
- Coverage   84.35%   84.28%   -0.07%     
==========================================
  Files          27       27              
  Lines        2077     2094      +17     
==========================================
+ Hits         1752     1765      +13     
- Misses        212      214       +2     
- Partials      113      115       +2
```


| [Impacted Files](https://codecov.io/gh/labstack/echo/pull/1489?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [echo.go](https://codecov.io/gh/labstack/echo/pull/1489/diff?src=pr&el=tree#diff-ZWNoby5nbw==) | `84.87% <76.47%> (-0.47%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/labstack/echo/pull/1489?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/labstack/echo/pull/1489?src=pr&el=footer). Last update [712b5e4...31d6d78](https://codecov.io/gh/labstack/echo/pull/1489?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

ochanism: > * Can you call it `StartH2CServer `?
Ok

> * Do you mind updating the docs on echox project
Ok I'm going to update it soon.

ochanism: I've changed the function name and I've made a pull request for a document.
Please refer to https://github.com/labstack/echox/pull/132

Patch:
diff --git a/echo.go b/echo.go
index 3869dc6..ed65888 100644
--- a/echo.go
+++ b/echo.go
@@ -59,6 +59,8 @@ import (
 	"github.com/labstack/gommon/log"
 	"golang.org/x/crypto/acme"
 	"golang.org/x/crypto/acme/autocert"
+	"golang.org/x/net/http2"
+	"golang.org/x/net/http2/h2c"
 )
 
 type (
@@ -723,6 +725,34 @@ func (e *Echo) StartServer(s *http.Server) (err error) {
 	return s.Serve(e.TLSListener)
 }
 
+// StartH2CServer starts a custom http/2 server with h2c (HTTP/2 Cleartext).
+func (e *Echo) StartH2CServer(address string, h2s *http2.Server) (err error) {
+	// Setup
+	s := e.Server
+	s.Addr = address
+	e.colorer.SetOutput(e.Logger.Output())
+	s.ErrorLog = e.StdLogger
+	s.Handler = h2c.NewHandler(e, h2s)
+	if e.Debug {
+		e.Logger.SetLevel(log.DEBUG)
+	}
+
+	if !e.HideBanner {
+		e.colorer.Printf(banner, e.colorer.Red("v"+Version), e.colorer.Blue(website))
+	}
+
+	if e.Listener == nil {
+		e.Listener, err = newListener(s.Addr)
+		if err != nil {
+			return err
+		}
+	}
+	if !e.HidePort {
+		e.colorer.Printf("⇨ http server started on %s\n", e.colorer.Green(e.Listener.Addr()))
+	}
+	return s.Serve(e.Listener)
+}
+
 // Close immediately stops the server.
 // It internally calls `http.Server#Close()`.
 func (e *Echo) Close() error {
diff --git a/go.mod b/go.mod
index eacaf4b..17e57fe 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/stretchr/testify v1.4.0
 	github.com/valyala/fasttemplate v1.1.0
 	golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876
-	golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
+	golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa
 	golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 // indirect
 	golang.org/x/text v0.3.2 // indirect
 )
diff --git a/go.sum b/go.sum
index e329def..08ef80e 100644
--- a/go.sum
+++ b/go.sum
@@ -29,8 +29,8 @@ golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90
 golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=


Test patch:
diff --git a/echo_test.go b/echo_test.go
index 3f2e48e..68c556f 100644
--- a/echo_test.go
+++ b/echo_test.go
@@ -14,6 +14,7 @@ import (
 
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+	"golang.org/x/net/http2"
 )
 
 type (
@@ -512,6 +513,17 @@ func TestEchoStartAutoTLS(t *testing.T) {
 	}
 }
 
+func TestEchoStartH2CServer(t *testing.T) {
+	e := New()
+	e.Debug = true
+	h2s := &http2.Server{}
+
+	go func() {
+		assert.NoError(t, e.StartH2CServer(":0", h2s))
+	}()
+	time.Sleep(200 * time.Millisecond)
+}
+
 func testMethod(t *testing.T, method, path string, e *Echo) {
 	p := reflect.ValueOf(path)
 	h := reflect.ValueOf(func(c Context) error {