description
HTTP2与HTTP3的支持

绝大部分的浏览器都已支持http2,后端支持http2仅需要更新nginx版本,调整配置则可。golang的http模块已支持http2的处理,下面主要介绍如何使用Elton支持http2。

生成证书

浏览器只支持通过https方式使用http2,为了开发方便,可以生成开发环境使用的证书,mkcert仅需几条命令则可生成多域名证书。

  1. // 生成证书
  2. mkcert me.dev localhost 127.0.0.1 ::1
  3. // 安装证书
  4. mkcert -install

启动服务

因为golang本身http模块已支持http2,则只需要以https的方式启动服务则可。

  1. package main
  2. import (
  3. "bytes"
  4. "github.com/vicanso/elton"
  5. )
  6. func main() {
  7. e := elton.New()
  8. e.GET("/", func(c *elton.Context) error {
  9. c.BodyBuffer = bytes.NewBufferString("Hello, World!")
  10. return nil
  11. })
  12. certFile := "/tmp/me.dev+3.pem"
  13. keyFile := "/tmp/me.dev+3-key.pem"
  14. err := e.ListenAndServeTLS(":3000", certFile, keyFile)
  15. if err != nil {
  16. panic(err)
  17. }
  18. }

上面例子中证书是以文件的形式保存,实际使用时证书都统一存储,加密访问(如保存在数据库等),下面的例子讲解如果使用[]byte来初始化TLS:

  1. package main
  2. import (
  3. "bytes"
  4. "crypto/tls"
  5. "io/ioutil"
  6. "github.com/vicanso/elton"
  7. )
  8. // 获取证书内容
  9. func getCert() (cert []byte, key []byte, err error) {
  10. // 此处仅简单从文件中读取,在实际使用,是从数据库中读取
  11. cert, err = ioutil.ReadFile("/tmp/me.dev+3.pem")
  12. if err != nil {
  13. return
  14. }
  15. key, err = ioutil.ReadFile("/tmp/me.dev+3-key.pem")
  16. if err != nil {
  17. return
  18. }
  19. return
  20. }
  21. func main() {
  22. e := elton.New()
  23. cert, key, err := getCert()
  24. if err != nil {
  25. panic(err)
  26. }
  27. // 先初始化tls配置,生成证书
  28. tlsConfig := &tls.Config{}
  29. tlsConfig.Certificates = make([]tls.Certificate, 1)
  30. tlsConfig.Certificates[0], err = tls.X509KeyPair(cert, key)
  31. if err != nil {
  32. panic(err)
  33. }
  34. e.Server.TLSConfig = tlsConfig
  35. e.GET("/", func(c *elton.Context) error {
  36. c.BodyBuffer = bytes.NewBufferString("hello world!")
  37. return nil
  38. })
  39. err = e.ListenAndServeTLS(":3000", "", "")
  40. if err != nil {
  41. panic(err)
  42. }
  43. }

h2c

