Question

In: Computer Science

Write a C program that accepts a port number as a command line argument, and starts...

Write a C program that accepts a port number as a command line argument, and starts an HTTP server. This server should constantly accept() connections, read requests of the form:
GET /path HTTP/1.1\r\n\r\n
read the file indicated by /path, and send it over the "connect" file descriptor returned by the call to accept().

Solutions

Expert Solution

#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include "csapp.h"
#include <pthread.h>
#include <string.h>


#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400


#ifdef BIT32
typedef unsigned long long aint;
#else
typedef unsigned long aint;
#endif

static const char *user_agent = "User-Agent: Mozilla/5.0 (X11; Linux i686; rv:1.9.2.3) Gecko/20100423 Firefox/3.6.3\r\n";
static const char *accept_encoding = "Accept-Encoding: gzip, deflate\r\n";
static const char *accept_0 = "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n";

int proxy(int fd);
int open_clientfd_safe(char *hostname, int port);
int parse_uri(char *uri, char *hostname, int *port);
int read_request(rio_t *rp, char *buf_request, char *hostname, int *port, char *uri);

void *thread(void *vargp);
void initial();
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);
sem_t mutex;
static int set_num, line_num;

static void init();
static int loaddata(char *tag, char *response);
static void storedata(char *response, char *tag);
static void pop_usage(int *array, int key, int len);


struct cache_line
{
        int valid;
        char *tag;
        char *block;
};
struct cache_set
{
        struct cache_line *line;
        int *usage;
};
struct cache
{
        struct cache_set *set;
};
static struct cache c;

int main(int argc,char **argv)
{
        int listenfd, *connfd, port;
        unsigned clientlen;
        //struct hostent *hp;
        struct sockaddr_in clientaddr;
        //char *haddrp;
        pthread_t tid;

        /* Check command line args */
        if (argc != 2)
        {
                fprintf(stderr, "usage: %s <port>\n", argv[0]);
                exit(1);
        }

        port = atoi(argv[1]);

        //ignore sigpipe
        signal(SIGPIPE, SIG_IGN);
            //initialization
        initial();
        listenfd = Open_listenfd(port);
        while(1)
        {
                    connfd = malloc(sizeof(int));
                clientlen = sizeof(clientaddr);
                *connfd = accept(listenfd, (SA *)&clientaddr, &clientlen);
                //hp = Gethostbyaddr((const char *) & clientaddr.sin_addr.s_addr,sizeof(clientaddr.sin_addr.s_addr),AF_INET);
                //haddrp = inet_ntoa(clientaddr.sin_addr);
                    printf("Build new connection! Connfd:%d\n", *connfd);
                pthread_create(&tid, NULL, thread, connfd);
        }
}

/* Initialization*/
static void init()
{
        int i, j;
        c.set = malloc(sizeof (struct cache_set) * set_num);
        for (i = 0; i < set_num; i++)
        {
                c.set[i].line = malloc(sizeof (struct cache_line) * line_num);
                c.set[i].usage = malloc(sizeof (int) * line_num);
                for (j = 0; j < line_num; j++)
                {
                c.set[i].usage[j] = j;
                c.set[i].line[j].valid = 0;
                        c.set[i].line[j].tag = malloc(MAXLINE);
                        c.set[i].line[j].block = malloc(MAX_OBJECT_SIZE);
                }
        }
}
void initial()
{
            sem_init(&mutex, 0, 1);
            set_num = 1;
            line_num = 10;
        init();
}

static void pop_usage(int *array, int key, int len)
{
        int i, j;
        for(i = 0; i < len; i++)
        {
                if(array[i] == key)
                break;
        }
        for(j = i; j > 0; j--)
        {
                array[j] = array[j - 1];
        }
        array[0] = key;
}

static int loaddata(char *tag, char *response)
{
        aint set_index = 0;
        int i;
        for(i = 0; i < line_num; i++)
        {
                if(c.set[set_index].line[i].valid == 1 && (strcmp(c.set[set_index].line[i].tag, tag) == 0))
                {
                        pop_usage(c.set[set_index].usage,i,line_num);
                        strcpy(response, c.set[set_index].line[i].block);
                        break;
                }
        }
        if(i == line_num)
        {
                return 0;
        }
        else
        {
                return 1;
        }
}

