wwwapi.cpp

来自「FastDb是高效的内存数据库系统」· C++ 代码 · 共 1,062 行 · 第 1/2 页

CPP
1,062
字号
//-< WWWAPI.CPP >----------------------------------------------------*--------*
// FastDB                    Version 1.0         (c) 1999  GARRET    *     ?  *
// (Main Memory Database Management System)                          *   /\|  *
//                                                                   *  /  \  *
//                          Created:     27-Mar-99    K.A. Knizhnik  * / [] \ *
//                          Last update:  1-Jul-99    K.A. Knizhnik  * GARRET *
//-------------------------------------------------------------------*--------*
// Implementation of WWWapi class
//-------------------------------------------------------------------*--------*

#define INSIDE_FASTDB

#include "wwwapi.h"
#include <ctype.h>

const size_t init_reply_buffer_size = 4*1024;

inline unsigned string_hash_function(const char* name)
{
  unsigned h = 0, g;

  while(*name)
  {
    h = (h << 4) + *name++;

    if ((g = h & 0xF0000000) != 0)
    {
      h ^= g >> 24;
    }

    h &= ~g;
  }

  return h;
}

#define ERROR_TEXT(x) \
"HTTP/1.1 " x "\r\n\
Connection: close\r\n\r\n\
<HTML><HEAD><TITLE>Invalid request to the database</TITLE>\r\n\
</HEAD><BODY>\n\r\
<H1>" x "</H1>\n\r\
</BODY></HTML>\r\n\r\n"

WWWconnection::WWWconnection()
{
  memset(hash_table, 0, sizeof hash_table);
  sock = NULL;
  reply_buf = new char[init_reply_buffer_size];
  reply_buf_size = init_reply_buffer_size;
  free_pairs = NULL;
  peer = NULL;
  userData = NULL;
  userDataDestructor = NULL;
}

WWWconnection::~WWWconnection()
{
  reset();
  name_value_pair *nvp, *next;

  for (nvp = free_pairs; nvp != NULL; nvp = next)
  {
    next = nvp->next;
    delete nvp;
  }

  delete[] reply_buf;
  delete[] peer;

  if (userDataDestructor != NULL && userData != NULL)
  {
    userDataDestructor(userData);
  }
}


inline char* WWWconnection::extendBuffer(size_t inc)
{
  if (reply_buf_used + inc >= reply_buf_size)
  {
    reply_buf_size = reply_buf_size*2 > reply_buf_used + inc
                     ? reply_buf_size*2 : reply_buf_used + inc;
    char* new_buf = new char[reply_buf_size+1];
    memcpy(new_buf, reply_buf, reply_buf_used);
    delete[] reply_buf;
    reply_buf = new_buf;
  }

  reply_buf_used += inc;
  return reply_buf;
}

bool WWWconnection::terminatedBy(char const* str) const
{
  size_t len = strlen(str);

  if (len > reply_buf_used - 4)
  {
    return false;
  }

  return memcmp(reply_buf + reply_buf_used - len, str, len) == 0;
}

WWWconnection& WWWconnection::append(const void *buf, int len)
{
  int pos = reply_buf_used;
  char *dst = extendBuffer(len);
  memcpy(dst + pos, buf, len);
  return *this;
}

WWWconnection& WWWconnection::append(char const* str)
{
  int pos = reply_buf_used;
  char* dst = extendBuffer(strlen(str));
  unsigned char ch;

  switch (encoding)
  {

  case TAG:
    strcpy(dst + pos, str);
    encoding = HTML;
    break;

  case HTML:
    encoding = TAG;
#if 1 // MS-Explorer handle "&nbsp;" in HTML string literals in very strange way

    if (str[0] == ' ' && str[1] == '\0')
    {
      strcpy(extendBuffer(5) + pos, "&nbsp;");
      return *this;
    }

#endif
    while (true)
    {
      switch(ch = *str++)
      {

      case '<':
        dst = extendBuffer(3);
        dst[pos++] = '&';
        dst[pos++] = 'l';
        dst[pos++] = 't';
        dst[pos++] = ';';
        break;

      case '>':
        dst = extendBuffer(3);
        dst[pos++] = '&';
        dst[pos++] = 'g';
        dst[pos++] = 't';
        dst[pos++] = ';';
        break;

      case '&':
        dst = extendBuffer(4);
        dst[pos++] = '&';
        dst[pos++] = 'a';
        dst[pos++] = 'm';
        dst[pos++] = 'p';
        dst[pos++] = ';';
        break;

      case '"':
        dst = extendBuffer(5);
        dst[pos++] = '&';
        dst[pos++] = 'q';
        dst[pos++] = 'u';
        dst[pos++] = 'o';
        dst[pos++] = 't';
        dst[pos++] = ';';
        break;

      case '\0':
        dst[pos] = '\0';
        return *this;
#if 0 // MS-Explorer handle "&nbsp;" in HTML string literals in very strange way

      case ' ':
        dst = extendBuffer(5);
        dst[pos++] = '&';
        dst[pos++] = 'n';
        dst[pos++] = 'b';
        dst[pos++] = 's';
        dst[pos++] = 'p';
        dst[pos++] = ';';
        break;
#endif

      default:
        dst[pos++] = ch;
      }
    }

  case URL:
    encoding = TAG;

    while (true)
    {
      ch = *str++;

      if (ch == '\0')
      {
        dst[pos] = '\0';
        return *this;
      }
      else if (ch == ' ')
      {
        dst[pos++] = '+';
      }
      else if (!isalnum(ch))
      {
        dst = extendBuffer(2);
        dst[pos++] = '%';
        dst[pos++] = (ch >> 4) >= 10
                     ? (ch >> 4) + 'A' - 10 : (ch >> 4) + '0';
        dst[pos++] = (ch & 0xF) >= 10
                     ? (ch & 0xF) + 'A' - 10 : (ch & 0xF) + '0';
      }
      else
      {
        dst[pos++] = ch;
      }
    }
  }

  return *this;
}


void WWWconnection::reset()
{
  reply_buf_used = 0;
  encoding = TAG;

  for (int i = itemsof(hash_table); --i >= 0;)
  {
    name_value_pair *nvp, *next;

    for (nvp = hash_table[i]; nvp != NULL; nvp = next)
    {
      next = nvp->next;
      nvp->next = free_pairs;
      free_pairs = nvp;
    }

    hash_table[i] = NULL;
  }
}

void WWWconnection::addPair(char const* name, char const* value)
{
  name_value_pair* nvp;

  if (free_pairs != NULL)
  {
    nvp = free_pairs;
    free_pairs = nvp->next;
  }
  else
  {
    nvp = new name_value_pair;
  }

  unsigned hash_code = string_hash_function(name);
  nvp->hash_code = hash_code;
  hash_code %= hash_table_size;
  nvp->next = hash_table[hash_code];
  hash_table[hash_code] = nvp;
  nvp->value = value;
  nvp->name = name;
}

#define HEX_DIGIT(ch) ((ch) >= 'a' ? ((ch) - 'a' + 10) : (ch) >= 'A' ? ((ch) - 'A' + 10) : ((ch) - '0'))

char* WWWconnection::unpack(char* body, size_t length)
{
  char *src = body, *end = body + length;

  while (src < end)
  {
    char* name = src;
    char ch;
    char* dst = src;

    while (src < end && (ch = *src++) != '=')
    {
      if (ch == '+')
      {
        ch = ' ';
      }
      else if (ch == '%')
      {
        ch = (HEX_DIGIT(src[0]) << 4) | HEX_DIGIT(src[1]);
        src += 2;
      }

      *dst++ = ch;
    }

    *dst = '\0';
    char* value = dst = src;

    while (src < end && (ch = *src++) != '&')
    {
      if (ch == '+')
      {
        ch = ' ';
      }
      else if (ch == '%')
      {
        ch = (HEX_DIGIT(src[0]) << 4) | HEX_DIGIT(src[1]);
        src += 2;
      }

      *dst++ = ch;
    }

    *dst = '\0';
    addPair(name, value);
  }

  stub = get
           ("stub");

  return get
           ("page");
}


