[usocket-devel] socket-connect raises non-obvious conditions when given a bad host
Erik Huelsmann
ehuels at gmail.com
Sat Jan 12 22:12:41 UTC 2008
On 1/11/08, Ryan Davis <ryan at acceleration.net> wrote:
> When I try to use socket-connect with a host that can't be resolved, it
> throws an unhelpful error:
>
> Couldn't write to #<SB-SYS:FD-STREAM for "a socket" {C0286C1}>:
> Broken pipe
> [Condition of type SB-INT:SIMPLE-STREAM-ERROR]
>
> Restarts:
> 0: [ABORT] Return to SLIME's top level.
> 1: [TERMINATE-THREAD] Terminate this thread (#<THREAD "repl-thread"
> {B35FB79}>)
>
> Backtrace:
> 0: (SB-IMPL::SIMPLE-STREAM-PERROR "Couldn't write to ~s"
> #<SB-SYS:FD-STREAM for "a socket" {C0286C1}> 32)
> 1: (SB-IMPL::SIMPLE-STREAM-PERROR "Couldn't write to ~s"
> #<SB-SYS:FD-STREAM for "a socket" {C0286C1}> 32)
> 2: (FORCE-OUTPUT #<SB-SYS:FD-STREAM for "a socket" {C0286C1}>)
> 3: (USOCKET-BAD-ERROR)
>
> Here is my test program:
>
> (defun usocket-bad-error ()
> (let ((s (usocket:socket-stream
> (usocket:socket-connect "doesntexist.example.com" 80))))
> (format s "GET / HTTP/1.1")
> (force-output s);;error thrown here
> s))
>
> Looking through the usocket code, what I would have liked here is a
> bad-host-name condition thrown that I could handle. I initially thought
> a good spot for this would be in socket-connect, if the ip variable is
> nil, but upon looking at the usocket source so more, I'm not sure if
> there's a better place for that.
To find where the better place is, we need to find out why the error
raised by sb-bsd-sockets (HOST-NOT-FOUND) isn't leaking through to the
caller. It took me a while to figure that out. Here's the deal:
usocket translates all errors and signals it gets from the lower
level. In this case it receives the host-not-found error. But, as it
turns out, even though the 'error' function is used to notify callers,
HOST-NOT-FOUND is actually a CONDITION. And that's where things go
wrong: as soon as usocket sees a condition, it SIGNALs it. But (as you
may know), there's a large difference between a SIGNAL and an ERROR
call: if there's no handler-bind or handler-case which claims the
signalled condition, (signal ...) returns NIL, whereas the (error ...)
call enters the debugger.
Your code example doesn't have a usocket:unknown-condition handler
installed, which means the signal thrown by usocket returns NIL, which
becomes the return value of usocket::get-random-host-by-name, which
becomes the return value of host-to-vector-quad...
Now, after re-reading much of the CLHS on the condition system as well
as the chapter of Practical Common Lisp, I think I was wrong to ERROR
errors and SIGNAL conditions. Rather, I think I should have been using
ERROR in all cases. I'm still wondering whether I should be using
restarts anywhere in the library. Probably not, since the
computational effort required to get to, say, host name resolution in
socket-connect is negligible.
Anyway, the above means that:
1) You found an error that's not in the translation table, which it
should have been (a bug/incompleteness)
2) I'll be switching from signal to error for all conditions (and thus
not only errors anymore)
Thanks for your report! I hope you'll be sending more as you're
encountering problems.
bye,
Erik.
More information about the usocket-devel
mailing list