smlnj / smlnj

Standard ML of New Jersey
BSD 3-Clause "New" or "Revised" License
184 stars 14 forks source link

`SockUtil.recvVec` behaviour does not match documentation #292

Open brabes opened 4 weeks ago

brabes commented 4 weeks ago

Version

2024.2 (latest)

Operating System

OS Version

Presumably any.

Processor

System Component

SML/NJ Library

Severity

Minor

Description

The documentation for SockUtil.recvVec says:

recvVec (sock, n) reads n bytes from the stream socket sock; fewer than n bytes is returned when the stream is closed at the other end of the connection. It raises the Size exception when n is negative.

However if fewer than n bytes are available it raises SysErr. There is a comment alluding to this on line 97 of smlnj-lib/INet/sock-util.sml saying (** If the server closes the connection, do we get 0 bytes or an error??? **).

The basis function Socket.recvVec works as expected.

Transcript

uncaught exception SysErr [SysErr: closed socket]

Expected Behavior

val it = ...

Steps to Reproduce

fun myRecvStr(sock, len) =
  let
    val v = Socket.recvVec(sock, len)
  in
    Byte.bytesToString v
  end

fun getHttp () =
  let
    val {addr=addr,host=_,port=_} = SockUtil.resolveAddr (valOf (SockUtil.addrFromString "mlton.org"))
    val s = SockUtil.connectINetStrm({addr=addr, port=80})
    val _ = SockUtil.sendStr(s, "HEAD / HTTP/1.1\r\nHost: mlton.org\r\nConnection: close\r\n\r\n")
  in
    SockUtil.recvStr(s, 8192)
    (* works if you replace the above line with this *)
    (*myRecvStr(s, 8192)*)
  end

val () = print (getHttp ())

N.B. SockUtil.recvStr uses SockUtil.recvVec internally.

Additional Information

No response

Email address

brabes@protonmail.com

brabes commented 3 weeks ago

I think it’s the other way round, Socket.recvVec from basis does work as expected but SockUtil.recvVec from SML-NJ library does not.

The example could, perhaps, have been clearer:

fun getHttp () =
  let
    val {addr=addr,host=_,port=_} = SockUtil.resolveAddr (valOf (SockUtil.addrFromString "mlton.org"))
    val s = SockUtil.connectINetStrm({addr=addr, port=80})
    val _ = SockUtil.sendStr(s, "HEAD / HTTP/1.1\r\nHost: mlton.org\r\nConnection: close\r\n\r\n")
  in
    SockUtil.recvVec(s, 8192) (* <- throws SysError *)
    (* Socket.recvVec(s, 8192) *) (* <- this works as expected, returning a Word8Vector with length 382 *)
  end

I tried Socket.recvVec on PolyML and it works the same as on SML/NJ, that is it returns a short vector as expected. I would have expected SockUtil.recvVec to work in the same way, but it does not.

Would an acceptable solution be to just redefine SockUtil.recvVec as follows?

fun recvVec (sock, n) = Socket.recvVec (sock, n)