On 1/11/08, Ryan Davis ryan@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.