# Copyright 2005 Kevin Reid, under the terms of the MIT X license # found at http://www.opensource.org/licenses/mit-license.html ................ This is the second try at network access. From what I've seen, there are two kinds of Internet-protocol network APIs: 'BSD sockets', and ones with limitations. ' Therefore, I have decided to provide a nearly[1]-unaltered socket interface for E-on-CL, and add shortcuts later. If I understand correctly, a socket 'by itself' provides no communication - bind(), connect(), or sendto() must be used. Therefore, my taming plan so far is to make the sockaddr-equivalent carry the authority for connection, and creating sockets an unprivileged operation. http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_10.html#tag_02_10 [1] By "nearly", I mean that there shall be socket objects, and operations corresponding to the standard functions, etc; so the interface does not accidentally prevent one from, say, bind()ing a TCP socket. There will still be taming. ? def ALL := EIO.getALL(); null ? def makeSocket := # value: ? pragma.enable("dot-props") > def socket := makeSocket(makeSocket::internet, makeSocket::stream) # value: ? def localHTTP := getSocketPeerRef("localhost", "80") # value: ? socket.connect(def test extends localHTTP {}) # problem: the "__main$test__C" doesn't coerce to a In the ideal model, these *should* work, and they do in SBCL (XXX make it possible to test this), but many CL implementations don't offer a socket interface with true unconnected sockets. ' x ? def outs := socket.getOut() x # value: -> x x ? outs.available() x # value: 0 x x ? def ins := socket.getIn() x # value: <- x x ? ins.available() x # value: 0 ? [def connectResult := socket.connect(localHTTP), socket] # value: [, ] In order to preserve E semantics by default: - Sockets are set non-blocking on creation. - connect() is not performed within the turn. ? interp.waitAtTop(connectResult) ? connectResult # value: true ? socket # value: ? def outs := socket.getOut() # value: -> ? def ins := socket.getIn() # value: <- ? ins.available() # value: 0 ? interp.waitAtTop(outs.whenAvailable(18, thunk {})) ? outs.available() > 0 # value: true this is "GET / HTTP/1.0\r\n\r\n" ? outs.write([71, 69, 84, 32, 47, 32, 72, 84, 84, 80, 47, 49, 46, 48, 13, 10, 13, 10]) ? outs.flush() ? interp.waitAtTop(ins.whenAvailable(8, thunk { > print(ins.read(0, 17)) > })) this is "HTTP/1.1 200 OK\r\n" # stdout: [72, 84, 84, 80, 47, 49, 46, 49, 32, 50, 48, 48, 32, 79, 75, 13, 10] ? [ins.close(), outs.close()] # value: [true, true] --- Listening socket (incomplete) ? pragma.enable("dot-props") > def listener := makeSocket(makeSocket::internet, makeSocket::stream) # value: ? def local := getSocketLocalRef(null, "35037") # value: ? def loop := getSocketPeerRef("localhost", "35037") # value: XXX unspecified-port local refs - and document that they reveal a port number XXX return value is null-or-broken - test and document ? [def r := listener."bind"(local), listener] # value: [, ] ? interp.waitAtTop(r) ? [r, listener] # value: [null, ] XXX listen() and accepting XXX bind()ing of connect sockets XXX errors: name lookup failures connect failures address already bound XXX reuse-addr XXX general socket options interface XXX check that nothing exposes synchronous changes XXX consequences of getOut/getIn on a listening socket XXX listen on already listening/connected XXX bind on already bound XXX connect on already connected/listening XXX general support for null addr-info input XXX listen without bind must fail since no authority to recieve outside data has been given; support a totally-unspecific local ref to enable this XXX running out of sockets must kill the vat, since it's nondeterministic