static void storedata(char *response,char *tag)
{
        aint set_index = 0;
        int lru;
        lru = c.set[set_index].usage[line_num - 1];
        strcpy(c.set[set_index].line[lru].tag, tag);
        strcpy(c.set[set_index].line[lru].block, response);
        if(c.set[set_index].line[lru].valid == 0)
        {
                c.set[set_index].line[lru].valid = 1;
        }
        pop_usage(c.set[set_index].usage, lru, line_num);

}

/* Thread*/
void *thread(void *vargp)
{
        int connfd = *((int *)vargp);
        pthread_detach(pthread_self());
        free(vargp);
        proxy(connfd);
        close(connfd);
        return NULL;
}

/*open_clientfd function */
int open_clientfd_safe(char *hostname, int port)
{
        int clientfd;
        struct hostent *hp;
        struct sockaddr_in serveraddr;

        if ((clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
                return -1; /* Check error */
        /* Fill in the server’s IP address and port */
        P(&mutex);
        if ((hp = gethostbyname(hostname)) == NULL)
                return -2; /* Check error */
        bzero((char *) &serveraddr, sizeof(serveraddr));
        serveraddr.sin_family = AF_INET;
        bcopy((char *)hp->h_addr_list[0], (char *)&serveraddr.sin_addr.s_addr, hp->h_length);
        serveraddr.sin_port = htons(port);
        V(&mutex);
        /* Establish a connection with the server */
        if (connect(clientfd, (SA *) &serveraddr, sizeof(serveraddr)) < 0)
        return -1;
        return clientfd;
}


/*
 * proxy - serve as a proxy to handle one HTTP request/response transaction
 */

int proxy(int connfd)
{
    int port, serverfd, len, response_len;
    char buf_request[MAX_OBJECT_SIZE], buf_response[MAX_OBJECT_SIZE], hostname[MAXLINE], cache_response[MAX_OBJECT_SIZE], uri[MAXLINE];
    rio_t rio_client, rio_server;
    /* reset the buffer */
    memset(buf_request, 0, sizeof(buf_request));
    memset(buf_response, 0, sizeof(buf_response));
    memset(hostname, 0, sizeof(hostname));
    memset(cache_response, 0, sizeof(cache_response));
    memset(uri, 0, sizeof(uri));
    /* Read request line and headers */
    rio_readinitb(&rio_client, connfd);
    printf("read_request in!\n");

    if(read_request(&rio_client, buf_request, hostname, &port, uri) < 0)
        return -1;
    printf("read_request out!\n");
    printf("uri:%s\thostname:%s\tport:%d\n", uri, hostname, port);
    printf("buf_request: %s\n", buf_request);
    if((loaddata(uri, cache_response)) == 1)
        {
        printf("Cache hit!\n");
        if(rio_writen(connfd, cache_response, sizeof(cache_response)) < 0)
        {
                fprintf(stderr, "cache response error\n");
                return -1;
        }
        /*reset the buffer*/
        memset(cache_response, 0, sizeof(cache_response));
        }
    else
        {
                printf("Cache miss!\n");
                /*send request to real server*/
                if((serverfd = open_clientfd_safe(hostname, port)) < 0)
        {
                        fprintf(stderr, "open serverfd error\n");
                        return -1;
        }
                rio_readinitb(&rio_server, serverfd);

        if(rio_writen(serverfd, buf_request, strlen(buf_request)) < 0)
        {
                        printf("buf_request error:\n%s\n", buf_request);
                            fprintf(stderr, "rio_writen send request error\n");
                        close(serverfd);
                    return -1;
        }

                /*receive from server and send back to the client */
                memset(cache_response, 0, sizeof(cache_response));
                response_len = 0;
                while((len = rio_readnb(&rio_server, buf_response, sizeof(buf_response))) > 0)
        {
                printf("hostname:%s\tport:%d\nbuf_response:%s\n", hostname, port, buf_response);
                        strcat(cache_response, buf_response);
                        response_len += len;
                if(rio_writen(connfd, buf_response, len) < 0)
            {
                                printf("buf_response error:%s\nlen:%d\n%s\n", strerror(errno), len, buf_response);
                    fprintf(stderr, "rio_writen send response error\n");
                                    close(serverfd);
                                    return -1;
            }
                        /* reset the buffer*/
                        memset(buf_response, 0, sizeof(buf_response));
                }
                    if(response_len <= MAX_OBJECT_SIZE)
                        storedata(uri, cache_response);
                        close(serverfd);
        }

    return 0;
}

/*
 * read_request - read and parse HTTP request headers
 */
int read_request(rio_t *rp, char *buf_request, char *hostname, int *port, char *uri)
{
    char buf[MAXLINE], method[MAXLINE];
        memset(buf, 0, sizeof(buf));
        memset(method, 0, sizeof(method));
        /* request line */
        if(rio_readlineb(rp, buf, MAXLINE) <= 0)
    return -1;
        printf("buf: %s\n", buf);
    sscanf(buf, "%s %s", method, uri);

        /* get hostname and port information*/
        parse_uri(uri, hostname, port);
    /* fill in request for real server */
    sprintf(buf_request, "%s %s HTTP/1.0\r\n", method, uri);
        printf("buf_request: %s\n", buf_request);
    if(rio_readlineb(rp, buf, MAXLINE) < 0)
        {
                printf("rio_readlineb fail!\n");
        return -1;
    }
        while(strcmp(buf, "\r\n"))
        {
                printf("buf: %s\n", buf);
                if(strcmp(buf, "\n") == 0)
                {
                        printf("bufn \n");
                }
                if(strcmp(buf, "\r") == 0)
                {
                        printf("bufr \n");
                }
        if(strstr(buf, "Host"))
        {
            strcat(buf_request, "Host: ");
            strcat(buf_request, hostname);
            strcat(buf_request, "\r\n");
        }
        else if(strstr(buf, "Accept:"))
            strcat(buf_request, accept_0);
        else if(strstr(buf, "Proxy-Connection:"))
            strcat(buf_request, "Proxy-Connection: close\r\n");
        else if(strstr(buf, "User-Agent:"))
            strcat(buf_request, user_agent);
        else if(strstr(buf, "Accept-Encoding:"))
            strcat(buf_request, accept_encoding);
        else if(strstr(buf, "Connection:"))
            strcat(buf_request, "Connection: close\r\n");
        else if(!strstr(buf, "Cookie:"))
        /*add addtional header except above headers*/
            strcat(buf_request, buf);
                    memset(buf, 0, sizeof(buf));
        if(rio_readlineb(rp, buf, MAXLINE) < 0)
        {
            fprintf(stderr, "rio_readlineb read request error\n");
            return -1;
        }
    }

        strcat(buf_request, "\r\n");
        return 0;
}

/*
 * parse_uri - parse URI into hostname and port args
 */

int parse_uri(char *uri, char *hostname, int *port)
{
    char *ps, *pe, *phost;
    char *uricp = malloc(strlen(uri) + 1);
    strncpy(uricp, uri, strlen(uri));

    /* start pointer */
    if((ps = strstr(uricp, "http://")) == NULL)
        return -1;
    ps += strlen("http://");
    /* end pointer */
    if((pe = strstr(ps, "/")) == NULL)
        return -1;
    *pe = '\0';
        /*if hostname contains port*/
        phost = strsep(&ps, ":");
    if(ps == NULL)
        *port = 80;
    else
        *port = atoi(ps);
        strncpy(hostname, phost, strlen(phost));
        return 0;
}

/*
 * clienterror - returns an error message to the client
 */
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg)
{
    char buf[MAXLINE], body[MAXBUF];
        memset(buf, 0, sizeof(buf));
        memset(buf, 0, sizeof(body));
    /* Build the HTTP response body */
        sprintf(body, "<html><title>Tiny Error</title>");
    sprintf(body, "%s<body bgcolor=""ffffff"">\r\n", body);
    sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg);
    sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause);
    sprintf(body, "%s<hr><em>The Tiny Web server</em>\r\n", body);
    /* Print the HTTP response */
    sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
    rio_writen(fd, buf, strlen(buf));
        sprintf(buf, "Content-type: text/html\r\n");
    rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body));
    rio_writen(fd, buf, strlen(buf));
        rio_writen(fd, body, strlen(body));
}

