Just some notes! See src/tools/dflx_web.flx!

Step 1: Set up server listening socket on PORT.

var PORT=1234;                        // port to listen on
val qlen = 10;                               // length of queue
var listener: Socket::socket_t;   // server socket

Socket::mk_listener(&listener, &PORT, qlen);

Step 2:  Listen and spawn

forever {
  var s:Socket:: socket_t;           // socket bound to incoming connection
  Socket::accept(listener, &s);  // blocking this fibre (mainline)
  var h = handler s;                     // create handler procedure for socket
  spawn_fthread  h;                   // spawn a fibre to handle the connection
};

Step 3: Write the handler

noinline proc handler (var k:Socket::socket_t) ()
{
    var line: string;
    get_line(k, &line);  // should be the GET line.
   .....

get_line is mysterious!

src/lib/std/io/stream.flx:  

  noinline proc get_line[istr with IByteStream[istr]] (

and we have:

  proc write_string[ostr with OByteStream[ostr]] (

and in src/lib/std/socket.flx:

  instance Stream::IByteStream[socket_t]
  instance Stream::OByteStream[socket_t]

So .. sockets provide I/O streams of bytes.

Now the hard bit. Yes, you get a rant! Sockets are CRAP.
There you go. There's no good way to close a socket.
Serious design stupidity.

Here's the best I could come up with:


////////////////////////////////////////////////////////////

  Faio::sleep(clock,DELAY); // give OS time to empty its buffers
  fprint$ cstderr,"fthread socket "+str k+" shutdown now\n";

// try this:
// Advised by: koettermar...@gmx.de, MANY THANKS!

  gen hack_recv: socket_t * &char * int * int -> int = "recv($1,$2,$3,$4)";

  var buf:char ^1025;
  var counter = 0;
  var extra = 0;
  shutdown(k,1); // shutdown read
retry:>
  var b = hack_recv(k,C_hack::cast[&char] (&buf),1024,0);
  //println$ "Error code " + str b + " from read after shutdown";
  if b > 0 do
    extra += b;
    if extra > 2000 do
      println$ "Read too many extraneous bytes from OS buffer";
      goto force_close;
     done;
   goto retry;
  elif b == -1 do
    ++counter;
    if counter > 200 do
      println "Timeout waiting for write buffers to be flushed";
      goto force_close;
    done;
    Faio::sleep(clock,0.1); // 100 ms
    goto retry;
  done;
  assert b==0;

force_close:> 
  Socket::shutdown(k,2);
  ioclose(k);
  fprint$ cstderr,"fthread "+str k+" terminating!\n";


The basic problem is this. If you just close an async socket the OS is allowed
by Posix to simply flush all its buffers. So stuff you write won't go
to the client. Indeed Linux really DOES do this stupidity.

For synchronous sockets, there is "hang" parameter which makes the close
call hang about for a period of time, and return an error if the buffers didn't
get flushed, or no error if they are. But this option doesn't work with 
asynchronous
sockets because close always returns immediately.

What's more shutdown is a load of crud as well. In fact the whole idea of a 
bidirectional socket is wrong.

What you have to do is shutdown the read side of the socket,
then loop around reading until you get 0 bytes. Two other things
can happen: you get >0 bytes read, which you ignore (because you're
trying to shut down the socket, you don't care what the client is asking
for!) Or you get an error code -1, meaning, there are no bytes to read,
but it isn't EOF yet either! In that case you sleep a bit and try again.

When you get 0 bytes, that's a "for sure the client has shutdown their
end of the connection", and its safe to close our end now, even if there
are bytes in the OS write buffer, because the client isn't going to read them.
So we shutdown our write end then close the socket.

The "hack_recv" you see above is a low level asynchronous read operation.
Unfortunately we have to therefore sleep. We must not wait about because 
that would block the pthread. So we sleep which only blocks the fibre.

The problem with this crap is that the fibre and socket remain alive for 100ms,
even if the buffers were empty 1ms after the sleep starts. This means the server
has too many sockets open: more than it needs. Which exposes it to a DNS attack.

Note Felix doesn't have proper I/O that is safe. If you read from a socket,
you will wait until you get what you ask for (or an error). There is no
timed wait. There should be!

Generally there are two way to DNS attack a server.

1. FLOOD IT.  The server tries to read a file and it gets a lot
more than it bargained for. Can be thwarted by a limit.

2. STARVE IT. The server tries to read a file and gets nothing.
The client just sends one byte and stops, or sends very slowly. 
Can be thwarted by a rate monitor.

Even with these thwarts, which disconnect unfriendly clients,
you have to wait a while to establish the client is unfriendly.
So you are wasting resources. So in both cases a DNS attack
can be created by the simple expedient of requesting enough
unfriendly connections that the server chokes.

There is no way around these issues but the problem is that
the way of closing a socket as shown above is just crap.
Async close should just return and the OS should do the 
work above. The close should specify the delay the OS is
allowed to wait to write its buffers: this is exactly what happens
with a synchronous close after all. The parameter controlling
this just doesn't work for asynchronous sockets.

Sockets kill the stream I/O concepts in Felix. The ioclose function
closes the socket, but you have to do the crap above first.
There's no way to put that in the ioclose because ioclose is
an iclose and an oclose.  And note above we actually read
off the socket AFTER closing the read side! Shutdown tells
the client to stop writing, but the client doesn't have to obey!
The client also doesn't have to read the stuff we wrote
out of the OS buffers. So we really cannot wait too long
for cooperation.

--
john skaller
skal...@users.sourceforge.net
http://felix-lang.org




------------------------------------------------------------------------------
CenturyLink Cloud: The Leader in Enterprise Cloud Services.
Learn Why More Businesses Are Choosing CenturyLink Cloud For
Critical Workloads, Development Environments & Everything In Between.
Get a Quote or Start a Free Trial Today.
http://pubads.g.doubleclick.net/gampad/clk?id=119420431&iu=/4140/ostg.clktrk
_______________________________________________
Felix-language mailing list
Felix-language@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to