# -+- number -+- category -+- title -+- email -+- name -+- homepage -+- clickOnName -+- language -+- image -+- browser-used -+- security-cookie =0 -+- 2004-06-16:1 -+- Software Development -+- Multithreaded HTTP Server for Linux -+- derzhavets@hotmail.com -+- Boris Derzhavets -+- -+- email -+- English -+- -+- Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) -+- 1616

Multithreaded HTTP Server for Linux built up by the pool of threads
utilizing select() system call.

Developing code bellow we follow general guidelines from [1] chapter 11.
The only one source file has been modified is server.c.
Instead of fork new process to handle each incoming client's connection,
as procedure server_run ( ) does in [1], we start pool of 1024 threads
accepting as parameter descriptor of passive socket.
The procedure run by each thread is asynchronous BSD socket's server utilizing
select() system call (see for example [2],chapter 13(5)) to switch between handling
incoming client's requests by accept() system call and receiving "http" requests
from clients already connected to server utilizing procedures
handle_request() and handle_get() from [1],chapter 11.
References
1.Mark Mitchel,Jeffrey Oldham,Alex Samuel. Advanced Linux Programming.
New Riders Publishing , 2001. www.advancedlinuxprogramming.com
2.Douglas E. Comer,David L. Stevens Internet Working with TCP/IP ,vol 3
Client-Server Programming and application Linux/Posix Socket Version,
Prentice Hall,Inc. 2001

/*
 *   server.c  module :- multithreaded version, starting pool of threads,
 *   multiplexing select( ) call, to serve new connections  
 *   utilizing accept() system call and "http" responds for clients 
 *   already connected to server simultaneously.
 */

#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include <pthread.h>
#include "server.h"
#define NUM_THREADS 1024 

pthread_mutex_t get_mutex=PTHREAD_MUTEX_INITIALIZER;

static char* not_found_response_template =
  "HTTP/1.0 404 Not Found\n"
  "Content-type: text/html\n"
  "\n"
  "<html>\n"
  " <body>\n"
  "  <h1>Not Found</h1>\n"
  "  <p>The requested URL %s was not found on this server.</p>\n"
  " </body>\n"
  "</html>\n";
                                                                                
                                                                                
static char* bad_method_response_template =
  "HTTP/1.0 501 Method Not Implemented\n"
  "Content-type: text/html\n"
  "\n"
  "<html>\n"
  " <body>\n"
  "  <h1>Method Not Implemented</h1>\n"
  "  <p>The method %s is not implemented by this server.</p>\n"
  " </body>\n"
  "</html>\n";
static char* ok_response =
  "HTTP/1.0 200 OK\n"
  "Content-type: text/html\n"
  "\n";
                                                                                
static char* bad_request_response =
  "HTTP/1.0 400 Bad Request\n"
  "Content-type: text/html\n"
  "\n"
  "<html>\n"
  " <body>\n"
  "  <h1>Bad Request</h1>\n"
  "  <p>This server did not understand your request.</p>\n"
  " </body>\n"
  "</html>\n";

void
die(const char *func, int err)
{
        fprintf(stderr,"%s: %s\n",func,strerror(err));
        abort();
}

static void handle_get (int connection_fd, const char* page)
{
  struct server_module* module = NULL;
  if (*page == '/' && strchr (page + 1, '/') == NULL) {
    char module_file_name[64];
    snprintf (module_file_name, sizeof (module_file_name),
              "%s.so", page + 1);
    module = module_open (module_file_name);
  }
                                                                                
  if (module == NULL) {
    char response[1024];
    snprintf (response, sizeof (response), not_found_response_template, page);
    write (connection_fd, response, strlen (response));
  }
  else {
    write (connection_fd, ok_response, strlen (ok_response));
    (*module->generate_function) (connection_fd);
    module_close (module);
  }
}
static void *
handle_connection (int fdSock)
{
int connection_fd = fdSock;
  char buffer[256];
  ssize_t bytes_read;
  bytes_read = read (connection_fd, buffer, sizeof (buffer) - 1);
  if (bytes_read > 0) {
    char method[sizeof (buffer)];
    char url[sizeof (buffer)];
    char protocol[sizeof (buffer)];
                                                                                
    buffer[bytes_read] = '\0';
    sscanf (buffer, "%s %s %s", method, url, protocol);
    while (strstr (buffer, "\r\n\r\n") == NULL)
      bytes_read = read (connection_fd, buffer, sizeof (buffer));
    if (bytes_read == -1) {
      close (connection_fd);
      return;
    }
    if (strcmp (protocol, "HTTP/1.0") && strcmp (protocol, "HTTP/1.1")) {
      write (connection_fd, bad_request_response,
             sizeof (bad_request_response));
    }
    else if (strcmp (method, "GET")) {
      char response[1024];
      snprintf (response, sizeof (response),
                bad_method_response_template, method);
      write (connection_fd, response, strlen (response));
    }
    else
       handle_get (connection_fd, url);
  }
  else if (bytes_read == 0)
      ;
  else {
    system_error ("read");
   }
close(connection_fd);
}