Related Solutions

A C program that accepts a single command line argument and converts it in to binary...
A C program that accepts a single command line argument and converts it in to binary with array length of 16 bits. The array should contain the binary of the int argument. the program should also convert negative numbers. Side note the command line arg is a valid signed int.
Write a C or C++ program A6p2.c(pp) that accepts one command line argument which is an integer n between 2 and 4 inclusi...
Write a C or C++ program A6p2.c(pp) that accepts one command line argument which is an integer n between 2 and 4 inclusive. Generate 60 random integers between 1 and 49 inclusive and store them in a 5 by 12 two dimensional integer array (e.g.,int a[5][12];). Use pthread to create n threads to square all 60 array elements. You should divide this update task among the n threads as evenly as possible. Print the array both before and after the...
program c Write a program called filesearch that accepts two command-line arguments: A string A filename...
program c Write a program called filesearch that accepts two command-line arguments: A string A filename If the user did not supply both arguments, the program should display an error message and exit. The program opens the given filename. Each line that contains the given string is displayed. Use the strstr function to search each line for the string. You may assume no line is longer than 255 characters. The matching lines are displayed to standard output (normally the screen).
Python Write a program that takes a text filename as command line argument, and prints number...
Python Write a program that takes a text filename as command line argument, and prints number of times each letter occurred in this file.
Introduction Write in C++ at the Linux command line a program that is the same as...
Introduction Write in C++ at the Linux command line a program that is the same as the previous collection app project but now uses a class to store the items and also can save the items to a file that can be read back into the array by the user when the program is re-started. You can use your project 1 submission as a starting point or you can do something new as long as it meets the listed requirements....
Write a C++ program that prints out all of the command line arguments passed to the...
Write a C++ program that prints out all of the command line arguments passed to the program. Each command line argument should be separated from the others with a comma and a space. If a command line argument ends in a comma, then another comma should NOT be added
Write a C++ program that accepts a positive integer number from the keyboard . The purpose...
Write a C++ program that accepts a positive integer number from the keyboard . The purpose of the program is to find and the display all the square pair numbers between 1 and that number. The user should be able to repeat the process until he/she enters n or N in order to terminate the process and the program. Square numbers are certain pairs of numbers when added together gives a square number and when subtracted also gives a square...
The program should be able to do the following: In Java accepts one command line parameter....
The program should be able to do the following: In Java accepts one command line parameter. The parameter specifies the path to a text file containing the integers to be sorted. The structure of the file is as follows: There will be multiple lines in the file (number of lines unknown). Each line will contain multiple integers, separated by a single whitespace. reads the integers from the text file in part a into an array of integers. sort the integers...
Complete Question 1a-c 1a) Write a C program that displays all the command line arguments that...
Complete Question 1a-c 1a) Write a C program that displays all the command line arguments that appear on the command line when the program is invoked. Use the file name cl.c for your c program. Test your program with cl hello goodbye and cl 1 2 3 4 5 6 7 8 and cl 1b) Write a C program that reads in a string from the keyboard. Use scanf with the conversion code %s. Recall that the 2nd arg in...
UNIX ONLY Write a bash script that will accept a filename as a command line argument....
UNIX ONLY Write a bash script that will accept a filename as a command line argument. Your script should first check whether the filename has been passed as argument or not (hint: at least one argument has to be provided). If no argument has been provided your script should display appropriate message and exit. If a file name has been provided, your script should check if the file exists in the current directory and display the contents of the file....
ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT