Commit e6c9784f authored by Chaitanya Shravan's avatar Chaitanya Shravan

PushOrigin

parents
CC = g++ -Wno-write-strings
SERVER_FILE = server.cpp
HTTP_SERVER_FILE = http_server.cpp
HTTP_HEADER = http_server.hh
all: server
server: $(SERVER_FILE) $(HTTP_SERVER_FILE) $(HTTP_HEADER)
$(CC) $(HTTP_HEADER) $(SERVER_FILE) $(HTTP_SERVER_FILE) -lpthread -g -o server
clean:
rm -f server
# Bootcamp Week 3: Simple HTTP server
This week, we will modify our simple echo server of last week to build a simple HTTP server, serving HTML web pages. While your previous client and server were exchanging simple text, we will now make them exchange HTTP requests and responses.
## HTTP server specification
Your HTTP web server must parse the data received from the client and construct a HTTP request. It must then generate a suitable HTTP response and send it back to the client over the socket. Before you start, skim through [HTTP Made Really Easy](https://www.jmarshall.com/easy/http/) to understand the HTTP specifications. This [HTTP Tutorial](https://www.tutorialspoint.com/http/index.htm) is also a useful reference. You need not implement the complete HTTP specification, but only a subset. Below are some specifications of what all aspects of HTTP you must handle.
1. It is enough if you handle only HTTP GET requests for simple HTML files for now. The URL specified in the HTTP GET request must be read from the local filesystem and returned in the HTTP response. A URL can resemble the path of a directory, e.g., `/dir_a/sub_dir_b/`, or that of a file, e.g., `/dir_a/sub_dir_b/index.html`. If the URL is that of a directory, we will be looking for an `index.html` file in that directory and serve that. You can use any starting root folder for your HTML files. We have provided a sample set of HTML files for you to use in your testing, which are stored in the directory `html_files`. You can use this as the root directory for your server's HTML files.
2. The communication between the client and the server will be through TCP protocol. If you do not have root permissions on your machine to open a socket on port 80, you can use a higher port number like 8080 for the server to listen on. It is enough to support HTTP 1.0 for now, in which the server closes the TCP connection immediately after responding to the client’s request. That is, your server worker thread can close the connection once it sends a HTTP response.
3. The HTTP response returned by the server should return the status code of 200 in its initial line if the requested file is found and can be successfully returned. The server must return a response with status code 404 if the requested resource cannot be found. When returning the 404 error code, this error message must be wrapped in simple HTML headers for the browser to display it correctly.
4. It is not required for the server to parse any of the headers in the HTTP request. For a first implementation, you can also skip returning any of the HTTP headers in the response file, and only return the status of the response followed by the actual HTML content. Once this basic version works, you should try to support the HTTP headers of Date, Content-Type, and Content-Length in the HTTP response. Note that there is a end-of-line sequence (\r\n) present after every header line, and an extra end of line present after the headers and before the start of the body. There is no need for an end-of-line sequence after the message body. To fill the headers, you may use the `stat` function in the C library to get information about a file like its size. You can use functions like `time` to get the current time. It is also fine to return dummy values in these headers for an initial implementation.
If you complete building your server with these simple specifications, you can proceed to support more HTTP functionality as well, e.g., parse more headers, or add support for more content types. (This part is optional.)
## Skeleton code
Recall that the HTTP server must read data from the socket, parse the received data to form a HTTP request structure from it, generate a suitable HTTP response structure, convert this response back into a string, and write it into the socket. We have provided some skeleton code for you to get started with the HTTP processing at the server. This skeleton code is only provided as a hint, and you may choose to ignore it and write all the HTTP processing by yourself as well.
The file `http_server.h` defines data structures to store the HTTP request and response at the server. You can add more fields or modify the data structures in any way you want. We have provided you with some starting code to parse HTTP requests and generate HTTP responses in `http_server.cpp`, which you can complete based on the specification given above. In this file, you must fill in code to extract the HTTP request from a string (received on the socket) in the constructor function of the HTTP request. You must fill in code to generate a HTTP response from a received HTTP request in the function `handle_request`. You must fill in code in the function `get_string` to convert the HTTP response structure into a string to write into the socket.
You must now integrate this HTTP parsing into your simple echo server built in the previous week. The worker thread of your server that is handling the client must read data from the client socket, create a HTTP request object from it, call the function `handle_request` to generate a HTTP response, call the function `get_string` on this object to convert the response to a string, and write it into the client socket before closing the connection.
For your server code to compile correctly, you will need to include `http_server.h` in the server file, so that the HTTP related functions and data structures are accessible. You also need to compile the server with the `http_server.cpp` to generate your final executable. You can use a Makefile to compile your server correctly.
## Testing your HTTP server
Open your favourite web browser (e.g., Mozilla Firefox), and type `http://localhost:8080/<filename\>` (with a suitable port number and filename as per your implementation) in the address bar to request for the particular file from the server. If your HTTP server works properly, then you should be able to see a corresponding webpage (if the requested URL exists) or a 404 message (if the requested URL doesn’t exist).
You can also test your server using the wget command-line tool which is used to download files from the web. If you use the command `wget http://localhost:8080/<filename\> -O <output_file>`, then the file at the requested url should be downloaded and saved in the output_file. Test your server thoroughly before proceeding to the next exercise.
## Submission
Push your simple HTTP web server into your GitHub repository.
File added
File added
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<netdb.h>
void error(const char *msg) {
perror(msg);
exit(1);
}
int main(int argc, char *argv[]){
int sockfd,portno,n;
struct sockaddr_in server_addr;
struct hostent *server;
char buffer[256];
if(argc<3){fprintf(stderr,"usage %s hsotname port\n", argv[0]); exit(0);}
//Socket Creation
portno = atoi(argv[2]);
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0) error("ERROR Openeing Socket");
//server address in sockaddr_in
server = gethostbyname(argv[1]);
if(server == NULL){ fprintf(stderr, "ERROR, No Such Host\n"); exit(0);}
bzero((char *)&server_addr, sizeof(server_addr));
server_addr.sin_family=AF_INET;
bcopy((char *)server->h_addr, (char *)&server_addr.sin_addr.s_addr,server->h_length);
server_addr.sin_port = htons(portno);
//connect to server
if(connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))<0)
error("ERROR Connecting\n");
while(1) {
//user input
printf("Please Enter The Message: ");
bzero(buffer, 256);
fgets(buffer,255,stdin);
//send mssg to server
n=write(sockfd, buffer, strlen(buffer));
if(n<0) error("ERROR Writing To Socket\n");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if(n<0) error("ERROR Reading From Socket");
printf("Server Response: %s\n",buffer);
}
return 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flat 11</title>
</head>
<body>
<h1>Welcome to 1st Flat of Apartment 1.</h1>
<a href="../../index.html">GO to home</a>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flat 12</title>
</head>
<body>
<h1>Welcome to 2nd Flat of Apartment 1.</h1>
<a href="../../index.html">GO to home</a>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Apartment 1</title>
</head>
<body>
<h1> This is Apartment 1. Child of Society BootCampus.</h1>
<a href="flat11/index.html">Flat 1</a>
<a href="flat12/index.html">Flat 2</a>
<a href="../index.html">GO to home</a>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Welcome to 1st Flat of Apartment 2.</h1>
<a href="../../index.html">GO to home</a>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Apartment 1</title>
</head>
<body>
<h1>This is Apartment 2. Child of Society BootCampus.</h1>
<a href="flat21/index.html">Flat 1st</a>
<a href="../index.html">GO to home</a>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flat31
</title>
</head>
<body>
<h1>Welcome to 1st Flat of Apartment 3.</h1>
<a href="../../index.html">GO to home</a>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flat 32</title>
</head>
<body>
<h1>Welcome to 2nd Flat of Apartment 3.</h1>
<a href="../../index.html">GO to home</a>
<a </body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Apartment 1</title>
</head>
<body>
<h1>This is Apartment 3. Child of Society BootCampus.</h1>
<a href="flat31/index.html">Flat 1st</a>
<a href="flat32/index.html">Flat 2nd</a>
<a href="../index.html">GO to home</a>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>This is BootCampus.</h1>
<a href="apart1/index.html">Apartment 1</a>
<a href="apart2/index.html">Apartment 2</a>
<a href="apart3/index.html">Apartment 3</a>
</body>
</html>
\ No newline at end of file
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<h1>Not Found</h1>
<p>The Requested URL was not found on this server.</p>
</body>
</html>
#include "http_server.hh"
#include <vector>
#include <sys/stat.h>
#include <fstream>
#include <sstream>
vector<string> split(const string &s, char delim)
{
vector<string> elems;
stringstream ss(s);
string item;
while (getline(ss, item, delim))
{
if (!item.empty())
elems.push_back(item);
}
return elems;
}
HTTP_Request::HTTP_Request(string request)
{
vector<string> lines = split(request, '\n');
vector<string> first_line = split(lines[0], ' ');
this->HTTP_version = "1.0"; // We'll be using 1.0 irrespective of the request
/*
TODO : extract the request method and URL from first_line here
*/
this->method = first_line[0];
this->url = first_line[1];
//cout<<this->method;
fflush(stdout);
if (this->method != "GET")
{
cerr << "Method '" << this->method << "' not supported" << endl;
exit(1);
}
}
HTTP_Response *handle_request(string req)
{
HTTP_Request *request = new HTTP_Request(req);
HTTP_Response *response = new HTTP_Response();
string url = string("html_files") + request->url;
response->HTTP_version = "1.0";
struct stat sb;
if (stat(url.c_str(), &sb) == 0) // requested path exists
{
int flag = 0;
string body;
if (S_ISDIR(sb.st_mode))
{
/*
In this case, requested path is a directory.
TODO : find the index.html file in that directory (modify the url
accordingly)
*/
// file = fopen(url + "/index.html","r")
ifstream f(url + "/index.html");
if (f)
{
url = url + "/index.html";
}
else
{
ifstream f("html_files/notfound.html");
response->status_code = "404";
if (f)
{
ostringstream ss;
ss << f.rdbuf();
response->body = ss.str();
}
response->status_text = "Page Not Found";
flag = 1;
}
}
/*
TODO : open the file and read its contents
*/
if (!flag)
{
ifstream f(url);
if (f)
{
ostringstream ss;
ss << f.rdbuf();
response->body = ss.str();
response->status_code = "200";
response->status_text = "OK";
}
/*
TODO : set the remaining fields of response appropriately
*/
// DummyValue
response->Date = "Date: Mon, 27 Jul 2009 12:28:53 GMT";
response->content_type = "text/html";
response->content_length = to_string(response->body.length());
}
}
else
{
ifstream f("html_files/notfound.html");
response->status_code = "404";
if (f)
{
ostringstream ss;
ss << f.rdbuf();
response->body = ss.str();
}
/*
TODO : set the remaining fields of response appropriately
*/
response->status_text = "Page Not Found";
response->content_type = "text/html";
response->content_length = to_string(response->body.length());
// DummyValue
response->Date = "Date: Mon, 27 Jul 2009 12:28:53 GMT";
}
delete request;
return response;
}
string HTTP_Response::get_string()
{
/*
TODO : implement this function
*/
return "HTTP/" + HTTP_version + " " + status_code + " " + status_text + "\n" + Date + "\n" + "Content-Length: " + content_length + "\n" + "Content-Type: " + content_type + "\r\n\n" + body;
}
#ifndef _HTTP_SERVER_HH_
#define _HTTP_SERVER_HH_
#include <iostream>
using namespace std;
struct HTTP_Request
{
string HTTP_version;
string method;
string url;
// TODO : Add more fields if and when needed
HTTP_Request(string request); // Constructor
};
struct HTTP_Response
{
string HTTP_version; // 1.0 for this assignment
string status_code; // ex: 200, 404, etc.
string status_text; // ex: OK, Not Found, etc.
string content_type;
string content_length;
string body;
string Date;
// TODO : Add more fields if and when needed
string get_string(); // Returns the string representation of the HTTP Response
};
HTTP_Response *handle_request(string request);
#endif
File added
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <pthread.h>
#include "http_server.hh"
#include <queue>
#include <signal.h>
using namespace std;
//
queue <int> Clients;
pthread_mutex_t mutexClients = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cvThread = PTHREAD_COND_INITIALIZER;
pthread_cond_t cv_master_Thread = PTHREAD_COND_INITIALIZER;
int master_sleep=0;
int flag=0;
#define NumThreads 8
#define MAX_Clients 512
pthread_t thread_id[NumThreads];
void error(const char *msg)
{
perror(msg);
exit(1);
}
void intHandler(int num)
{
flag=1;
pthread_cond_broadcast(&cvThread);
pthread_cond_signal(&cv_master_Thread);
//cout<<"In Handler"<<endl;
fflush(stdout);
for (int i = 0; i < NumThreads; ++i)
{
//cout<<"Wala Thread Joined"<<endl;
pthread_join(thread_id[i],NULL);
//cout<<"Wala Thread Joined"<<endl;
}
exit(0);
}
void *clientHandler(void *arg)
{
char buffer[1024];
int n = 0;
int newsockfd;
string response_string;
while(true)
{ HTTP_Response *response;
pthread_mutex_lock(&mutexClients);
while(Clients.empty()){
if(flag) {//cout<<"YeLoopBhiKhatam"; fflush(stdout);
break;}
pthread_cond_wait(&cvThread,&mutexClients);
}
if(!flag){
newsockfd = Clients.front();
Clients.pop();
if(master_sleep)
{ master_sleep=0;
//cout<<"Signal to wake server up will be sent"<<endl;
pthread_cond_signal(&cv_master_Thread);
}
pthread_mutex_unlock(&mutexClients);
// reading client's message
bzero(buffer, 1024);
n = read(newsockfd, buffer, 1023);
//printf("%s",buffer);
if (n < 0) {
//error("ERROR Reading From Socket");
close(newsockfd);
continue;
}
response = handle_request(buffer);
response_string = response->get_string();
// cout << response_string;
// fflush(stdout);
n = write(newsockfd, response_string.c_str(), response_string.size());
if (n < 0)
{
close(newsockfd);
continue;
// error("ERROR writing to socket");
}
delete response;
close(newsockfd);
//sleep(50);
}
else if(flag)
{
pthread_mutex_unlock(&mutexClients);
break;}
}
// cout<<"Thread Loop Ke Bahar";
// fflush(stdout);
pthread_exit(NULL);
}
void init_server(struct sockaddr_in *server_addr, int portno)
{
server_addr->sin_family = AF_INET;
server_addr->sin_addr.s_addr = INADDR_ANY;
server_addr->sin_port = htons(portno);
}
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno, n;
socklen_t client_len;
struct sockaddr_in server_addr, client_addr;
pthread_t thread_No[NumThreads] ;
struct sigaction sa={0};
sa.sa_handler= intHandler;
sa.sa_flags = SA_RESTART ;
sigaction(SIGINT,&sa,NULL);
// signal(SIGINT, intHandler);
if (argc < 2)
{
fprintf(stderr, "ERROR,No Port Provided\n");
exit(1);
}
// socketCreation and server initialization
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR Opening Socket\n");
bzero((char *)&server_addr, sizeof(server_addr));
portno = atoi(argv[1]);
init_server(&server_addr, portno);
// socket binding to the portno
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
error("ERROR On Binding\n");
for(int i=0;i<NumThreads;i++)
{ thread_No[i]=i;
pthread_create(&thread_id[i],NULL,clientHandler,&thread_No[i]); }
while (!flag)
{
// listen for incoming connection requests
listen(sockfd, 256);
client_len = sizeof(client_addr);
// accepting a new request by creating a newsockfd
newsockfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
if (newsockfd < 0)
error("ERROR on accept\n");
pthread_mutex_lock(&mutexClients);
Clients.push(newsockfd);
while(Clients.size()==MAX_Clients){
master_sleep=1;
//cout<<"Server Will Sleep Now"<<endl;
pthread_cond_wait(&cv_master_Thread,&mutexClients);
}
pthread_mutex_unlock(&mutexClients);
pthread_cond_signal(&cvThread);
//cout<<Clients.front(); fflush(stdout);
}
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment