/* file: thread.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <pthread.h>
#include "http_session.h"
#include "http_header.h"
#include "get_time.h"
#include "thread.h"
extern size_t RECV_BUFFER_SIZE;
extern size_t SEND_BUFFER_SIZE;
extern size_t URI_SIZE;
extern size_t TIME_BUFFER_SIZE;
extern size_t MIME_BUFFER_SIZE;
extern size_t CONTENT_ENCODING_SIZE;
extern size_t FILE_MAX_SIZE;
extern char WEB_CACH_PATH[];
pthread_mutex_t req_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_once_t once = PTHREAD_ONCE_INIT;
pthread_key_t key;
char *req_global = NULL;
int http_sock_global = -1;
int new_thread(const int http_sock, char *http_req)
{
if(pthread_mutex_init(&req_mutex, NULL) != 0) /* initialize the mutex */
{
#ifdef DEBUG
printf("pthread_mutex_init req_mutex\n");
#endif
return -1;
}
if(pthread_mutex_lock(&req_mutex) != 0) /* lock it to change */
{
#ifdef DEBUG
printf("pthread_mutex_lock\n");
return -1;
#endif
}
/*********************************************/
req_global = http_req; /* change to new request */
http_sock_global = http_sock;
/********************************************/
if(pthread_mutex_unlock(&req_mutex) != 0) /* unlock */
{
#ifdef DEBUG
printf("pthread_mutex_unlock\n");
#endif
return -1;
}
pthread_t tid;
if(pthread_create(&tid, NULL, deal_req, NULL) != 0) /* create a thread to deal with the new request */
{
#ifdef DEBUG
printf("pthread_create\n");
#endif
return -1;
}
pthread_once(&once, thread_init);
void *exit_stat = NULL;
pthread_join(tid ,&exit_stat);
if(exit_stat == NULL)
{
return -1;
}
return 0;
}
void thread_init()
{
if(pthread_key_create(&key, destructor) != 0) /* nothing to do,just print a error message */
{
#ifdef DEBUG
printf("pthread_key_create\n");
#endif
return;
}
return;
}
/* called when thread exit if thread's TSD is not NULL, free all memery alloc by its thread */
void destructor(void *arg)
{
struct HTTP_TSD *tsd = NULL;
if((tsd = (struct HTTP_TSD *)pthread_getspecific(key)) != NULL)
{
if(!tsd->http_req)
free(tsd->http_req);
if(!tsd->uri_buf)
free(tsd->uri_buf);
if(!tsd->time_buf)
free(tsd->time_buf);
if(!tsd->file_buf)
free(tsd->file_buf);
if(!tsd->send_buf)
free(tsd->send_buf);
if(!tsd->mime_type)
free(tsd->mime_type);
if(!tsd->content_encoding)
free(tsd->content_encoding);
free(tsd);
return;
}
return;
}
void *deal_req(void *arg)
{
struct HTTP_TSD *tsd;
pthread_once(&once, thread_init);
if(pthread_mutex_lock(&req_mutex) != 0)
{
#ifdef DEBUG
printf("pthread_mutex_lock req_mutex in deal_req\n");
#endif
return NULL;
}
/**********************************************************************************/
/*
* Allocate All memery need by the new thread
*/
if((tsd = (struct HTTP_TSD *)pthread_getspecific(key)) == NULL)
{
if((tsd = (struct HTTP_TSD *)calloc(1, sizeof(struct HTTP_TSD))) == NULL)
{
#ifdef DEBUG
printf("calloc HTTP_TSD\n");
#endif
return NULL;
}
if((tsd->http_req = (char *)calloc(RECV_BUFFER_SIZE + 1, sizeof(char))) == NULL)
{
#ifdef DEBUG
printf("calloc RECV_BUFFER_SIZE\n");
#endif
return NULL;
}
if((tsd->uri_buf = (char *)calloc(URI_SIZE + 4, sizeof(char))) == NULL)
{
#ifdef DEBUG
printf("calloc URI_SIZE\n");
#endif
return NULL;
}
if((tsd->time_buf = (char *)calloc(TIME_BUFFER_SIZE + 1, sizeof(char))) == NULL)
{
#ifdef DEBUG
printf("calloc TIME_BUFFER_SIZE\n");
#endif
return NULL;
}
if((tsd->file_buf = (unsigned char *)calloc(FILE_MAX_SIZE + 1, sizeof(int))) == NULL)
{
#ifdef DEBUG
printf("calloc FILE_MAX_SIZE\n");
#endif
return NULL;
}
if((tsd->send_buf = (unsigned char *)calloc(SEND_BUFFER_SIZE + 1, sizeof(unsigned char))) == NULL)
{
#ifdef DEBUG
printf("calloc SEND_BUFFER_SIZE\n");
#endif
return NULL;
}
if((tsd->mime_type = (char *)calloc(MIME_BUFFER_SIZE + 1, sizeof(char))) == NULL)
{
#ifdef DEBUG
printf("calloc MIME_BUFFER_SIZE\n");
#endif
return NULL;
}
if((tsd->content_encoding = (char *)calloc(CONTENT_ENCODING_SIZE + 1, sizeof(char))) == NULL)
{
#ifdef DEBUG
printf("calloc CONTENT_ENCODING_SIZE\n");
#endif
return NULL;
}
/* copy the http_request message to dynamic created memery, and this memery is thread specific data(TSD) */
/* req_global is mutexed, so during copying ,others threads can not read or write it until unlock */
tsd->http_sock = http_sock_global;
memcpy(tsd->http_req, req_global,RECV_BUFFER_SIZE); /* is memcpy thread safe ,i am not sure */
if(pthread_setspecific(key, tsd) != 0)
{
#ifdef DEBUG
printf("pthread_setspecific\n");
#endif
return NULL;
}
}
/**********************************************************************************/
if(pthread_mutex_unlock(&req_mutex) != 0) /* unlock it */
{
#ifdef DEBUG
printf("pthread_mutex_unlock in deal_req\n");
#endif
return NULL;
}
/* now deal http request
* step 1. get uri
* step 2. get the file which require from http URL ,and store in tsd->file_buf
* step 3. set http response header(fill the tsd->send_buf with some http header fields)
* step 4. call the system funtion send(), this send() will copy datas in tsd->send_buf to kernel tcp buffer
* step 5. exit this thread and free all memery allocate
*/
if(get_uri() == NULL)
{
#ifdef DEBUG
printf("NULL of get_uri in deal_req\n");
#endif
return NULL;
}
if(get_mime_type() == NULL)
{
#ifdef DEBUG
printf("NULL of get_mime_type in deal_req\n");
#endif
return NULL;
}
if(get_file_disk() == -1)
{
#ifdef DEBUG
printf("error get_file_disk in deal_req\n");
#endif
return NULL;
}
int send_bytes = 0;
if((send_bytes = set_header()) == -1)
{
#ifdef DEBUG
printf("error set_header in deal_req\n");
#endif
return NULL;
}
/* lock the socket file descriptor in order to send data */
if(pthread_mutex_lock(&req_mutex) != 0)
{
#ifdef DEBUG
printf("error pthread_mutex_lock socket, begain send data\n");
#endif
return NULL;
}
if( send(tsd->http_sock, tsd->send_buf, send_bytes, 0) == -1)
{
#ifdef DEBUG
printf("error send in deal_req\n");
#endif
return NULL;
}
if(pthread_mutex_unlock(&req_mutex) != 0)
{
#ifdef DEBUG
printf("error pthread_mutex_unlock, after send data\n");
#endif
return NULL;
}
pthread_exit((void *)1);
}
/* thread safe mode get_uri function
* get the uri from http request string, then store it in TSD
*/
char *get_uri()
{
struct HTTP_TSD *tsd = NULL;
if((tsd = (struct HTTP_TSD *)pthread_getspecific(key)) == NULL)
{
#ifdef DEBUG
printf("getspecific in get_uri\n");
#endif
return NULL;
}
if(tsd->http_req == NULL)
{
#ifdef DEBUG
printf("NULL of tsd->uri_http in get_uir\n");
#endif
return NULL;
}
/* HTTP request is some like this 'GET /index.html HTTP/1.1'
* so the uri base is charactor '/' ,and uri end is space(' ')
*/
int index = 0;
while( (tsd->http_req[index] != '/') && (tsd->http_req[index] != '\0') ) /* index to base '/' */
{
index++;
}
int base = index;
while( ((index - base) < URI_SIZE) && (tsd->http_req[index] != ' ') && (tsd->http_req[index] != '\0') )
{
index++;
}
if( (index - base) >= URI_SIZE) /* too long of uri request */
{
#ifdef DEBUG
fprintf(stderr, "error: too long of uri request.\n");
#endif
tsd->http_status = URI_TOO_LONG;
return NULL;
}
if((tsd->http_req[index - 1] == '/') && (tsd->http_req[index] == ' ')) /* no request page ,so default page index.html return */
{
memcpy(tsd->uri_buf, "index.html", 10);
return tsd->uri_buf;
}
/* copy uri to TSD uri_buf, is the standard C lib function memcpy thread safe ? i am not sure*/
memcpy(tsd->uri_buf, tsd->http_req + base + 1, index - base - 1);
#ifdef DEBUG
printf("uri:%s\n", tsd->uri_buf);
#endif
return tsd->uri_buf;
}
int get_uri_status()
{
struct HTTP_TSD *tsd = NULL;
if((tsd = (struct HTTP_TSD *)pthread_getspecific(key)) == NULL)
{
#ifdef DEBUG
printf("getspecific in get_uri_status\n");
#endif
return -1;
}
if(tsd->uri_buf == NULL)
{
#ifdef DEBUG
printf("NULL of tsd->uri_buf in get_uri_status\n");
#endif
return -1;
}
/* permission check */
if(access(tsd->uri_buf, F_OK) == -1)
{
#ifdef DEBUG
fprintf(stderr, "File: %s not found.\n", tsd->uri_buf);
#endif
tsd->http_status = FILE_NOT_FOUND;
memcpy(tsd->uri_buf, "404.html\0", 9);
return FILE_NOT_FOUND;
}
if(access(tsd->uri_buf, R_OK) == -1)
{
#ifdef DEBUG
fprintf(stderr, "File: %s can not read.\n", tsd->uri_buf);
#endif
tsd->http_status = FILE_FORBIDEN;
return FILE_FORBIDEN;
}
tsd->http_status = FILE_OK;
return FILE_OK;
}
char *get_mime_type()
{
struct HTTP_TSD *tsd = NULL;
if((tsd = (struct HTTP_TSD *)pthread_getspecific(key)) == NULL)
{
#ifdef DEBUG
printf("getspecific in get_mime_type\n");
#endif
return NULL;
}
if(tsd->uri_buf == NULL)
{
#ifdef DEBUG
printf("NULL of tsd->uri_buf in get_mime_type\n");
#endif
return NULL;
}
if(tsd->mime_type == NULL)
{
#ifdef DEBUG
printf("NULL of tsd->mime_type in get_mime_type\n");
#endif
return NULL;
}
int len = strlen(tsd->uri_buf);
int dot = len - 1; /* the index of uri last charactor */
/* search dot charactor from the tail of uri */
while( dot >= 0 && tsd->uri_buf[dot] != '.')
{
dot--;
}
/* if the uri begain with a dot and the dot is the last one(that is to say the uri contain only a dot '.'), then it is a bad uri request,so return NULL */
if(dot == 0)
{
#ifdef DEBUG
printf("bad uri request\n");
#endif
return NULL;
}
dot++; /* index to type field in uri*/
int type_len = len - dot; /* the length of file extend name */
char *type_str = tsd->uri_buf + dot; /* the pointer to extend name */
switch(type_len)
{
case 4:
if(!strcmp(type_str, "html") || !strcmp(type_str, "HTML"))
{
strncpy(tsd->mime_type, "text/html", 9);
return "text/html";
}
if(!strcmp(type_str, "jpeg") || !strcmp(type_str, "JPEG"))
{
strncpy(tsd->mime_type, "image/jpeg", 10);
return "image/jpeg";
}
break;
case 3:
if(!strcmp(type_str, "htm") || !strcmp(type_str, "HTM"))
{
strncpy(tsd->mime_type, "text/html", 9);
return "text/html";
}
if(!strcmp(type_str, "css") || !strcmp(type_str, "CSS"))
{
strncpy(tsd->mime_type, "text/css", 8);
return "text/css";
}
if(!strcmp(type_str, "png") || !strcmp(type_str, "PNG"))
{
strncpy(tsd->mime_type, "image/png", 9);
return "image/png";
}
if(!strcmp(type_str, "jpg") || !strcmp(type_str, "JPG"))
{
strncpy(tsd->mime_type, "image/jpeg", 10);
return "image/jpeg";
}
if(!strcmp(type_str, "gif") || !strcmp(type_str, "GIF"))
{
strncpy(tsd->mime_type, "image/gif", 9);
return "image/gif";
}
if(!strcmp(type_str, "ico") || !strcmp(type_str, "ICO"))
{
strncpy(tsd->mime_type, "image/ico", 9);
return "image/ico";
}
if(!strcmp(type_str, "txt") || !strcmp(type_str, "TXT"))
{
strncpy(tsd->mime_type, "text/plain", 10);
return "text/plain";
}
break;
case 2:
if(!strcmp(type_str, "js") || !strcmp(type_str, "JS"))
{
strncpy(tsd->mime_type, "text/javascript", 15);
return "text/javascript";
}
break;
default: /* unknown mime type or server do not support type now*/
memcpy(tsd->mime_type, "UNSUPPORT_MIME_TYPE", 19);
tsd->http_status = UNSUPPORT_MIME_TYPE;
return NULL;
break;
}
return NULL; /* never run to hear, but i'd like to keep it */
}
int get_file_disk()
{
struct HTTP_TSD *tsd = NULL;
if((tsd = (struct HTTP_TSD *)pthread_getspecific(key)) == NULL)
{
#ifdef DEBUG
printf("getspecific in get_file_disk\n");
#endif
return -1;
}
if(tsd->uri_buf == NULL)
{
#ifdef DEBUG
printf("NULL of tsd->uri_buf in get_file_disk\n");
#endif
return -1;
}
if(tsd->file_buf == NULL)
{
#ifdef DEBUG
printf("NULL of tsd->file_buf in get_file_disk\n");
#endif
return -1;
}
if(tsd->content_encoding == NULL)
{
#ifdef DEBUG
printf("NULL of tsd->content_encoding in get_file_disk\n");
#endif
return -1;
}
int read_count = 0;
/* */
int fd = open(tsd->uri_buf, O_RDONLY);
if(fd == -1)
{
#ifdef DEBUG
printf("open() in get_file_disk\n");
#endif
return -1;
}
unsigned long st_size;
struct stat st;
if(fstat(fd, &st) == -1)
{
#ifdef DEBUG
printf("stat() in get_file_disk\n");
#endif
return -1;
}
st_size = st.st_size;
if(st_size > FILE_MAX_SIZE)
{
#ifdef DEBUG
fprintf(stderr, "the file %s is too large.\n", tsd->uri_buf);
#endif
tsd->http_status = FILE_TOO_LARGE;
return -1;
}
/* is read thread safe? */
if((read_count = read(fd, tsd->file_buf, FILE_MAX_SIZE)) == -1)
{
#ifdef DEBUG
printf("read() in get_file_disk");
#endif
tsd->http_status = SERVER_ERROR;
return -1;
}
tsd->http_status = FILE_OK;
#ifdef DEBUG
printf("file %s size : %lu , read %d\n", tsd->uri_buf, st_size, read_count);
#endif
tsd->file_size = read_count;
return read_count;
}
|