char* WWWconnection::get
  (char const* name, int n)
{
  unsigned hash_code = string_hash_function(name);
  name_value_pair* nvp;

  for (nvp = hash_table[hash_code % hash_table_size];
       nvp != NULL;
       nvp = nvp->next)
  {
    if (nvp->hash_code == hash_code && strcmp(nvp->name, name) == 0)
    {
      if (n == 0)
      {
        return (char*)nvp->value;
      }

      n -= 1;
    }
  }

  return NULL;
}




//--------------------------------------------------


WWWapi::WWWapi(dbDatabase& dbase, int n_handlers, dispatcher* dispatch_table)
    : db(dbase)
{
  memset(hash_table, 0, sizeof hash_table);
  sock = NULL;
  address = NULL;
  dispatcher* disp = dispatch_table;

  while (--n_handlers >= 0)
  {
    unsigned hash_code = string_hash_function(disp->page);
    disp->hash_code = hash_code;
    hash_code %= hash_table_size;
    disp->collision_chain = hash_table[hash_code];
    hash_table[hash_code] = disp;
    disp += 1;
  }
}

bool WWWapi::open(char const* socket_address,
                  socket_t::socket_domain domain,
                  int listen_queue)
{
  if (sock != NULL)
  {
    close();
  }

  address = new char[strlen(socket_address) + 1];
  strcpy(address, socket_address);
  sock = domain != socket_t::sock_global_domain
         ? socket_t::create_local(socket_address, listen_queue)
         : socket_t::create_global(socket_address, listen_queue);
  canceled = false;

  if (!sock->is_ok())
  {
    char buf[64];
    sock->get_error_text(buf, sizeof buf);
    fprintf(stderr, "WWWapi::open: create socket failed: %s\n", buf);
    return false;
  }

  return true;
}




bool WWWapi::connect(WWWconnection& con)
{
  assert(sock != NULL);
  con.reset();
  delete con.sock;
  con.sock = sock->accept();
  con.address = address;

  if (con.sock == NULL)
  {
    if (!canceled)
    {
      char buf[64];
      sock->get_error_text(buf, sizeof buf);
      fprintf(stderr, "WWWapi::connect: accept failed: %s\n", buf);
    }

    return false;
  }

  return true;
}

void WWWapi::cancel()
{
  canceled = true;
  sock->cancel_accept();
}

void WWWapi::close()
{
  delete sock;
  delete[] address;
  sock = NULL;
}



bool WWWapi::dispatch(WWWconnection& con, char* page)
{
  unsigned hash_code = string_hash_function(page);

  for (dispatcher* disp = hash_table[hash_code % hash_table_size];
       disp != NULL;
       disp = disp->collision_chain)
  {
    if (disp->hash_code == hash_code && strcmp(disp->page, page) == 0)
    {
      bool result = disp->func(con);
      db.commit();
      return result;
    }
  }

  return true;
}


void URL2ASCII(char* src)
{
  char* dst = src;
  char ch;

  while ((ch = *src++) != '\0')
  {
    if (ch == '%')
    {
      *dst++ = ((src[0] - '0') << 8) | (src[1] - '0');
    }
    else if (ch == '+')
    {
      *dst++ = ' ';
    }
    else if (ch == '.' && *src == '.')
    {
      // for security reasons do not allow access to parent directory
      break;
    }
    else
    {
      *dst++ = ch;
    }
  }

  *dst = '\0';
}


bool CGIapi::serve(WWWconnection& con)
{
  nat4 length;
  con.reset();

  if ((size_t)con.sock->read(&length, sizeof length, sizeof length)
      != sizeof(length))
  {
    return true;
  }

  int size = length - sizeof length;
  char* buf = new char[size];

  if (con.sock->read(buf, size, size) != size)
  {
    return true;
  }

  char* page = con.unpack(buf + buf[0], length - sizeof length - buf[0]);
  char* peer = con.get("peer");
  con.peer = new char[strlen(peer)+1];
  strcpy(con.peer, peer);
  bool result = true;

  if (page != NULL)
  {
    con.extendBuffer(4);
    result = dispatch(con, page);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?