httpx icon indicating copy to clipboard operation
httpx copied to clipboard

IPv6 failed

Open jerrygzy opened this issue 1 year ago • 2 comments

import options, asyncdispatch
import httpx, net

proc onRequest(req: Request): Future[void] {.async.} =
  # Log the incoming request
  echo "Received request: ", req.httpMethod.get(), " ", req.path

  if req.httpMethod == some(HttpGet):
    case req.path.get()
    of "/":
      req.send("Hello World")
      echo "Sent response: Hello World"
    else:
      req.send(Http404)
      echo "Sent response: 404 Not Found"

let settings = Settings(
  port: Port(80),
  bindAddr: "::",  # IPv6 address
  listener: newSocket(AF_INET6, SOCK_STREAM, IPPROTO_IPV6)
)

# Start the server and log that it's running
echo "Starting server on port ", settings.port
run(onRequest, settings)
echo "Server is running"

error

/Users/of/Work/Nim/web_test/ssr_test/src/serverv6.nim(22) serverv6
/Users/of/.choosenim/toolchains/nim-2.2.0/lib/pure/net.nim(309) newSocket
/Users/of/.choosenim/toolchains/nim-2.2.0/lib/std/oserrors.nim(92) raiseOSError
Error: unhandled exception: Bad file descriptor [OSError]

jerrygzy avatar Oct 26 '24 07:10 jerrygzy

Yes, that's a problem. Is it an httpx problem?

Here's a much simpler version:

import net

let l = newSocket(AF_INET6, SOCK_STREAM, IPPROTO_IPV6)

which gives me:

/home/infinoid/workspace/forms/nim/test2.nim(3) test2
/home/infinoid/.choosenim/toolchains/nim-2.2.0/lib/pure/net.nim(309) newSocket
/home/infinoid/.choosenim/toolchains/nim-2.2.0/lib/std/oserrors.nim(92) raiseOSError

Same error, no httpx.

(note: I'm not an httpx developer, I'm just having a similar problem and found this issue. But I don't think this one is httpx's fault.)

Infinoid avatar Feb 22 '25 16:02 Infinoid

Here's what's wrong with your program:

  1. you probably meant to open a TCP socket with an IPv6 address , not a (raw) IPv6 socket. The third argument should be IPPROTO_TCP.
  2. there's a few more calls needed to set up a socket as a TCP listener. httpx does that here, but since you provided your own socket, you need to do it yourself. At the very least it will need bind and listen calls.

The linux tcp(7) manpage says:

To receive new incoming connections, first bind(2) the socket to a local address and port and then call listen(2) to put the socket into the listening state.

And the nim docs do the same.

Here's a corrected example:

import options, asyncdispatch
import httpx, net

proc onRequest(req: Request): Future[void] {.async.} =
  # Log the incoming request
  echo "Received request: ", req.httpMethod.get(), " ", req.path

  if req.httpMethod == some(HttpGet):
    case req.path.get()
    of "/":
      req.send("Hello World")
      echo "Sent response: Hello World"
    else:
      req.send(Http404)
      echo "Sent response: 404 Not Found"

let settings = Settings(
  port: Port(8888),
  bindAddr: "::", # IPv6 address
  listener: newSocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)
)

settings.listener.setSockOpt(OptReuseAddr, true)
settings.listener.setSockOpt(OptReusePort, true)
settings.listener.bindAddr(settings.port, settings.bindAddr)
settings.listener.listen()

echo "Starting server on port ", settings.port
run(onRequest, settings)
echo "Server is running"

And here's some curl verbose output confirming that it connected over ipv6:

% curl -v6 http://localhost:8888/
* Host localhost:8888 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
*   Trying [::1]:8888...
* Connected to localhost (::1) port 8888
* using HTTP/1.x
> GET / HTTP/1.1
> Host: localhost:8888
> User-Agent: curl/8.12.1-DEV
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 200
< Content-Length: 11
< Server: Nim-HTTPX
< Date: Sat, 22 Feb 2025 17:09:16 GMT
< 
* Connection #0 to host localhost left intact
Hello World%

So that's working!

That said, it would be great if httpx could create the right kind of socket based on the address given.

Infinoid avatar Feb 22 '25 17:02 Infinoid