当前位置: 首页 > 面试题库 >

访问net / http响应的基础套接字

邢乐
2023-03-14
问题内容

我是Go的新手,正在为一个项目进行评估。

我正在尝试编写一个自定义处理程序来为提供文件net/http。我无法使用默认http.FileServer()处理程序,因为我需要访问基础套接字(内部net.Conn),因此可以在其上执行一些信息平台特定的“
syscall”调用(主要是TCP_INFO)。

更确切地说:我需要http.ResponseWriter在处理程序函数中访问的基础套接字:

func myHandler(w http.ResponseWriter, r *http.Request) {
...
// I need the net.Conn of w
...
}

用在

http.HandleFunc("/", myHandler)

有没有办法做到这一点。我看了看这是怎么websocket.Upgrade做的,但是它使用了Hijack()“太多”的内容,因为然后我必须在获得的原始tcp套接字上编码“说HTTP”。我只想引用套接字,而不是完全接管。


问题答案:

在问题#30694完成之后,Go
1.13可能将支持在请求上下文中存储net.Conn,这使它相当干净和简单:

package main

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

type contextKey struct {
  key string
}
var ConnContextKey = &contextKey{"http-conn"}
func SaveConnInContext(ctx context.Context, c net.Conn) (context.Context) {
  return context.WithValue(ctx, ConnContextKey, c)
}
func GetConn(r *http.Request) (net.Conn) {
  return r.Context().Value(ConnContextKey).(net.Conn)
}

func main() {
  http.HandleFunc("/", myHandler)

  server := http.Server{
    Addr: ":8080",
    ConnContext: SaveConnInContext,
  }
  server.ListenAndServe()
}

func myHandler(w http.ResponseWriter, r *http.Request) {
  conn := GetConn(r)
  ...
}

直到那时…对于在TCP端口上侦听的服务器,net.Conn.RemoteAddr()。String()对于每个连接都是唯一的,并且可以作为r.RemoteAddr在http.Handler中使用,因此可以用作康恩斯全球地图的关键:

package main
import (
  "net/http"
  "net"
  "fmt"
  "log"
)

var conns = make(map[string]net.Conn)
func ConnStateEvent(conn net.Conn, event http.ConnState) {
  if event == http.StateActive {
    conns[conn.RemoteAddr().String()] = conn
  } else if event == http.StateHijacked || event == http.StateClosed {
    delete(conns, conn.RemoteAddr().String())
  }
}
func GetConn(r *http.Request) (net.Conn) {
  return conns[r.RemoteAddr]
}

func main() {
  http.HandleFunc("/", myHandler)

  server := http.Server{
    Addr: ":8080",
    ConnState: ConnStateEvent,
  }
  server.ListenAndServe()
}

func myHandler(w http.ResponseWriter, r *http.Request) {
  conn := GetConn(r)
  ...
}

对于在UNIX套接字上侦听的服务器,net.Conn.RemoteAddr()。String()始终为“
@”,因此上述操作无效。为此,我们可以覆盖net.Listener.Accept(),并使用它覆盖net.Conn.RemoteAddr()。String(),以便为每个连接返回唯一的字符串:

package main

import (
  "net/http"
  "net"
  "os"
  "golang.org/x/sys/unix"
  "fmt"
  "log"
)

func main() {
  http.HandleFunc("/", myHandler)

  listenPath := "/var/run/go_server.sock"
  l, err := NewUnixListener(listenPath)
  if err != nil {
    log.Fatal(err)
  }
  defer os.Remove(listenPath)

  server := http.Server{
    ConnState: ConnStateEvent,
  }
  server.Serve(NewConnSaveListener(l))
}

func myHandler(w http.ResponseWriter, r *http.Request) {
  conn := GetConn(r)
  if unixConn, isUnix := conn.(*net.UnixConn); isUnix {
    f, _ := unixConn.File()
    pcred, _ := unix.GetsockoptUcred(int(f.Fd()), unix.SOL_SOCKET, unix.SO_PEERCRED)
    f.Close()
    log.Printf("Remote UID: %d", pcred.Uid)
  }
}

var conns = make(map[string]net.Conn)
type connSaveListener struct {
  net.Listener
}
func NewConnSaveListener(wrap net.Listener) (net.Listener) {
  return connSaveListener{wrap}
}
func (self connSaveListener) Accept() (net.Conn, error) {
  conn, err := self.Listener.Accept()
  ptrStr := fmt.Sprintf("%d", &conn)
  conns[ptrStr] = conn
  return remoteAddrPtrConn{conn, ptrStr}, err
}
func GetConn(r *http.Request) (net.Conn) {
  return conns[r.RemoteAddr]
}
func ConnStateEvent(conn net.Conn, event http.ConnState) {
  if event == http.StateHijacked || event == http.StateClosed {
    delete(conns, conn.RemoteAddr().String())
  }
}
type remoteAddrPtrConn struct {
  net.Conn
  ptrStr string
}
func (self remoteAddrPtrConn) RemoteAddr() (net.Addr) {
  return remoteAddrPtr{self.ptrStr}
}
type remoteAddrPtr struct {
  ptrStr string
}
func (remoteAddrPtr) Network() (string) {
  return ""
}
func (self remoteAddrPtr) String() (string) {
  return self.ptrStr
}

func NewUnixListener(path string) (net.Listener, error) {
  if err := unix.Unlink(path); err != nil && !os.IsNotExist(err) {
    return nil, err
  }
  mask := unix.Umask(0777)
  defer unix.Umask(mask)

  l, err := net.Listen("unix", path)
  if err != nil {
    return nil, err
  }

  if err := os.Chmod(path, 0660); err != nil {
    l.Close()
    return nil, err
  }

  return l, nil
}


 类似资料:
  • HTTP控制器 URL解析规则 权限验证 Request对象 Response对象 错误与异常拦截 自定义路由 常见问题 使用模板引擎

  • 如何获取$HTTP_RAW_POST_DATA $content = $this->request()->getBody()->__toString(); $raw_array = json_decode($content, true); 如何获取客户端IP 举例,如何在控制器中获取客户端IP //真实地址 $ip = ServerManager::getInstance()->getServer

  • 我想从socket读取图像响应,并再次将其传递给另一个socket。但现在我连图像反应都看不懂。

  • HTTP控制器 控制器决定了一个请求进来,应该如何被处理,控制器即是应用程序的心脏,一个控制器就是一个类文件,当请求进来,定位到控制器方法,就会执行其中的代码 控制器的命名和文件路径 控制器文件以及类的命名遵循大驼峰法(CamelCase),统一存放于 App\HttpController 目录下,让我们举一个例子,来感受一下控制器文件、类、与访问的URL之间互相的联系,假设有一个后台添加用户的操

  • 我想在java中对API的HTTP响应实现重试框架。 如果回答是: 400:将json中的参数设为null,然后重试 202:返回成功 429:请等待2分钟,然后重试 5XX:等待5分钟,然后重试 如果重试次数超过,则抛出异常。是否有任何可用的库支持重试响应类型并允许编辑请求对象?如果没有,我怎么能设计一个?有没有围绕它的教程?

  • 生命周期 Response对象在系统中以单例模式存在,自收到客户端HTTP请求时自动创建,直至请求结束自动销毁。Response对象完全符合PSR7中的所有规范。 其他细节方法,有兴趣的同学可以在IDE中查看对应的代码。 方法列表 write 该方法用于向客户响应数据。 $response->write('hello world'); redirect 该方法用于将请求重定向至指定的URL $re