golang默认的HTTP2需要在以https的方式提供,对于内部系统间的调用,如果希望以http的方式使用http2,那么可以考虑h2c的处理。下面的代码示例包括了服务端与客户端怎么使用以http的方式使用http2。

  1. package main
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "net"
  6. "net/http"
  7. "time"
  8. "github.com/vicanso/elton"
  9. "github.com/vicanso/elton/middleware"
  10. "golang.org/x/net/http2"
  11. "golang.org/x/net/http2/h2c"
  12. )
  13. var http2Client = &http.Client{
  14. // 强制使用http2
  15. Transport: &http2.Transport{
  16. // 允许使用http的方式
  17. AllowHTTP: true,
  18. // tls的dial覆盖
  19. DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
  20. return net.Dial(network, addr)
  21. },
  22. },
  23. }
  24. func main() {
  25. go func() {
  26. time.Sleep(time.Second)
  27. resp, err := http2Client.Get("http://127.0.0.1:3000/")
  28. if err != nil {
  29. panic(err)
  30. }
  31. fmt.Println(resp.Proto)
  32. }()
  33. e := elton.New()
  34. e.Use(middleware.NewDefaultResponder())
  35. e.GET("/", func(c *elton.Context) error {
  36. c.Body = "Hello, World!"
  37. return nil
  38. })
  39. // http1与http2均支持
  40. e.Server = &http.Server{
  41. Handler: h2c.NewHandler(e, &http2.Server{}),
  42. }
  43. err := e.ListenAndServe(":3000")
  44. if err != nil {
  45. panic(err)
  46. }
  47. }
  1. curl -v --http2-prior-knowledge http://127.0.0.1:3000/
  2. * Trying 127.0.0.1...
  3. * TCP_NODELAY set
  4. * Connected to 127.0.0.1 (127.0.0.1) port 3000 (#0)
  5. * Using HTTP2, server supports multi-use
  6. * Connection state changed (HTTP/2 confirmed)
  7. * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
  8. * Using Stream ID: 1 (easy handle 0x7f8d9a804600)
  9. > GET / HTTP/2
  10. > Host: 127.0.0.1:3000
  11. > User-Agent: curl/7.54.0
  12. > Accept: */*
  13. >
  14. * Connection state changed (MAX_CONCURRENT_STREAMS updated)!
  15. < HTTP/2 200

http3

http3现在支持的浏览器只有chrome canary以及firefox最新版本,golang的http模块也未支持http3,http3的前景虽然并不确定,但是还是可以先尝尝鲜,需要注意http3还是试验性质,不要在正式环境中大规模使用。下面是使用quic-go使用http3的示例:

  1. package main
  2. import (
  3. "bytes"
  4. "crypto/tls"
  5. "io/ioutil"
  6. "log"
  7. "net/http"
  8. "time"
  9. "github.com/lucas-clemente/quic-go/http3"
  10. "github.com/vicanso/elton"
  11. )
  12. const listenAddr = ":4000"
  13. // 获取证书内容
  14. func getCert() (cert []byte, key []byte, err error) {
  15. // 此处仅简单从文件中读取,在实际使用,是从数据库中读取
  16. cert, err = ioutil.ReadFile("/tmp/me.dev+3.pem")
  17. if err != nil {
  18. return
  19. }
  20. key, err = ioutil.ReadFile("/tmp/me.dev+3-key.pem")
  21. if err != nil {
  22. return
  23. }
  24. return
  25. }
  26. func http3Get() {
  27. client := &http.Client{
  28. Transport: &http3.RoundTripper{},
  29. }
  30. resp, err := client.Get("https://127.0.0.1" + listenAddr + "/")
  31. if err != nil {
  32. log.Fatalln("http3 get fail ", err)
  33. return
  34. }
  35. log.Println("http3 get success", resp.Proto, resp.Status, resp.Header)
  36. }
  37. func main() {
  38. // 延时一秒后以http3的访问访问
  39. go func() {
  40. time.Sleep(time.Second)
  41. http3Get()
  42. }()
  43. e := elton.New()
  44. cert, key, err := getCert()
  45. if err != nil {
  46. panic(err)
  47. }
  48. // 先初始化tls配置,生成证书
  49. tlsConfig := &tls.Config{}
  50. tlsConfig.Certificates = make([]tls.Certificate, 1)
  51. tlsConfig.Certificates[0], err = tls.X509KeyPair(cert, key)
  52. if err != nil {
  53. panic(err)
  54. }
  55. e.Server.TLSConfig = tlsConfig.Clone()
  56. // 初始化http3服务
  57. http3Server := http3.Server{
  58. Server: &http.Server{
  59. Handler: e,
  60. Addr: listenAddr,
  61. },
  62. }
  63. http3Server.TLSConfig = tlsConfig.Clone()
  64. e.Use(func(c *elton.Context) error {
  65. http3Server.SetQuicHeaders(c.Header())
  66. return c.Next()
  67. })
  68. e.GET("/", func(c *elton.Context) error {
  69. c.BodyBuffer = bytes.NewBufferString("hello " + c.Request.Proto + "!")
  70. return nil
  71. })
  72. go func() {
  73. // http3
  74. err := http3Server.ListenAndServe()
  75. if err != nil {
  76. panic(err)
  77. }
  78. }()
  79. // https
  80. err = e.ListenAndServeTLS(listenAddr, "", "")
  81. if err != nil {
  82. panic(err)
  83. }
  84. }

curl可以自己编译支持http3的版本,编译方法可参考curl http3,下面是使用curl的调用示例:

  1. curl3 --http3 'https://me.dev:4000/' -v
  2. * Trying 127.0.0.1:4000...
  3. * Sent QUIC client Initial, ALPN: h3-24h3-23
  4. * h3 [:method: GET]
  5. * h3 [:path: /]
  6. * h3 [:scheme: https]
  7. * h3 [:authority: me.dev:4000]
  8. * h3 [user-agent: curl/7.68.0-DEV]
  9. * h3 [accept: */*]
  10. * Using HTTP/3 Stream ID: 0 (easy handle 0x7fad4f011e00)
  11. > GET / HTTP/3
  12. > Host: me.dev:4000
  13. > user-agent: curl/7.68.0-DEV
  14. > accept: */*
  15. >
  16. < HTTP/3 200
  17. < content-length: 13
  18. < alt-srv: h3-24=":4000"; ma=86400
  19. <
  20. * Connection #0 to host me.dev left intact
  21. hello HTTP/3!