如何使我用Golang编写的Web服务器支持HTTP/2服务器推送?

huangapple go评论89阅读模式
英文:

How to Make My Web Server written in Golang to support HTTP/2 Server Push?

问题

我的Web服务器是用Golang编写的,支持HTTPS。我希望在Web服务器中利用HTTP/2服务器推送功能。以下链接解释了如何将HTTP服务器转换为支持HTTP/2:
<https://www.ianlewis.org/en/http2-and-go>
然而,如何在Golang中实现服务器推送通知并不清楚。

  • 我应该如何添加服务器推送功能?
  • 如何控制或管理要推送的文档和文件?
英文:

My Web Server is Coded in Golang and supports HTTPS. I wish to leverage HTTP/2 Server Push features in the Web Server. The following Link explains how to convert HTTP Server to Support HTTP/2 :-
<https://www.ianlewis.org/en/http2-and-go>
However, it is not clear how to implement the Server Push notifications in Golang.

  • How should I add the Server Push functionality ?
  • How do I control, or manage, the documents and files to be Pushed ?

答案1

得分: 9

Go 1.7及更早版本的标准库不支持HTTP/2服务器推送。支持服务器推送的功能将在即将发布的1.8版本中添加(请参阅发布说明,预计发布时间为2月)。

在Go 1.8中,您可以使用新的http.Pusher接口,该接口由net/http的默认ResponseWriter实现。如果不支持服务器推送(HTTP/1)或不允许(客户端禁用了服务器推送),Pushers Push方法将返回ErrNotSupported。

示例:

package main                                                                              

import (
    "io"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/pushed", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "hello server push")
    })

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        if pusher, ok := w.(http.Pusher); ok {
            if err := pusher.Push("/pushed", nil); err != nil {
                log.Println("push failed")
            }
        }

        io.WriteString(w, "hello world")
    })

    http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)
}

如果您想在Go 1.7或更早版本中使用服务器推送,可以使用golang.org/x/net/http2并直接编写帧。

英文:

Go 1.7 and older do not support HTTP/2 server push in the standard library. Support for server push will be added in the upcoming 1.8 release (see the release notes, expected release is February).

With Go 1.8 you can use the new http.Pusher interface, which is implemented by net/http's default ResponseWriter. Pushers Push method returns ErrNotSupported, if server push is not supported (HTTP/1) or not allowed (the client has disabled server push).

Example:

package main                                                                              

import (
    &quot;io&quot;
    &quot;log&quot;
    &quot;net/http&quot;
)

func main() {
    http.HandleFunc(&quot;/pushed&quot;, func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, &quot;hello server push&quot;)
    })

    http.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {
        if pusher, ok := w.(http.Pusher); ok {
            if err := pusher.Push(&quot;/pushed&quot;, nil); err != nil {
                log.Println(&quot;push failed&quot;)
            }
        }

        io.WriteString(w, &quot;hello world&quot;)
    })

    http.ListenAndServeTLS(&quot;:443&quot;, &quot;server.crt&quot;, &quot;server.key&quot;, nil)
}

If you want to use server push with Go 1.7 or older use can use the golang.org/x/net/http2 and write the frames directly.

答案2

得分: 2

如其他答案中所提到的,你可以利用Go 1.8的特性(将写入器转换为http.Pusher,然后使用Push方法)。

但需要注意的是:你必须从服务器直接提供HTTP2流量。

如果你在NGINX等代理后面,这可能不起作用。如果你想考虑这种情况,可以利用Link头来通知要推送的URL。

// 在HTTP1.1的情况下,我们使用`Link`头来指示客户端(在我们的情况下是NGINX)应该获取某个URL。
//
// 更多信息请参见https://www.w3.org/TR/preload/#server-push-http-2。
func handleIndex(w http.ResponseWriter, r *http.Request) {
  var err error

  if *http2 {
    pusher, ok := w.(http.Pusher)
    if ok {
      must(pusher.Push("/image.svg", nil))
    }
  } else {
    // 当直接与NGINX交互时,这实际上会产生服务器推送的效果。
    w.Header().Add("Link", "</image.svg>; rel=preload; as=image")
  }

  w.Header().Add("Content-Type", "text/html")
  _, err = w.Write(assets.Index)
  must(err)
}

附注:如果你感兴趣,我在这里写了更多关于这个主题的内容:https://ops.tips/blog/nginx-http2-server-push/。

英文:

As mentioned in other answers, you can make use of Go 1.8 feature (cast the writer to http.Pusher and then use the Push method).

That comes with a caveat: you must be serving the HTTP2 traffic right from your server.

If you're behind a proxy like NGINX, this might not work. If you want to consider that scenario, you can make use of the Link header to advertise the URLs to be pushed.

// In the case of HTTP1.1 we make use of the `Link` header
// to indicate that the client (in our case, NGINX) should
// retrieve a certain URL.
//
// See more at https://www.w3.org/TR/preload/#server-push-http-2.
func handleIndex(w http.ResponseWriter, r *http.Request) {
  var err error

  if *http2 {
    pusher, ok := w.(http.Pusher)
    if ok {
      must(pusher.Push(&quot;/image.svg&quot;, nil))
    }
  } else {
    // This ends up taking the effect of a server push
    // when interacting directly with NGINX.
    w.Header().Add(&quot;Link&quot;, 
      &quot;&lt;/image.svg&gt;; rel=preload; as=image&quot;)
  }

  w.Header().Add(&quot;Content-Type&quot;, &quot;text/html&quot;)
  _, err = w.Write(assets.Index)
  must(err)
}

ps.: I wrote more about this here https://ops.tips/blog/nginx-http2-server-push/ if you're interested.

huangapple
  • 本文由 发表于 2017年1月6日 00:48:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/41490374.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定