/* 
   File:  read_data.c 

   Description:  function to read data from a client or the server
*/

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "proxy_protos.h"

extern int errno;


void
read_data ( struct kevent *ke, struct mdata *md )
{
  qdata             *q;
  int                r;
  int                from;
  int                pn;
  int                fd;
  float              et;
  struct kevent      sk;
  struct sockaddr_in sa;
  socklen_t          sl;
  char              *fr[2] = { "CLIENT", "SERVER" };

  /* 
     if this is the first data we're reading over this connection,
     record the start time. 
  */

  if ( md->r[ke->ident].bytes_read == 0 ) {
    gettimeofday(&md->r[ke->ident].ti_read,NULL);
  }

  /* are we reading from the server or a client? */

  from = md->r[ke->ident].type;

  /* what's the relay file descriptor? */
  
  r = md->r[ke->ident].relay;

  /* 
     if we haven't read any data yet, and the server file descriptor
     is still on status FD_CLOSE, we must be waiting for the connect()
     to the server to finish.  in order to find out if that connect()
     actually did finish, let's call getpeername() and see if it works.
     if it fails with errno = ENOTCONN, we're not connected yet.
  */

  fd = (from == CLIENT) ? r : ke->ident;

  if ( md->r[ke->ident].bytes_read == 0 &&
       md->r[fd].status == FD_CLOSE ) {
    
    sl = sizeof(sa);
    pn = getpeername(fd,(struct sockaddr *)&sa,&sl);
    gettimeofday(&md->r[fd].tf_connect,NULL);      

    if ( pn == -1 && errno == ENOTCONN ) {
      
      /* we're still not connected.  check how much time has passed */

      et = diff_timeval(&md->r[fd].ti_connect,&md->r[fd].tf_connect);

      if ( et > CONNECT_TIMEOUT ) {

	/* we've been waiting too long.  kill everything. */

	log_msg(md,"Connect timeout exceeded on [fd %d]: %.3f seconds",fd,et);
	close_connection(md,ke->ident);
	close_connection(md,r);

      }

      /* go back to the event loop */

      return;

    } else {
      
      /* we're connected.  set the relay FD status to FD_OPEN */

      md->r[fd].status = FD_OPEN;
      log_msg(md,"Connect succeeded eventually on [fd %d]: "
	      "checked after %.6f seconds",
	      fd,diff_timeval(&md->r[fd].ti_connect,
			      &md->r[fd].tf_connect));
      
    }

  }


  /* read from the file descriptor into a character buffer */

  q    = g_chunk_new(qdata,md->m);

  /* check to see if we got the chunk OK */

  if ( q == NULL ) {

    /* 
       oops.  no memory left.  set a flag so we don't try and 
       accept any more connections until the situation eases. 
    */

    log_msg(md,"g_chunk_new failed (out of memory)");
    md->out_of_memory = 1;
    return;

  } else if ( md->out_of_memory ) {

    /* 
       great, we've got some memory back, so we can start accepting
       connections again (hopefully). 
    */
    
    md->out_of_memory = 0;
  }

  q->l = read(ke->ident,q->s,QUEUE_BUFFER);
  q->p = 0;

  if ( q->l > 0 ) {

    /* record the time */

    gettimeofday(&md->r[ke->ident].tf_read,NULL);

    if ( md->verbose ) {
      log_msg(md,"[fd %d] read from %s [%d] [%d]",  
	      ke->ident, fr[from], ke->data, q->l);
    }

    /* if data was actually read, record it. */

    md->bytes_read[from]        += q->l;
    md->r[ke->ident].bytes_read += q->l;
  }

  /* is the relay connection still open?  */
  
  if ( md->r[r].status == FD_OPEN ) {
    
    /* 
       yes, it is.  append the data we just read to the relay queue
       if the read was successful. 
    */

    if ( q->l >= 0 ) {

      md->r[r].queue = g_slist_append(md->r[r].queue,q);
      
      /* prepare to add a kevent for writing this data to the relay. */
      
      sk.ident  = md->r[ke->ident].relay;
      sk.filter = EVFILT_WRITE;
      sk.flags  = EV_ADD | EV_ONESHOT;
      sk.udata  = write_data;
      
      /* add the relay write event */
      
      if ( kevent(md->kq,&sk,1,NULL,0,NULL) == -1 ) {
	perror("kevent");
	md->kevent_errors++;
      }

    } else {

      /* read returned an error */

      g_mem_chunk_free(md->m,q);

    }

  } else {

    log_msg(md,"read_data from %s [%d]: relay to %s [%d] closed", 
	    fr[from], ke->ident, fr[!from], r);
    close_connection(md,ke->ident);
    
    /* empty and free the relay queue. */
    
    if ( md->r[r].queue ) {
      g_slist_foreach(md->r[r].queue,chunk_free,md->m);
      g_slist_free(md->r[r].queue);
      md->r[r].queue = NULL;
    }
    
  }

  /* 
     if the other end has closed the connection to us, and we
     finished reading all the data from that end, we can 
     explicitly close that file descriptor and thereby remove
     any further events on it. 
  */
  
  if ( ke->flags & EV_EOF && 
       md->r[ke->ident].status == FD_OPEN &&
       ke->data == q->l ) {
    log_msg(md,"read_data from %s [%d] received EV_EOF: closing %d", 
	    fr[from], ke->ident, ke->ident);
    close_connection(md,ke->ident);
  }
  
}




syntax highlighted by Code2HTML, v. 0.9.1