/*  -
   Copyright (C) 2006 Weongyo Jeong (weongyo@gmail.com)

This file is part of ROVM.

ROVM is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.

ROVM is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */

#include "types.h"
#include "mpool.h"
#include "tktree.h"
#include "clstree.h"
#include "sha1.h"
#include "listen.h"
#include "rovm.h"

#include "thread.h"
#include "thread_mutex.h"
#include "thread_cond.h"

#include "mpm_worker_fdqueue.h"
#include "mpm_worker_pod.h"
#include "mpm_worker.h"

#include "connection.h"
#include "request.h"
#include "log.h"
#include "utils.h"

/**
   연결된 connection 을 이제 진진하게? 처리를 한다.

   @param c     연결된 connection 정보를 가지는 conn_rec 구조체
   @param sock  연결된 socket
 */
int
rc_process_connection (c)
     conn_rec *c;
{
  if (rc_process_request (c))
    return -1;

  return 0;
}

/**
   연결 당 하나의 connection 을 생성한다.

   @param ts    서버의 여러 정보와 ROVM 구조체를 가지는 변수
   @param p     이 연결에 사용할 메모리 pool
   @param sock  현재 연결에 사용되고 있는 socket
*/
conn_rec *
rc_create_connection (ts, p, sock)
     thread_starter *ts;
     rc_pool_t *p;
     rc_socket_t *sock;
{
  rc_status_t rv;
  conn_rec *c = (conn_rec *) mp_calloc (p, sizeof(conn_rec));
  
  /*
    Got a connection structure, so initialize what fields we can
     (the rest are zeroed out by mp_calloc).
  */
  c->ts = ts;
  c->sock = sock;
  c->notes = rc_table_make (p, 5);
  c->pool = p;

  if ((rv = rc_socket_addr_get (&c->local_addr, RC_LOCAL, sock)) != RC_SUCCESS) 
    {
      rovm_log (NULL, ROVMLOG_INFO, ROVMLOG_MARK,
                "rc_socket_addr_get(RC_LOCAL)");
      rc_socket_close (sock);
      return NULL;
    }
  
  rc_sockaddr_ip_get (&c->local_ip, c->local_addr);

  if ((rv = rc_socket_addr_get (&c->remote_addr, RC_REMOTE, sock)) != RC_SUCCESS) 
    {
      rovm_log (NULL, ROVMLOG_INFO, ROVMLOG_MARK,
                "rc_socket_addr_get (RC_REMOTE)");
      rc_socket_close (sock);
      return NULL;
    }

  rc_sockaddr_ip_get (&c->remote_ip, c->remote_addr);

  return c;  
}

/*
 * More machine-dependent networking gooo... on some systems,
 * you've got to be *really* sure that all the packets are acknowledged
 * before closing the connection, since the client will not be able
 * to see the last response if their TCP buffer is flushed by a RST
 * packet from us, which is what the server's TCP stack will send
 * if it receives any request data after closing the connection.
 *
 * In an ideal world, this function would be accomplished by simply
 * setting the socket option SO_LINGER and handling it within the
 * server's TCP stack while the process continues on to the next request.
 * Unfortunately, it seems that most (if not all) operating systems
 * block the server process on close() when SO_LINGER is used.
 * For those that don't, see USE_SO_LINGER below.  For the rest,
 * we have created a home-brew lingering_close.
 *
 * Many operating systems tend to block, puke, or otherwise mishandle
 * calls to shutdown only half of the connection.  You should define
 * NO_LINGCLOSE in ap_config.h if such is the case for your system.
 */
#ifndef MAX_SECS_TO_LINGER
#define MAX_SECS_TO_LINGER 30
#endif

#define SECONDS_TO_LINGER  2

/**
   현재 connection 을 닫는다.  메모리 해제는 연결이 끝날 경우, 자동적으로 되므로
   걱정할 필요는 없다.

   @param c     닫을 conn_rec 구조체.
 */
void
rc_close_connection (c)
     conn_rec *c;
{
  char dummybuf[512];
  rc_size_t nbytes;
  rc_time_t timeup = 0;

  /* Shut down the socket for write, which will send a FIN
     to the peer.  */
  if (rc_socket_shutdown (c->sock, RC_SHUTDOWN_WRITE) != APR_SUCCESS) 
    {
      rc_socket_close (c->sock);
      return;
    }

  /* Read available data from the client whilst it continues sending
     it, for a maximum time of MAX_SECS_TO_LINGER.  If the client
     does not send any data within 2 seconds (a value pulled from
     Apache 1.3 which seems to work well), give up.  */
  rc_socket_timeout_set (c->sock, rc_time_from_sec (SECONDS_TO_LINGER));
  rc_socket_opt_set (c->sock, RC_INCOMPLETE_READ, 1);
  
  /* The common path here is that the initial apr_socket_recv() call
     will return 0 bytes read; so that case must avoid the expensive
     apr_time_now() call and time arithmetic. */
  do 
    {
      nbytes = sizeof (dummybuf);
      if (rc_socket_recv (c->sock, dummybuf, &nbytes) || nbytes == 0)
        break;

      if (timeup == 0) 
        {
          /* First time through; calculate now + 30 seconds. */
          timeup = rc_time_now () + rc_time_from_sec (MAX_SECS_TO_LINGER);
          continue;
        }
    } 
  while (rc_time_now () < timeup);
  
  apr_socket_close (c->sock);
  return;
}
