How to use unix domain socket to make a request when using Gin?

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

How to use unix domain socket to make a request when using Gin?

问题

我正在为firecracker创建一个包装器。

要在命令行上使用firecracker启动虚拟机,你需要将一个socket文件传递给firecracker可执行文件。类似这样:

  1. firecracker --api-sock /path/to/file.socket

然后在另一个终端中,你可以像这样向该服务器/套接字发送请求:

  1. curl --unix-socket /tmp/firecracker.socket -XPUT 'http://localhost/actions' -d '{"action_type": "SendCtrlAltDel"}'

我正在尝试在Gin服务器内部复制相同的操作。

我有一个端点用于执行第一步操作,即启动服务器。一个简化的代码如下:

  1. cmd := exec.Command("firecracker", "--api-sock", "/path/to/file.socket")
  2. err := cmd.Start()

这个端点启动服务器并监听任何命令。问题是,我不知道如何使用socket文件向该服务器发送PUT请求。我在网上找到了这个,但对我来说不太明白。

下面是一个不使用任何socket文件的起始代码。

  1. func BootSource(c *gin.Context) {
  2. var body models.BootSource
  3. c.BindJSON(&body)
  4. bodyJson, _ := json.Marshal(body)
  5. // 初始化http客户端
  6. client := &http.Client{}
  7. // 设置HTTP方法、URL和请求体
  8. req, err := http.NewRequest(http.MethodPut, "http://localhost/boot-source", bytes.NewBuffer(bodyJson))
  9. if err != nil {
  10. panic(err)
  11. }
  12. // 设置请求头的Content-Type为json
  13. _, err = client.Do(req)
  14. if err != nil {
  15. panic(err)
  16. }
  17. }

我该如何使这个PUT请求使用socket文件?

请注意,我正在使用Gin框架。

英文:

I am creating a wrapper for firecracker.

To start a VM with firecracker on command line, you have to pass a socket file to the firecracker executable. Something like this:

  1. firecracker --api-sock /path/to/file.socket

Then from another terminal, you can make requests to this server/socket something like this:

  1. curl --unix-socket /tmp/firecracker.socket -XPUT 'http://localhost/actions' -d '{"action_type": "SendCtrlAltDel"}'

I am trying to replicate the same thing from within a Gin server.

I have an endpoint which does the first work, which is to start a server. A minimal code looks like this:

  1. cmd := exec.Command("firecracker", "--api-sock", "/path/to/file.socket")
  2. err := cmd.Start()

This endpoint starts the server and listens for any command. The problem is, I don't know how to use the socket file to make a PUT request to this server. I have found this on the web, but it does not makes much sense to me.

Here is a starter code which does not use any socket file.

  1. func BootSource(c *gin.Context) {
  2. var body models.BootSource
  3. c.BindJSON(&body)
  4. bodyJson, _ := json.Marshal(body)
  5. // initialize http client
  6. client := &http.Client{}
  7. // set the HTTP method, url, and request body
  8. req, err := http.NewRequest(http.MethodPut, "http://localhost/boot-source", bytes.NewBuffer(bodyJson))
  9. if err != nil {
  10. panic(err)
  11. }
  12. // set the request header Content-Type for json
  13. _, err = client.Do(req)
  14. if err != nil {
  15. panic(err)
  16. }
  17. }

How do I make this PUT request use the socket file?

Please also note that I'm using Gin framework.

答案1

得分: 2

要实现这个功能,你需要重写http.Client使用的传输方式,以配置一个函数来创建与套接字的连接:

  1. client := http.Client{
  2. Transport: &http.Transport{
  3. DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
  4. return net.DialContext(ctx, "unix", "/path/to/socket")
  5. }
  6. }
  7. }

然后你可以使用这个客户端,它发送的所有请求都将使用该连接。通常,对于通过套接字公开的HTTP服务,请求中使用的主机并不重要,所以你可以使用任何对你有意义的值,例如:

  1. client.Get("http://firecracker/some/api/path")

然而,由于你正在尝试使用Firecracker API,为什么不直接使用他们的SDK:https://github.com/firecracker-microvm/firecracker-go-sdk

这将为你处理连接的设置,避免你手动构建所有的请求。

英文:

To do this, you'll need to override the Transport used by your http.Client to configure a function for it to use to create a connection to the socket:

  1. client := http.Client{
  2. Transport: &http.Transport{
  3. DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
  4. return net.DialContext(ctx, "unix", "/path/to/socket")
  5. }
  6. }
  7. }

You can then use that client, and all requests made by it will use that connection. Usually for HTTP services exposed over a socket, the host used in the request is not important, so you can just use any value that makes sense to you e.g

  1. client.Get("http://firecracker/some/api/path")

However, as you are trying to use the Firecracker API, why not just use their SDK: https://github.com/firecracker-microvm/firecracker-go-sdk

This will handle the set up of the connection for you, and prevent you needing to manually craft all of the requests.

答案2

得分: 1

扩展到这个答案,你可以通过克隆默认传输来保留http默认设置

  1. defaultTransport, ok := http.DefaultTransport.(*http.Transport)
  2. if !ok {
  3. panic("http.DefaultTransport is not a *http.Transport")
  4. }
  5. unixTransport := defaultTransport.Clone()
  6. defaultDialContext := unixTransport.DialContext
  7. unixTransport.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
  8. return defaultDialContext(ctx, "unix", "/path/to/socket")
  9. }
  10. client := http.Client{Transport: unixTransport}
  11. client.Get("http://example.com")
英文:

Extending to this answer, you can keep the http defaults by cloning default transport

  1. defaultTransport, ok := http.DefaultTransport.(*http.Transport)
  2. if !ok {
  3. panic("http.DefaultTransport is not a *http.Transport")
  4. }
  5. unixTransport := defaultTransport.Clone()
  6. defaultDialContext := unixTransport.DialContext
  7. unixTransport.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
  8. return defaultDialContext(ctx, "unix", "/path/to/socket")
  9. }
  10. client := http.Client{Transport: unixTransport}
  11. client.Get("http://example.com")

huangapple
  • 本文由 发表于 2022年7月20日 16:17:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/73048137.html
匿名

发表评论

匿名网友

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

确定