void *
serv_request(void *insock)
{
int msock = (int)insock;
struct sockaddr_in fsin;
fd_set rfds;
fd_set afds;
unsigned int alen;
int fd,nfds;
int rval;
socklen_t address_length;
struct sockaddr_in socket_address;

nfds=getdtablesize();

FD_ZERO(&afds);
FD_SET(msock,&afds);

while(1) {
 memcpy(&rfds,&afds,sizeof(rfds));
 if (select(nfds,&rfds,(fd_set *)0,(fd_set *)0,(struct timeval *)0) < 0)
          die("select()",errno);
 if (FD_ISSET(msock,&rfds)) {
  int ssock;
  alen=sizeof(fsin);
  /*
   Serving new incoming client's connection
  */
  pthread_mutex_lock(&get_mutex);
    ssock=accept(msock,(struct sockaddr *)&fsin,&alen);
  pthread_mutex_unlock(&get_mutex);
  if (ssock < 0)
    die ("accept()",errno);
  if (verbose) {
      socklen_t address_length;
                                                                                
      address_length = sizeof (socket_address);
      rval = getpeername (ssock, &socket_address, &address_length);
      assert (rval == 0);
      printf ("Thread '%d' accepted connection from %s\n",
              pthread_self(),inet_ntoa (socket_address.sin_addr));
    }
  FD_SET(ssock,&afds);
}
for (fd=0;fd < nfds; ++fd)
 if (fd != msock && FD_ISSET(fd,&rfds)) {
  printf("Thread '%d' responding request...\n",pthread_self());
  /*
   Serving client had been already connected to server
  */
  handle_connection(fd);
  FD_CLR(fd,&afds);
  }
 }
}


void
server_run(struct in_addr local_address,uint16_t port)
{
struct sockaddr_in socket_address;
int rval;
int server_socket;
int k;
/*
Getting socket's descriptor to be passed to 
each thread in the pool
*/
  server_socket = socket (PF_INET, SOCK_STREAM, 0);
  if (server_socket == -1)
    system_error ("socket");
  memset (&socket_address, 0, sizeof (socket_address));
  socket_address.sin_family = AF_INET;
  socket_address.sin_port = port;
  socket_address.sin_addr = local_address;
  rval = bind (server_socket, &socket_address, sizeof (socket_address));
  if (rval != 0)
    system_error ("bind");
  rval = listen (server_socket, 10);
  if (rval != 0)
    system_error ("listen");
 if (verbose) {
    socklen_t address_length;
    address_length = sizeof (socket_address);
    rval = getsockname (server_socket, &socket_address, &address_length);
    assert (rval == 0);
    printf ("server listening on %s:%d\n",
            inet_ntoa (socket_address.sin_addr),
            (int) ntohs (socket_address.sin_port));
  }

/*
Starting pool of threads building up HTTP server
*/

size_t stacksize;
pthread_t p_thread[NUM_THREADS];
pthread_attr_t attr;
   pthread_attr_init(&attr);
   stacksize = 500000;
   pthread_attr_setstacksize (&attr, stacksize);
   pthread_attr_getstacksize (&attr, &stacksize);

for(k=0; k<NUM_THREADS; k++) {
      pthread_create(&p_thread[k],&attr,serv_request,(void*)server_socket);
}
                                                                                        
pthread_attr_destroy(&attr);

for(k=0;k<NUM_THREADS;k++) {
        pthread_join(p_thread[k], NULL);
        printf("Completed join with thread %d\n",k);
      }
}