如何在HttpHandler(Julia语言)中获取客户端IP地址?

时间:2014-07-02 09:50:26

标签: http julia httpserver

我需要在常规HttpHandler中获取客户端的IP地址,如下所示:

http = HttpHandler() do req::Request, res::Response
    Response( ismatch(r"^/hello/",req.resource) ? string("Hello ", split(req.resource,'/')[3], "!") : 404 )
end

reqhttp.sock都不包含此信息。

5 个答案:

答案 0 :(得分:5)

方法

如果你稍微了解朱莉娅的内部结构,就可以做到这一点。事实证明,Julia使用库 libuv 进行低级系统处理,并且该库具有一个名为 uv_tcp_getpeername 的函数。 Julia.Base不会导出此函数,但您可以通过ccall访问它。此外,模块 HttpServer 允许为各种事件定义回调,包括 connect 事件。

示例模块

module HTTPUtil
   export get_server
   using HttpServer

   function handle_connect(client)
      try
         buffer = Array(Uint8,32)
         bufflen::Int64 = 32

         ccall(:uv_tcp_getpeername,Int64,(Ptr{Void},Ptr{Uint8},Ptr{Int64}),client.sock.handle,buffer,&bufflen)

         peername::IPv4 = IPv4(buffer[5:8]...)
         task_local_storage(:ip,peername)
      catch e
         println("Error ... $e")
      end
   end

   function get_server()
      http = HttpHandler() do req::Request, res::Response
         ip = task_local_storage(:ip)
         println("Connection received from from $ip")
         Response(ismatch(r"^/hello/",req.resource)?string("Hello ",split(req.resource,'/')[3], " $(ip)!") : 404 )
      end

      http.events["connect"]=(client)->handle_connect(client)
      server = Server(http)
   end
end

解释

每次发出连接请求时,服务器都会创建一个对等套接字,并调用 connect 处理程序,该处理程序被定义为 handle_connect 。它需要一个类型Client的客户端参数。 客户端 类型的字段名为 sock ,类型为TcpSocket TcpSocket 有一个字段句柄,由 libuv 使用。然后,对象是每次发出连接请求时,都会调用连接处理程序,该处理程序使用 TcpSocket 中包含的数据调用 uv_tcp_getpeername 处理。声明一个字节数组充当缓冲区,然后将其转换回Base.IPv4。模块HTTPServer准确创建1 task for each client using @async,因此可以使用 task_local_storage 将IP地址存储在客户端本地;因此没有竞争条件。

使用它

julia> using HTTPUtil
julia> server = get_server()
Server(HttpHandler((anonymous function),TcpServer(init),Dict{ASCIIString,Function} with 3 entries:
  "error" => (anonymous function)
  "listen" => (anonymous function)
  "connect" => (anonymous function)),nothing)

julia> @async run(server,8000)
Listening on 8000...
Task (queued) @0x000000000767e7a0

julia> Connection received from from 192.168.0.23
Connection received from from 192.168.0.22
... etc

注释

  1. 为了说明,输出被修改,以便服务器响应每个浏览器" Hello ipaddr "
  2. 这应该包含在Base和/或HttpServer中,但目前不是,因此您需要使用此解决方法,直到它为止。
  3. get_server 中使用典型的循环结构来说明除了添加IP地址之外不需要更改它。
  4. 假设使用IPv4,但可以改进以便直接允许IPv4和IPv6,因为 libuv 支持这两者。

答案 1 :(得分:1)

在waTeim的优秀answer的基础上,我简化了一些与IPv6兼容的事情,以及SSL连接:

using MbedTLS

function handle_connect(client)
    ip, port = getsockname(isa(client.sock, MbedTLS.SSLContext) ? client.sock.bio : client.sock)
    task_local_storage(:ip, ip)
end

答案 2 :(得分:0)

(本来是对Josh Bode的回答添加了这个评论,但我没有必要的声誉。)

请注意,自Julia v0.7起,有必要使用getpeername()代替getsockname()

https://github.com/JuliaLang/julia/pull/21825

答案 3 :(得分:0)

感谢waTeim的回答,但是从2014年开始,朱莉娅的情况发生了变化。这在Julia 6.0中很有效,可能还有以上几点:

function ip(socket::TCPSocket) buffer = Array{UInt8}(32) bufflen::Int64 = 32 ccall(:uv_tcp_getpeername,Int64,(Ptr{Void},Ptr{UInt8},Ptr{Int64}), socket.handle, buffer, &bufflen) peername::IPv4 = IPv4(buffer[5:8]...) end

答案 4 :(得分:0)

根据您的情况,您可以传递linux命令。

userIP = strip(readstring(`hostname -i`), ['\n'])